/* -*- 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-2014 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. */ #ifdef NDEBUG /* Force assert() to always be in effect. */ #undef NDEBUG #endif #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_variadic_funkiness(){ 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); FCLI_V(("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 FCLI_V(("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")); FCLI_V(("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); FCLI_V(("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_CATYPE_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_CHECKIN==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); fcli_err_report(0); f_out("Caught expected SQL-triggered error: %b\n", &db->error.msg); assert(!f->error.code && "fsl_sym2rid() did not clear f's error state"); fsl_db_err_reset(db); #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_mkdir_for_file(%s,...)...\n", path); rc = fsl_mkdir_for_file(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_mkdir_for_file(fsl_buffer_cstr(&b), 0); assert(FSL_RC_TYPE==rc); path = "f-sanity.c"; assert(fsl_is_file(path)); rc = fsl_mkdir_for_file(path, 0); assert(0==rc) /* b/c no path component, nothing to do */; b.used = 0; fsl_buffer_appendf(&b, "%s/", path); rc = fsl_mkdir_for_file(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_buffer buf = fsl_buffer_empty; fsl_time_t const now = time(0); f_out("fsl_cx_stat()...\n"); rc = fsl_cx_stat( f, 1, "no-such-file", &fst ); assert(FSL_RC_NOT_FOUND==rc); fcli_err_reset(); rc = fsl_cx_stat( f, 0, fname, &fst ); assert(0==rc); assert(FSL_FSTAT_TYPE_FILE==fst.type); assert(fst.ctime>0 && fst.ctime0 && fst.mtime0); fname = "../src/fsl_cli.c"; rc = fsl_cx_stat( f, 1, fname, &fst ); assert(0==rc); buf.used = 0; rc = fsl_cx_stat2( f, 0, "src/", &fst, &buf, 0 ); f_out("rc=%s buf=[%b]\n", fsl_rc_cstr(rc), &buf); assert(0==rc); assert(FSL_FSTAT_TYPE_DIR==fst.type); assert('/' == buf.mem[buf.used-1]); buf.used = 0; rc = fsl_cx_stat2( f, 0, "src", &fst, &buf, 0 ); f_out("rc=%s buf=[%b]\n", fsl_rc_cstr(rc), &buf); assert(0==rc); assert(FSL_FSTAT_TYPE_DIR==fst.type); assert('/' != buf.mem[buf.used-1]); buf.used = 0; rc = fsl_cx_stat2( f, 0, "./", &fst, &buf, 0 ); f_out("buf=[%b]\n", &buf); assert(0==rc); assert(FSL_FSTAT_TYPE_DIR==fst.type); assert('/' == buf.mem[buf.used-1]); #if !defined(_WIN32) /* This next test will likely not work on Windows, but i need a file i can 'touch' without invaliding build dependencies (e.g. touching the Makefile or one of the sources). */ fname = fcli.appName; rc = fsl_cx_stat(f, 1, fname, &fst); assert(0==rc); time1 = fst.mtime; rc = fsl_file_mtime_set(fname, now); assert(0==rc); f_out("old mtime=%"FSL_INT64_T_PFMT"\n", time1); rc = fsl_cx_stat(f, 1, fname, &fst); assert(0==rc); time2 = fst.mtime; f_out("new mtime=%"FSL_INT64_T_PFMT"\n", time2); assert(time2 > time1); #endif fsl_buffer_clear(&buf); } 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.mtime 0); assert(fsl_dir_check("no-such-file") == 0); assert(fsl_dir_check(fcli.appName) < 0); test_fs_mkdir(); test_fs_cx_stat(); } static void test_sanity_localtime(){ fsl_int64_t now = time(0); fsl_cx * f = fcli.f; char * tsNow; char * ts2; fsl_db * db = fsl_cx_db(f); tsNow = fsl_db_unix_to_iso8601(db, now, 0); #if 1 ts2 = fsl_db_unix_to_iso8601(db, now, 1); f_out("now utc: %s\nlocal: %s\n", tsNow, ts2); #else { char * ts1; fsl_int64_t t1, t2; /* still very unsure about this. And it's broken. */ fsl_cx_flag_set(f, FSL_CX_F_LOCALTIME_GMT, 0 ); assert(!(f->flags & 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, "../src/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]); b->used = 0; rc = fsl_file_dirpart("foo", 3, b, 0); assert(0==b->used); fsl_buffer_clear(b); } static int fsl_list_visitor_f_dump_str(void * p, void * visitorState ){ FCLI_V(("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, 0, 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 = fsl_cx_db_file_repo(f,NULL); rc = fsl_checkout_filename_check(f, 0, zName, &buf); assert(FSL_RC_RANGE==rc && "The repo db IS outside the checkout tree, right?"); fcli_err_reset(); assert(0==buf.used); { fsl_buffer vroot = fsl_buffer_empty; char const * zRoot = fsl_cx_dir_name_checkout(f, NULL); char const * zName2; fsl_buffer_appendf(&vroot, "%/src/foo", zRoot); zName = "../../xyz/../f-apps/f-sanity.c"; zRoot = fsl_buffer_cstr(&vroot); rc = fsl_file_canonical_name2(zRoot, zName, &buf, 0); f_out("fsl_file_canonical_name2(%s, %s) ==> %b\n", zRoot, zName, &buf); assert(0==rc); fsl_buffer_clear(&vroot); zRoot = NULL; zName2 = fsl_buffer_cstr(&buf); assert(fsl_is_absolute_path(zName2)); /* f_out("zName2=%s\n", zName2); */ assert(0==fsl_stat(zName2, NULL, 1)); buf.used = 0; } zName = "/etc/hosts"; rc = fsl_checkout_filename_check(f, 0, 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); zName += 1; rc = fsl_checkout_filename_check(f, 0, zName, NULL); assert(0==rc); rc = fsl_checkout_filename_check(f, 0, zOrig, NULL); assert(0==rc); buf.used = 0; rc = fsl_checkout_filename_check(f, 0, "...../....", &buf); assert(0==rc && "Yes, that is actually a legal file path."); #if 0 /* See the ...../.... example above. Still looking for an error case which will trigger this particular error before re-enabling it in fsl_checkout.c. */ /* fcli_err_report(1); */ assert(FSL_RC_RANGE==rc); zName = NULL; fsl_error_get( fcli_error(), &zName, NULL); assert(NULL != zName); assert(NULL != strstr(zName, "resolve")); fcli_err_reset(); #endif 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_CATYPE_CHECKIN, &mrid); assert(!rc); rc = fsl_deck_load_rid(f, &d, mrid, FSL_CATYPE_CHECKIN); 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_stmt_cached(){ fsl_db * db = fsl_cx_db(fcli.f); fsl_stmt * s1 = NULL, * s2 = NULL, * check = NULL; int rc; char const * sql = "SELECT 1 WHERE 3=?"; f_out("Statement caching tests...\n"); assert(db); rc = fsl_db_prepare_cached(db, &s1, sql); assert(0==rc); assert(s1); check = s1; /* Concurrent use must fail to avoid that recursion bones us with a hard-to-track error... */ rc = fsl_db_prepare_cached(db, &s2, sql); assert(FSL_RC_ACCESS==rc); assert(!s2); fcli_err_reset(); rc = fsl_stmt_cached_yield(s1); assert(0==rc); /* Make sure we get the same pointer back... */ s1 = NULL; rc = fsl_db_prepare_cached(db, &s1, sql); assert(0==rc); assert(s1); assert(check == s1); rc = fsl_stmt_cached_yield(s1); assert(0==rc); rc = fsl_stmt_cached_yield(s1); assert(FSL_RC_MISUSE==rc); fcli_err_reset(); } static void test_db_beforeCommit(){ int rc; fsl_cx * f = fcli.f; fsl_db * db = fsl_cx_db_checkout(f); char * str; f_out("db beforeCommit tests...\n"); assert(db); rc = fsl_db_transaction_begin(db); assert(!rc); rc = fsl_db_before_commit(db, "REPLACE INTO vvar (name,value) VALUES" "('test-beforeCommit', '%p')", (void const *)db); assert(!rc); rc = fsl_db_exec(db, "REPLACE INTO vvar (name,value) VALUES" "('change-placeholder','%p')", (void const *)f); assert(!rc); rc = fsl_db_transaction_end(db, 0); assert(!rc); str = fsl_db_g_text( db, NULL, "SELECT value FROM vvar " "WHERE name='test-beforeCommit'"); assert(str); f_out("Read back beforeCommit-added value: %s\n", str); fsl_free(str); } static void test_buffer_compress(){ fsl_buffer buf = fsl_buffer_empty; int rc = 0; fsl_cx * f = fcli.f; fsl_size_t sz, szOrig; char const * infile = __FILE__; f_out("Buffer compression tests...\n"); assert(f); rc = fsl_buffer_fill_from_filename(&buf, infile); assert(!rc); assert(0 > fsl_buffer_uncompressed_size(&buf)); sz = szOrig = buf.used; rc = fsl_buffer_compress( &buf, &buf ); assert(!rc); assert(buf.used < sz); assert(szOrig == fsl_buffer_uncompressed_size(&buf)); /*f_out("Compressed [%s]. Size: %"FSL_SIZE_T_PFMT " => %"FSL_SIZE_T_PFMT"\n", infile, szOrig, buf.used);*/ sz = buf.used; rc = fsl_buffer_uncompress(&buf, &buf); assert(!rc); /*f_out("Uncompressed [%s]. Size: %"FSL_SIZE_T_PFMT " => %"FSL_SIZE_T_PFMT"\n", infile, sz, buf.used);*/ assert(szOrig == buf.used); if(0){ fsl_buffer sql = fsl_buffer_empty; fsl_buffer_reset(&buf); rc = fsl_buffer_appendf(&buf, "this isn't a quoted value."); fsl_buffer_appendf(&sql,"/*%%b*/ SELECT x FROM a WHERE a=%b\n", &buf); fsl_output(f, sql.mem, sql.used ); fsl_buffer_reset(&sql); fsl_buffer_appendf(&sql,"/*%%B*/ SELECT x FROM a WHERE a=%B\n", &buf); fsl_output(f, sql.mem, sql.used ); rc = fsl_buffer_reserve(&sql, 0); assert(!rc); } rc = fsl_buffer_reserve(&buf, 0); assert(!rc); } 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(&to, &from, 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(&to, &from,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 int fsl_confirmation_f_int_logging(void * state, char const * msg, fsl_size_t msgLen){ if(fcli_is_verbose()){ f_out("CONFIRM: %s\n", msg); } return fsl_confirmation_f_int(state, msg, msgLen); } static void test_confirmation(){ int rc, crc = FSL_CONFIRM_YES_ALL; fsl_confirmation_f f = fsl_confirmation_f_int_logging; void * fs = &crc; char const * msg = "Really do it? (Y)es, (N)o, (A)all?"; f_out("fsl_confirm() tests...\n"); rc = fsl_confirm(f, fs, msg); assert(crc == rc); crc = FSL_CONFIRM_NO_ALL; rc = fsl_confirm(f, fs, msg); assert(crc == rc); } static void test_glob_list(){ fsl_list gl = fsl_list_empty; int rc; fsl_size_t i; char const * str; char const * match; char const * zGlobs = "*.c *.h,'*.sql',,,\"*.sh\""; f_out("fsl_str_glob() and fsl_glob_list_xxx() tests...\n"); assert(fsl_str_glob("*.c", __FILE__)); assert(!fsl_str_glob("*.h", __FILE__)); assert(fsl_str_glob("*.[a-d]", __FILE__)); rc = fsl_glob_list_parse( &gl, zGlobs ); assert(!rc); rc = fsl_glob_list_append( &gl, "*.in"); assert(!rc); if(fcli_is_verbose()){ for(i = 0; i < gl.used; ++i){ str = (char const *)gl.list[i]; f_out("glob #%d: [%s]\n", (int)i+1, str); } } match = fsl_glob_list_matches(&gl, "foo.h"); assert(0==fsl_strcmp("*.h",match)); match = fsl_glob_list_matches(&gl, "foo.x"); assert(!match); match = fsl_glob_list_matches(&gl, "Makefile.in"); assert(0==fsl_strcmp("*.in",match)); fsl_glob_list_clean(&gl); } static void test_vtime_check(){ fsl_cx * f = fcli_cx(); fsl_id_t vid = 0; fsl_uuid_cstr uuid = NULL; int rc; int sigFlags = 0 /* | FSL_VFILE_CKSIG_SETMTIME */ ; f_out("fsl_vtime_check_sig() tests...\n"); fsl_checkout_version_info(f, &uuid, &vid); assert(vid>0); assert(uuid); rc = fsl_vfile_changes_scan(f, vid, sigFlags); fcli_err_report(0); assert(!rc); } static void test_buffer_compare(){ fsl_buffer b1 = fsl_buffer_empty; fsl_buffer b2 = fsl_buffer_empty; int rc; char const * inFile = "f-ls.c"; FILE * file; f_out("fsl_buffer_compare() tests...\n"); rc = fsl_buffer_fill_from_filename(&b1, inFile); assert(!rc); rc = fsl_buffer_compare_file(&b1, inFile); assert(0==rc); b1.mem[2] = '\0'; rc = fsl_buffer_compare_file(&b1, inFile); assert(0!=rc); rc = fsl_buffer_fill_from_filename(&b2, inFile); assert(!rc); file = fsl_fopen(inFile, "r"); assert(file); rc = fsl_stream_compare( fsl_input_f_FILE, file, fsl_input_f_buffer, &b2); fsl_fclose(file); assert(0==rc); b1.cursor = 0; b2.cursor = 0; rc = fsl_stream_compare( fsl_input_f_buffer, &b1, fsl_input_f_buffer, &b2); assert(0!=rc); b2.used = b2.used / 2; rc = fsl_buffer_compare_file(&b2, inFile); assert(0!=rc); fsl_buffer_clear(&b1); fsl_buffer_clear(&b2); } static void test_file_add(){ int rc; fsl_cx * f = fcli.f; char const * fname = "f-sanity.c"; fsl_db * db = fsl_cx_db_checkout(f); f_out("fsl_checkout_file_add() sanity check...\n"); rc = fsl_db_transaction_begin(db); assert(!rc); rc = fsl_checkout_file_add( f, 1, fname ); assert(0==rc); /* fcli_err_report(0); */ fname = "no-such-file"; rc = fsl_checkout_file_add( f, 1, fname ); assert(FSL_RC_NOT_FOUND==rc); /* fcli_err_report(0); */ fsl_db_transaction_rollback(db); fcli_err_reset(); } static void test_file_rm(){ int rc; fsl_cx * f = fcli.f; char const * fname = "fsl.c"; fsl_db * db = fsl_cx_db_checkout(f); enum { BufSize = 2000 }; char cwd[BufSize]; f_out("fsl_checkout_file_rm() sanity check...\n"); rc = fsl_getcwd(cwd, BufSize, NULL); assert(!rc); rc = fsl_db_transaction_begin(db); assert(!rc); rc = fsl_chdir("../src", 0); assert(0==rc); rc = fsl_checkout_file_rm( f, 1, fname, 0 ); assert(0==rc); rc = fsl_chdir(cwd, 0); assert(0==rc); /* fcli_err_report(0); */ fname = "no-such-file"; rc = fsl_checkout_file_rm( f, 1, fname, 0 ); assert(0==rc); fsl_db_transaction_rollback(db); fcli_err_reset(); } static void test_branch_create(){ int rc; fsl_branch_opt opt = fsl_branch_opt_empty; fsl_id_t ckoutRid = 0; fsl_cx * f = fcli.f; fsl_db * db = fsl_cx_db_repo(f); fsl_id_t rid = 0; char const doSave = 0 /* enable to actually create/save a new branch */; f_out("fsl_branch_create() sanity check...\n"); assert(db); fsl_checkout_version_info(f, NULL, &ckoutRid); opt.name = "lib-generated-branch"; opt.basisRid = ckoutRid; opt.bgColor = "#f0f0f0"; if(!doSave) fsl_db_transaction_begin(db); rc = fsl_branch_create(f, &opt, &rid); fcli_err_report(0); if(!doSave) fsl_db_transaction_rollback(db); assert(0==rc); assert(rid>0); } static void test_file_simplify_name(){ fsl_size_t n, n2; enum { BufSize = 512 }; char cbuf[BufSize]; char const * zName = "a///b/../c"; f_out("test_file_simplify_name()...\n"); n = fsl_strlen(zName); memcpy( cbuf, zName, n+1 ); n2 = fsl_file_simplify_name(cbuf, n, 1); assert(n2 < n); assert(3==n2); assert(0==fsl_strcmp("a/c", cbuf)); } typedef struct { int counter; } ExtractState; int test_repo_extract_f(fsl_repo_extract_state const * xs){ int rc = 0; fsl_cx * f = xs->f; ExtractState * st = (ExtractState*)xs->state; enum { BufSize = 60 }; static char tbuf[BufSize]; assert(f); ++st->counter; fsl_cx_err_set(xs->f, 0, NULL); if(fcli_is_verbose()){ if(!xs->fc->uuid){ f_out("EXTRACT: RM %s\n", xs->fc->name); }else{ fsl_time_t mtime = 0; rc = fsl_mtime_of_manifest_file(xs->f, xs->versionRid, xs->fileRid, &mtime); assert(0==rc); assert(mtime>0); fsl_strftime_unix(tbuf, BufSize, "%Y-%m-%d %H:%M:%S", mtime, 0); f_out("repo_extract: %-8u %s %s\n", (unsigned)xs->content->used, tbuf, xs->fc->name); } } return 0; } static void test_repo_extract(){ int rc; fsl_cx * f = fcli_cx(); fsl_id_t vid = 0; ExtractState ex; f_out("test_repo_extract()...\n"); fsl_checkout_version_info(f, NULL, &vid); assert(vid>0); ex.counter = 0; rc = fsl_repo_extract(fcli_cx(), vid, test_repo_extract_f, &ex); assert(0==rc); assert(ex.counter > 20); f_out("Extracted %d file(s).\n", ex.counter); } /* static */ void test_repo_is_readonly(){ /* TODO */ } int main(int argc, char * const * argv ){ int rc = 0; fsl_cx * f; char singleTest; 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; if(!f || !fsl_cx_db_checkout(f)){ fcli_err_set(FSL_RC_MISUSE, "This app requires a checkout db."); goto end; } 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)); singleTest = fcli_flag2("1","single", NULL); if(!singleTest){ rc = test_variadic_funkiness(); if(!rc) rc = test_sanity_repo(); if(!rc) rc = test_sanity_delta(); if(!rc){ assert(fsl_rid_is_a_checkin(f, 1)); assert(!fsl_rid_is_a_checkin(f, 2)); 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(); test_confirmation(); test_glob_list(); test_vtime_check(); test_buffer_compare(); test_file_add(); test_file_rm(); test_branch_create(); test_file_simplify_name(); test_stmt_cached(); test_buffer_compress(); test_db_beforeCommit(); } }else{ /* placeholder for use while developing tests. */ test_repo_extract(); } { 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; }