/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* ** Copyright (c) 2013 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** This file holds some basic sanity test code. */ #include "fossil-scm/fossil-cli.h" /* Fossil App mini-framework */ #include "fossil-scm/fossil-internal.h" #include #include static void fcli_local_help(){ printf("Usage:\n\t%s \n\n", fcli.appName); puts("Runs some basic sanity tests. Requires a checkout."); } static int test_sanity_0(){ int rc = 0; /* Testing a bug/fix: Build on a 32-bit environment (untested on 64-bit) Enable 'long long' typedef fsl_int32_t fsl_id_t #define FSL_ID_T_PFMT FSL_INT32_T_PFMT And then... */ char buf[80]; int i = 0; f_out("Checking for (not asserting) sizeof()/va_list " "weirdness... (use -V for more info)\n"); f_out("On 64-bit platforms these examples probably all look" "right (output=='1 2 3'), " "but mixing in a 64-bit type on a 32-bit platform can lead " "to weirdness (bugs) in conjunction with va_list arguments.\n"); f_out("#%d: %"FSL_ID_T_PFMT" %"FSL_ID_T_PFMT" %"FSL_ID_T_PFMT"\n", ++i, 1, 2, 3); f_out("#%d: %"FSL_SIZE_T_PFMT" %"FSL_ID_T_PFMT" %"FSL_SIZE_T_PFMT"\n", ++i, (fsl_size_t)1, (fsl_id_t)2, (fsl_size_t)3); VERBOSE(("This next one (might) fail badly because the numeric arguments " "are passed on as integers and the va_arg() handling extracts " "a larger type, effectively skipping over arguments and potentially " "overrunning memory (i.e. corruption):\n")); f_out("#%d: %"FSL_SIZE_T_PFMT" %"FSL_ID_T_PFMT" %"FSL_SIZE_T_PFMT"\n", ++i, 1, 2, 3); #if 0 /* Interesting... we get warnings for printf()... Unfortunately, we can't recycle gcc's printf warnings here (via __attribute__), because it does not like our custom string formatting options. */ puts("printf(3) says:"); printf("#%d: %"FSL_SIZE_T_PFMT" %"FSL_ID_T_PFMT" %"FSL_SIZE_T_PFMT"\n", ++i, 1, 2, 3); #endif VERBOSE(("The trigger is the 'll' format specifier in conjunction with " "integer arguments which are not strongly typed and don't match " "their format specifier's expected sizeof() exactly. They are " "passed on as sizeof==4 and extracted as sizeof==8.\n")); VERBOSE(("It can be worked around by explicity casting the arguments " "to fsl_size_t resp. fsl_id_t, and this is sometimes needed " "EVEN IF the arguments are strongly typed (not sure why!).\n")); { fsl_size_t sz1 = 1, sz3 = 3; f_out("#%d: %"FSL_SIZE_T_PFMT" %"FSL_ID_T_PFMT" %"FSL_SIZE_T_PFMT"\n", ++i, sz1, 2, sz3); } /* Problem has something to do with the sizeof(int) resp. sizeof(fsl_id_t) and the va_arg() handling of the etRADIX bits in fsl_appendf(). The fix? Find the right combination of formatting string for integers in that combination. %d is apparently not correct. And it only appears to happen when we cross multiple levels of va_list handling? Namely, it happens in fsl_snprintf() but not fsl_output(): */ fsl_snprintf(buf, sizeof(buf), "#%d: ?? %"FSL_SIZE_T_PFMT" %"FSL_ID_T_PFMT" %"FSL_SIZE_T_PFMT" ??", (int)++i, (fsl_size_t)1, (fsl_id_t)2, (fsl_size_t)3); f_out("#%d fsl_snprintf(cast) says: %s\n", i, buf); fsl_snprintf(buf, sizeof(buf), "#%d: ?? %"FSL_SIZE_T_PFMT" %"FSL_ID_T_PFMT" %"FSL_SIZE_T_PFMT" ??", ++i, 1, 2, 3); f_out("#%d fsl_snprintf() says: %s\n", i, buf); VERBOSE(("That one is (or was, at one point) failing differently in tcc/gcc " "in some build combinations.\n")); ++i; f_out("#%d: f_out(cast) says: %"FSL_SIZE_T_PFMT" %"FSL_ID_T_PFMT" %"FSL_SIZE_T_PFMT" ??\n", (int)++i, (fsl_size_t)1, (fsl_id_t)2, (fsl_size_t)3); f_out("#%d: f_out() says: ?? %"FSL_SIZE_T_PFMT" %"FSL_ID_T_PFMT" %"FSL_SIZE_T_PFMT" ??\n", ++i, 1, 2, 3); /** Those fsl_snprintf() and f_out() calls SHOULD fail in the same way but they're not on my 32-bit box (in gcc - they do in tcc!). They go through the same code, differing only by how many times the va_list is passed on, thus my suspicion regarding the number of va_list indirections having something to do with this. */ return rc; } static int test_sanity_repo(){ fsl_cx * f = fcli.f; int rc = 0; fsl_buffer buf = fsl_buffer_empty; fsl_deck d = fsl_deck_empty; fsl_id_t rid; fsl_uuid_str uuid = NULL; char * str; fsl_db * db = fsl_cx_db_repo(f); fsl_size_t slen, slen2; f_out("Running basic uuid/rid tests against this repo...\n"); assert( 1==fsl_uuid_to_rid(f, "99237c3636730f20ed07b227c5092c087aea8b0c") ); assert( 1==fsl_uuid_to_rid(f, "99237c363673")); assert( 0>fsl_uuid_to_rid(f, "992") ); assert(f->error.code == FSL_RC_AMBIGUOUS); assert( strstr(fsl_buffer_cstr(&f->error.msg), "ambig") ); fcli_err_reset(); rc = fsl_sym_to_rid(f, "prev", FSL_ATYPE_CHECKIN, &rid); assert(!rc); assert(rid>0); rc = fsl_content_get(f, rid, &buf); assert(!rc); /* assert('B' == *fsl_buffer_cstr(&buf)); */ d.f = f; rc = fsl_deck_parse(&d, &buf); assert(0==rc); fsl_buffer_clear(&buf); assert(FSL_CATYPE_MANIFEST==d.type); assert(rid==d.rid); uuid = fsl_rid_to_uuid(f, d.rid); assert(uuid); assert(0==fsl_uuidcmp(d.uuid, uuid)); fsl_free(uuid); assert(d.D>0 && d.Derror.code); assert(db->error.msg.used); f_out("Caught expected SQL-triggered error: %b\n", &db->error.msg); #if 0 fcli_err_reset() /* b/c fsl_sym2rid() (indirectly) sets error at the fsl_cx context level */; #endif return 0; } static int test_sanity_delta(){ fsl_buffer f1 = fsl_buffer_empty, f2 = fsl_buffer_empty, d12 = fsl_buffer_empty, d21 = fsl_buffer_empty, a1 = fsl_buffer_empty, a2 = fsl_buffer_empty; int rc; char const * F1 = __FILE__; char const * F2 = "f-sizeof.c"; fsl_size_t len1 = 0, len2 = 0; f_out("Running core delta tests...\n"); rc = fsl_buffer_fill_from_filename(&f1, F1); assert(!rc); rc = fsl_buffer_fill_from_filename(&f2, F2); assert(!rc); f_out("Input file sizes: f1: %"FSL_SIZE_T_PFMT", f2: %"FSL_SIZE_T_PFMT"\n", f1.used, f2.used); rc = fsl_buffer_delta_create(&f1, &f2, &d12); assert(!rc); rc = fsl_buffer_delta_create(&f2, &f1, &d21); assert(!rc); f_out("Delta sizes: f1=>f2: %"FSL_SIZE_T_PFMT", f2=>f1: %"FSL_SIZE_T_PFMT"\n", d12.used, d21.used); /* f_out("%b\n", &d12); */ rc = fsl_delta_applied_size(d12.mem, d12.used, &len1); assert(!rc); assert(len1==f2.used); rc = fsl_delta_applied_size(d21.mem, d21.used, &len2); assert(!rc); assert(len2==f1.used); rc = fsl_buffer_delta_apply(&f1, &d12, &a2); assert(!rc); rc = fsl_buffer_delta_apply(&f2, &d21, &a1); assert(!rc); if( fsl_buffer_compare(&f1,&a1) || fsl_buffer_compare(&f2, &a2) ){ fsl_fatal(FSL_RC_CONSISTENCY, "delta test failed"); } fsl_buffer_clear(&f1); fsl_buffer_clear(&f2); fsl_buffer_clear(&a1); fsl_buffer_clear(&a2); fsl_buffer_clear(&d12); fsl_buffer_clear(&d21); return rc; } static void test_sanity_tkt_01(){ fsl_buffer sql = fsl_buffer_empty; fsl_cx * f = fcli.f; char const * orig = fsl_schema_ticket(); int rc = fsl_cx_schema_ticket(f, &sql); assert(!rc); assert(sql.used>200); if(0) f_out("Ticket schema size=%"FSL_SIZE_T_PFMT ", default schema size=%"FSL_SIZE_T_PFMT"\n", sql.used, fsl_strlen(orig)); fsl_buffer_clear(&sql); } static void test_sanity_tkt_fields(){ fsl_cx * f = fcli.f; int rc; f_out("Loading custom ticket fields...\n"); rc = fsl_cx_ticket_load_fields(f, 0); assert(!rc); f_out("Ticket field count=%"FSL_SIZE_T_PFMT": ", f->ticket.customFields.used); for(rc = 0; rc < (int)f->ticket.customFields.used; ++rc){ fsl_card_J const * jc = f->ticket.customFields.list[rc]; f_out( "%s%s", rc ? ", " : "", jc->field); } f_out("\n"); } static void test_fs_mkdir(){ int rc; char const * path = "_sanity/foo/bar/baz/"; fsl_buffer b = fsl_buffer_empty; fsl_fstat fst = fsl_fstat_empty; f_out("fsl_mkdir2(%s,...)...\n", path); rc = fsl_mkdir2(path, 1); assert(0==rc); rc = fsl_stat( path, &fst, 1); assert(0==rc); assert(FSL_FSTAT_TYPE_DIR==fst.type); fsl_buffer_appendf(&b, "%//", __FILE__); rc = fsl_mkdir2(fsl_buffer_cstr(&b), 0); assert(FSL_RC_TYPE==rc); path = "f-sanity.c"; assert(fsl_is_file(path)); rc = fsl_mkdir2(path, 0); assert(0==rc) /* b/c no path component, nothing to do */; b.used = 0; fsl_buffer_appendf(&b, "%s/", path); rc = fsl_mkdir2(fsl_buffer_cstr(&b), 0); assert(FSL_RC_TYPE==rc); fsl_buffer_clear(&b); } static void test_fs_cx_stat(){ fsl_cx * f = fcli.f; int rc; fsl_fstat fst = fsl_fstat_empty; fsl_int64_t time1, time2; char const * fname = "src/fsl_cli.c"; fsl_time_t const now = time(0); f_out("fsl_cx_stat()...\n"); rc = fsl_cx_stat( f, "no-such-file", &fst ); assert(FSL_RC_NOT_FOUND==rc); rc = fsl_cx_stat( f, fname, &fst ); assert(0==rc); assert(FSL_FSTAT_TYPE_FILE==fst.type); assert(fst.ctime>0 && fst.ctime0 && fst.mtime0); fname = 0 ? "test" : fcli.appName; rc = fsl_cx_stat(f, fname, &fst); assert(0==rc); time1 = fst.mtime; rc = fsl_file_set_mtime(fname, now); assert(0==rc); f_out("old mtime=%"FSL_INT64_T_PFMT"\n", time1); rc = fsl_cx_stat(f, fname, &fst); assert(0==rc); time2 = fst.mtime; f_out("new mtime=%"FSL_INT64_T_PFMT"\n", time2); assert(time2 > time1); } static void test_sanity_fs(){ char * s = NULL; fsl_buffer b = fsl_buffer_empty; int rc; fsl_fstat fst = fsl_fstat_empty; rc = fsl_find_home_dir(&b, 1); f_out("Home dir: %b\n", &b); assert(0==rc); b.used = 0; rc = fsl_stat(__FILE__, &fst, 1); assert(0==rc); assert(FSL_FSTAT_TYPE_FILE == fst.type); assert(fst.mtime>0); assert(fst.mtimeflags & FSL_CX_F_LOCALTIME_GMT)); t1 = fsl_cx_time_adj(f, now); ts1 = fsl_db_unix_to_iso8601(db, t1, 0); fsl_cx_flag_set(f, FSL_CX_F_LOCALTIME_GMT, 1 ); assert(f->flags & FSL_CX_F_LOCALTIME_GMT); t2 = fsl_cx_time_adj(f, now); ts2 = fsl_db_unix_to_iso8601(db, t2, 0); fsl_cx_flag_set(f, FSL_CX_F_LOCALTIME_GMT, 0 ); f_out("now=%s\nts1=%s\nts2=%s\n", tsNow, ts1, ts2); fsl_free(ts1); } #endif fsl_free(tsNow); fsl_free(ts2); } static void test_julian(){ char const * ts; double tolerance = 0 /* Tolerance, in Julian Days, for round-trip rounding errors. In my tests most time conversions are either exact or +/-1ms, but the exact range of error is probably at least partially platform-dependent. The differences between the reference JD values (taken from sqlite3) and the results are consistently (0, 0.000000001, and 0.000000011), which is quite close enough for what we do with these. */ ? 1 : 0.000000012; double eB; double eE; char buf[24]; double j = 0; char rc; char vbose = fcli.verbose>1; struct tester { /* ISO8601 time string. */ char const * ts; /* Expected JD result. This value is normally taken from sqlite3's strftime('%J', this->ts). */ double expect; } list[] = { {"2013-12-13T01:02:03.000", 2456639.543090278}, {"2013-09-10T23:35:53.471", 2456546.483257755}, {"2013-09-10T22:35:53.471", 2456546.441591088}, {"2013-09-10T21:35:53.471", 2456546.399924421}, {"2013-09-10T20:35:53.471", 2456546.358257755}, {"2013-09-10T19:35:53.471", 2456546.316591088}, {"2013-09-10T18:35:53.471", 2456546.274924421}, {"2013-09-10T17:35:53.471", 2456546.233257755}, {"2013-09-10T16:35:53.471", 2456546.191591088}, {"2013-09-10T15:35:53.471", 2456546.149924421}, {"2013-09-10T14:35:53.471", 2456546.108257755}, {"2013-09-10T13:14:15.167", 2456546.051564422}, {"2013-09-10T01:02:03.004", 2456545.543090324}, {"2013-09-09T12:11:10.987", 2456545.007766053}, {"2013-09-09T11:10:09.876", 2456544.965392072}, {"2013-02-02T12:13:14.000", 2456326.009189815}, {"2013-01-02T12:13:14.000", 2456295.009189815}, {"2011-12-31T12:51:46.686", 2455927.035957014}, {"2011-01-01T00:15:54.000", 2455562.511041666}, {"2008-12-20T02:23:18.000", 2454820.599513888}, {"2008-01-02T14:53:47.000", 2454468.12068287}, {"2007-07-21T14:09:59.000", 2454303.090266198}, {"2013-12-31T23:59:59.997", 2456658.499999954}, {0,0} }; struct tester * t; f_out("iso8601 <==> julian tests...\n"); for( t = list; t->ts; ++t ){ ts = t->ts; j = 0; if(vbose) f_out("\ninput : %s\n", ts); rc = fsl_iso8601_to_julian(ts, &j); assert(rc); if(vbose){ f_out("julian: %"FSL_JULIAN_T_PFMT"\n", j); f_out("expect: %"FSL_JULIAN_T_PFMT"\n", t->expect); } buf[0] = 0; rc = fsl_julian_to_iso8601(j, buf, 1); assert(rc); if(vbose) f_out("j=>iso: %s\n", buf); if(0 != t->expect){ eB = t->expect - tolerance; eE = t->expect + tolerance; assert( (j>=eB && j<=eE) ); } if(0!=fsl_strncmp(ts, buf, 22/*not the final digit*/)){ f_out("WARNING: round-trip mismatch at >ms level " "for:" "\n\t%"FSL_JULIAN_T_PFMT"\t%s" "\n\t%"FSL_JULIAN_T_PFMT"\t%s\n", t->expect, ts, j, buf); }else{ rc = fsl_julian_to_iso8601(j, buf, 0); if(vbose) f_out("j=>iso: %s\n", buf); assert(rc); if(0!=fsl_strncmp(ts, buf, 19)){ /* This is caused by: SS.000 ==> (SS-1).999 in julian-to-iso8601 */ f_out("WARNING: mismatch:\n\t%s\n\t%s\n", ts, buf); } } } } static void test_julian2(){ fsl_cx * f = fcli.f; fsl_db * db = fsl_cx_db_repo(f); double tolerance = 0 /* Tolerance, in Julian Days, for round-trip rounding errors. In my tests most time conversions are either exact or +/-1ms, but the exact range of error is probably at least partially platform-dependent. The differences between the reference JD values (taken from sqlite3) and the results are consistently (0, 0.000000001, and 0.000000011), which is quite close enough for what we do with these. */ ? 1 : 0.000000012; double eB; double eE; char buf[24]; int rc; char vbose = fcli.verbose>1; fsl_stmt q = fsl_stmt_empty; int counter = 0, warnings = 0, diffBy1Ms = 0; f_out("Running all event.mtimes through the julian<==>iso converters...\n"); rc = fsl_db_prepare(db, &q, "SELECT mtime, strftime('%%Y-%%m-%%dT%%H:%%M:%%f',mtime) " "FROM event ORDER BY mtime DESC " /*"LIMIT 100"*/ ); while( FSL_RC_STEP_ROW == fsl_stmt_step(&q) ){ char const * ts = fsl_stmt_g_text(&q, 1, NULL); double const jexp = fsl_stmt_g_double(&q, 0); double j = 0; int const oldWarn = warnings; ++counter; if(vbose) f_out("\ninput : %s\n", ts); rc = fsl_iso8601_to_julian(ts, &j); assert(rc); if(vbose){ f_out("julian: %"FSL_JULIAN_T_PFMT"\n", j); f_out("expect: %"FSL_JULIAN_T_PFMT"\n", jexp); } assert(j>0.0); buf[0] = 0; rc = fsl_julian_to_iso8601(j, buf, 1); assert(rc); if(vbose) f_out("j=>iso: %s\n", buf); eB = jexp - tolerance; eE = jexp + tolerance; assert( (j>=eB && j<=eE) ); if(0!=fsl_strncmp(ts, buf, 22/*not the final digit!*/)){ /* See if only the last three digits differ by 1 integer point (1ms), and don't warn if that's the case. There's a corner case there when N.x99 rolls over, and another for N.999, etc., but we'll punt on that problem. */ int const f1 = atoi(ts+20); int const f2 = atoi(buf+20); int const d = f1 - f2; /* assert(f1 || f2); */ if(d<-1 || d>1){ f_out("WARNING: possible round-trip fidelity mismatch: " "\n\twant: %"FSL_JULIAN_T_PFMT"\t%s" "\n\tgot : %"FSL_JULIAN_T_PFMT"\t%s\n", jexp, ts, j, buf); if(1==++warnings){ f_out("These are normally caused by, e.g., N.789 ==> N.790 " "or vice versa, which is still well within tolerance " "but is more difficult to test against in string form.\n" ); } }else{ if(0!=fsl_strncmp(ts, buf, 19)){ f_out("Mismatch at YMDHmS level:\n\t%s\n\t%s\n", ts, buf); } ++diffBy1Ms; } } /* Try without fractional part. These should never mismatch. */ rc = fsl_julian_to_iso8601(j, buf, 0); if(vbose) f_out("j=>iso: %s\n", buf); assert(rc); if(0!=fsl_strncmp(ts, buf, 19)){ /* f_out("VERY UNPEXECTED MISMATCH:\n\t%.*s\n\t%.*s\n", 19, ts, 19, buf); */ /* assert(!"These should never mismatch. " "Unless, of course, the SS.999 problem hits."); */ if(oldWarn == warnings) ++warnings /* avoid double counting */; /* This is caused by: SS.000 ==> (SS-1).999 in julian-to-iso8601. But that's "fixed" now - we shouldn't see this anymore. Still seeing it in the main fossil repo for about 0.27% of the records :/. */ } } fsl_stmt_finalize(&q); f_out("%d Julian timestamps tested from event.mtime.\n", counter); if(diffBy1Ms>0){ f_out("%d record(s) (%3.2f%%) differed round-trip by 1ms " "(this is \"normal\"/not unexpected).\n", diffBy1Ms, ((diffBy1Ms+0.0)/(counter+0.0)*100)); assert((1.0 > ((diffBy1Ms+0.0)/(counter+0.0)*100)) /*^^^ 1% was arbitrarily chosen! Increase if needed! */ && "Suspiciously many Julian/ISO8601 off-by-1ms conversions."); } if(warnings>0){ f_out("ACHTUNG: conversion warning count: %d (%3.2f%% of total)\n", warnings, ((warnings+0.0)/(counter+0.0)*100)); assert((1.0 > ((warnings+0.0)/(counter+0.0)*100)) /*^^^ 1% based on current rate of 0.51% of 779 records. */ && "Suspiciously many Julian/ISO8601 conversion warnings."); } } static void test_pathfinder(){ fsl_pathfinder PF = fsl_pathfinder_empty; fsl_pathfinder * pf = &PF; char const * found = NULL; int rc; f_out("fsl_pathfinder sanity checks...\n"); /* add() cannot fail here b/c of custom allocator, resp. it will abort() if it fails. */ fsl_pathfinder_dir_add(pf, "src"); fsl_pathfinder_ext_add(pf, ".c"); rc = fsl_pathfinder_search(pf, "fsl_fs", &found, NULL); assert(0==rc); assert(strstr(found, "fsl_fs.c")); f_out("pathfinder found %s\n", found); rc = fsl_pathfinder_search(pf, "nono", &found, NULL); assert(FSL_RC_NOT_FOUND==rc); fsl_pathfinder_clear(pf); } static void test_fs_dirpart(){ fsl_buffer B = fsl_buffer_empty; fsl_buffer * b = &B; char const * cwd = "/foo/bar/baz"; fsl_size_t n = fsl_strlen(cwd); int rc; f_out("fsl_file_dirpart() tests...\n"); rc = fsl_file_dirpart(cwd, (fsl_int_t)n, b, 0); assert(0==rc); assert(0==fsl_strcmp("/foo/bar", fsl_buffer_cstr(b))); assert('/'!=b->mem[b->used-1]); b->used = 0; rc = fsl_file_dirpart(cwd, (fsl_int_t)n, b, 1); assert(0==rc); assert(0==fsl_strcmp("/foo/bar/", fsl_buffer_cstr(b))); assert('/'==b->mem[b->used-1]); b->used = 0; rc = fsl_file_dirpart("/", 1, b, 0); assert(0==rc); assert(0==b->used); rc = fsl_file_dirpart("/", 1, b, 1); assert(1==b->used); assert('/'==b->mem[0]); fsl_buffer_clear(b); } static int fsl_list_visitor_f_dump_str(void * p, void * visitorState ){ VERBOSE(("dir entry: %s\n", (char const *)p)); ++*((int*)visitorState); return 0; } static void test_dir_names(){ fsl_list li = fsl_list_empty; int rc; fsl_id_t rid; int count = 0; rid = #if 0 2973 /* some commit rid with dirs in it. Not rid 1. */ #elif 0 -1 /* all versions */ #else 0 /* current checkout */ #endif ; f_out("fsl_repo_dir_name()/fsl_dirpart() UDF tests " "for rid=%"FSL_ID_T_PFMT"...\n",(fsl_id_t)rid); rc = fsl_repo_dir_names( fcli.f, rid, &li, 1 ); fcli_err_report(0); assert(!rc); assert(li.used>0); fsl_list_visit( &li, 0, fsl_list_visitor_f_dump_str, &count ); assert(count==li.used); f_out("%d dir entry(ies).\n", count); fsl_list_visit_free(&li, 1); assert(NULL == li.list); assert(0 == li.used); } static void test_tree_name(){ fsl_cx * f = fcli.f; fsl_buffer buf = fsl_buffer_empty; int rc; char const * zName = "src/fsl.c"; char const * zOrig = zName; f_out("Starting fsl_checkout_filename_check() test.\n"); rc = fsl_checkout_filename_check(f, zName, &buf); /* f_out("fsl_checkout_filename_check(%s) ==> %b\n", zName, &buf); */ assert(0==rc); assert(0==fsl_strcmp(zName, fsl_buffer_cstr(&buf))); buf.used = 0; zName = "/etc/hosts"; rc = fsl_checkout_filename_check(f, zName, &buf); assert(FSL_RC_RANGE == fcli_error()->code); /*fcli_err_report(1); assert(fcli_error()->code == 0); */ fcli_err_reset(); assert(fcli_error()->code == 0); assert(FSL_RC_RANGE==rc); rc = fsl_checkout_filename_check(f, zOrig, NULL); assert(0==rc); buf.used = 0; rc = fsl_checkout_filename_check(f, "...../....", &buf); assert(FSL_RC_RANGE==rc); /* fcli_err_report(1); */ zName = NULL; fsl_error_get( fcli_error(), &zName, NULL); assert(NULL != zName); assert(NULL != strstr(zName, "resolve")); fcli_err_reset(); fsl_buffer_clear(&buf); } static void test_strftime(){ enum { BufSize = 256 }; char buf[BufSize]; fsl_size_t len; char const * str = buf; struct tm * tm; time_t timt; struct FmtInfo { char const * fmt; char const * label; } fmtList[] = { {"%Y-%m-%d %H:%M:%S", "YYYY-mm-dd HH:MM:SS"}, {"%%", "Literal percent"}, {"%a", "Abbr. weekday name"}, {"%A", "Full weekday name"}, {"%b", "Abbr. month name"}, {"%e", "day of month, blank-padded"}, {"%h", "Same as %b"}, {"%B", "Full month name"}, {"%c", "??? \"appropriate date/time representation\""}, {"%C", "Century as two digits"}, {"%d", "day of month, 01-31"}, {"%D", "%m/%d/%y"}, {"%E", "ignored"}, {"%H", "hour, 00-23"}, {"%I", "hour, 01-12"}, {"%j", "day of year, 001-366"}, {"%k", "hour, 0-24, blank-padded"}, {"%l", "hour, 1-12, blank-padded"}, {"%m", "month, 01-12"}, {"%M", "minute, 00-59"}, {"%n", "\\n"}, {"%O", "ignored"}, {"%p", "\"am\" or \"pm\""}, {"%r", "%I:%M:%S %p"}, {"%R", "%H:%M"}, {"%S", "seconds, 00-61"}, {"%t", "\\t"}, {"%T", "%H:%M:%S"}, {"%u", "ISO-8601 weeday as number 1-7, 1=Monday"}, {"%U", "week of year, Sunday as first day"}, {"%v", "dd-bbb-YYYY"}, {"%V", "ISO-8601 week number"}, {"%w", "weekday, 0-6, Sunday=0"}, {"%W", "week of year, Monday as first day"}, {"%x", "??? \"appropriate date representation\""}, {"%X", "??? \"appropriate time representation\""}, {"%y", "year, 00-99"}, {"%Y", "year with century"}, {NULL,NULL} }; struct FmtInfo const * fi = fmtList; f_out("fsl_strftime() tests...\n"); time(&timt); tm = localtime(&timt); if(fcli_is_verbose()){ for( ; fi->fmt; ++fi ){ len = fsl_strftime(buf, BufSize, fi->fmt, tm); f_out("strftime: %s %s ==> %s\n", fi->fmt, fi->label, buf); assert(len>0); } }else{ len = fsl_strftime(buf, BufSize, "%Y-%m-%d %H:%M:%S", tm); f_out("strftime: %s\n", str); assert(19==len); } { int rc; fsl_buffer bf = fsl_buffer_empty; rc = fsl_buffer_strftime(&bf, "%H:%M:%S", tm); assert(0==rc); assert(8==bf.used); fsl_buffer_clear(&bf); } } static void test_config_db(){ fsl_confdb_t mode = FSL_CONFDB_CKOUT; fsl_cx * f = fcli_cx(); char * str; fsl_size_t slen; int rc; f_out("fsl_config_xxx() tests...\n"); rc = fsl_config_transaction_begin(f, mode); assert(!rc); rc = fsl_config_set_text(f, mode, "sanity-text", "hi"); assert(!rc); rc = fsl_config_set_blob(f, mode, "sanity-blob", "hi!!!", 3/*yes, 3!*/); assert(!rc); rc = fsl_config_set_int32(f, mode, "sanity-int32", 32); assert(!rc); rc = fsl_config_set_int32(f, mode, "sanity-int64", 64); assert(!rc); rc = fsl_config_set_id(f, mode, "sanity-id", 2345); assert(!rc); rc = fsl_config_set_double(f, mode, "sanity-double", -42.24); assert(!rc); rc = fsl_config_set_bool(f, mode, "sanity-bool", 1); assert(!rc); str = fsl_config_get_text(f, mode, "sanity-text", &slen); assert(0==fsl_strcmp("hi", str)); assert(2==slen); fsl_free(str); str = fsl_config_get_text(f, mode, "sanity-blob", &slen); assert(0==fsl_strcmp("hi!", str)); assert(3==slen); fsl_free(str); assert(32==fsl_config_get_int32(f, mode, -1, "sanity-int32")); assert(64==fsl_config_get_int64(f, mode, -1, "sanity-int64")); assert(2345==fsl_config_get_id(f, mode, -1, "sanity-id")); assert(1==fsl_config_get_bool(f, mode, -1, "sanity-bool")); { /* portability problem: on my x64 box sanity-double==-42.24. On my i32 box it prints out as -42.24 but does not compare == to -42.24. So we'll do a slightly different check... */ fsl_double_t dbl = fsl_config_get_double(f, mode, -1, "sanity-double"); /* f_out("dbl=%"FSL_DOUBLE_T_PFMT"\n", dbl); */ /* assert(-42.24==dbl); */ assert(-42 == (int)dbl); assert(-24 == (int)(dbl*100) % -100); } rc = fsl_config_transaction_end(f, mode, 0); assert(!rc); } static void test_mtime_of_manifest(){ fsl_cx * f = fcli_cx(); char const * tag = "current"; fsl_id_t mrid = 0; fsl_id_t fnid = 0; int rc; fsl_deck d = fsl_deck_empty; fsl_size_t i; fsl_time_t timey; fsl_db * dbR = fsl_cx_db_repo(f); struct tm * tm; enum { BufSize = 30 }; char strBuf[BufSize]; fsl_card_F const * fc; f_out("fsl_mtime_of_manifest() checks...\n"); rc = fsl_sym_to_rid(f, tag, FSL_ATYPE_CHECKIN, &mrid); assert(!rc); rc = fsl_deck_load_rid(f, mrid, &d, FSL_CATYPE_MANIFEST); assert(!rc); /* Pedantic note: we are knowingly bypassing any lookup of files in the parent manifest here. Don't try this at home. */ rc = fsl_deck_F_rewind(&d); for( i = 0; i < d.F.list.used; ++i){ fsl_int64_t stm; fc = (fsl_card_F const * )d.F.list.list[i]; /* if(!rc) while( !(rc=fsl_deck_F_next(&d, &fc)) && fc) { */ if(!fc->uuid) continue; fnid = fsl_uuid_to_rid(f, fc->uuid); assert(fnid>0); rc = fsl_mtime_of_manifest_file(f, mrid, fnid, &timey); assert(0==rc); { time_t tt = (time_t)timey; tm = localtime(&tt); } strBuf[0] = 0; fsl_strftime(strBuf, BufSize, "%Y-%m-%d %H:%M:%S", tm); if(fcli_is_verbose()){ f_out("%8"FSL_ID_T_PFMT" %.8s %"FSL_TIME_T_PFMT" => %s %s\n", (fsl_id_t)fnid, fc->uuid, (fsl_time_t)timey, strBuf, fc->name); } assert(19==fsl_strlen(strBuf)); stm = fsl_db_g_int64(dbR, -1, "SELECT FSL_CI_MTIME(" "%"FSL_ID_T_PFMT",%"FSL_ID_T_PFMT")", (fsl_id_t)mrid, (fsl_id_t)fnid); assert(stm == timey); } fsl_deck_finalize(&d); } static void test_buffer_count_lines(){ fsl_buffer to = fsl_buffer_empty; fsl_buffer from = fsl_buffer_empty; int rc; f_out("fsl_buffer_copy_lines() tests...\n"); fsl_buffer_append(&from, "a\nb\nc\nd\n", -1); rc = fsl_buffer_copy_lines(&from, &to, 2); assert(0==rc); rc = fsl_strcmp("a\nb\n", fsl_buffer_cstr(&to)); assert(0==rc); to.used = 0; rc = fsl_buffer_copy_lines(&from, &to, 3); assert(0==rc); rc = fsl_strcmp("c\nd\n", fsl_buffer_cstr(&to)); /* f_out("<<<%b>>>\n", &to); */ assert(from.cursor==from.used); assert(0==rc); fsl_buffer_clear(&to); fsl_buffer_clear(&from); } static void test_buffer_streams(){ fsl_buffer bin = fsl_buffer_empty; fsl_buffer bout = fsl_buffer_empty; f_out("fsl_buffer_(input|output)_f() tests...\n"); fsl_buffer_append(&bin, "Buffer stream.", -1); fsl_stream(fsl_input_f_buffer, &bin, fsl_output_f_buffer, &bout); assert(bin.used==bin.cursor); assert(0==fsl_buffer_compare(&bin, &bout)); /* f_out("bout=<<<%b>>>\n", &bout); */ fsl_buffer_clear(&bin); fsl_buffer_clear(&bout); } /* static */ void test_repo_is_readonly(){ /* TODO */ } int main(int argc, char * const * argv ){ int rc = 0; fsl_cx * f; fcli.appHelp = fcli_local_help; rc = fcli_setup(argc, argv); if(FSL_RC_BREAK==rc) /* --help */return 0; else if(rc) goto end; f = fcli.f; f_out("Checkout dir=%s\n", fsl_cx_dir_name_checkout(f, NULL)); f_out("Checkout db=%s\n", fsl_cx_db_file_checkout(f, NULL)); f_out("Repo db=%s\n", fsl_cx_db_file_repo(f, NULL)); if(1){ rc = test_sanity_0(); if(!rc) rc = test_sanity_repo(); if(!rc) rc = test_sanity_delta(); if(!rc){ test_pathfinder(); test_fs_dirpart(); test_sanity_tkt_01(); test_sanity_tkt_fields(); test_sanity_fs(); test_sanity_localtime(); test_julian(); test_julian2(); test_dir_names(); test_tree_name(); test_strftime(); test_config_db(); test_mtime_of_manifest(); test_buffer_count_lines(); test_buffer_streams(); assert(fsl_rid_is_a_checkin(f, 1)); assert(!fsl_rid_is_a_checkin(f, 2)); } }else{ /* placeholder for use while developing tests. */ } { char const * ckoutUuid = NULL; fsl_id_t ckoutRid = 0; fsl_checkout_version_info(f, &ckoutUuid, &ckoutRid); f_out("checkout UUID=%s (RID %"FSL_ID_T_PFMT")\n", ckoutUuid, (fsl_id_t)ckoutRid); } end: f_out("If you made it this far, no assertions were triggered. " "Now try again with valgrind.\n"); return (fcli_err_report(0)||rc) ? EXIT_FAILURE : EXIT_SUCCESS; }