/* -*- 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 <string.h>
#include <time.h>
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.D<fsl_julian_now());
fsl_deck_finalize(&d);
assert(0==rc);
slen = 0;
str = fsl_db_g_text(db, &slen, "SELECT FSL_USER()");
assert(str && "fsl_user() SQL func is broken");
/* f_out("SELECT fsl_user()=%s\n", str); */
assert(0==fsl_strcmp(str, fsl_cx_user_get(f)));
fsl_free(str);
slen = 0;
str = fsl_db_g_text(db, &slen, "select fsl_content(1)");
assert(str && slen && "fsl_content() SQL func is broken");
fsl_free(str);
assert(168 == slen) /* side of rid 1 in libfossil */;
/* f_out("SELECT fsl_content() got %"FSL_SIZE_T_PFMT" bytes\n", (fsl_size_t)slen); */
slen2 = 0;
str = fsl_db_g_text(db, &slen2, "select fsl_content('rid:1')");
assert(str && slen2 && "fsl_content(sym) SQL func is broken");
assert(slen2==slen);
fsl_free(str);
/* f_out("SELECT fsl_content(rid) got %"FSL_SIZE_T_PFMT" bytes\n", (fsl_size_t)slen2); */
rid = fsl_db_g_id(db, -1, "select fsl_sym2rid('root:7bfbc3dba6c65')");
assert(1==rid);
/* f_out("SELECT fsl_sym2rid(...) got %"FSL_ID_T_PFMT"\n", (fsl_id_t)rid); */
rid = fsl_db_g_id(db, -666, "select fsl_sym2rid('abcdefabcdef')");
assert(-666==rid && "Very unexpected RID.");
assert(db->error.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.ctime<now);
assert(fst.mtime>0 && fst.mtime<now);
assert(fst.size>0);
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<time(0));
fsl_free(s);
fsl_buffer_clear(&b);
assert(fsl_dir_check(".") > 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);
fcli_err_reset();
zName = "..";
buf.used = 0;
rc = fsl_checkout_filename_check(f, 1, zName, &buf);
assert(0 == rc);
assert(1 == buf.used);
assert('.' == buf.mem[0]);
buf.used = 0;
zName = "../";
rc = fsl_checkout_filename_check(f, 1, zName, &buf);
assert(0 == rc);
assert(2 == buf.used);
assert('.' == buf.mem[0]);
assert('/' == buf.mem[1]);
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 = gmtime(&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);
timey = 0;
rc = fsl_mtime_of_manifest_file(f, mrid, 0, &timey);
assert(!rc);
assert(timey>0);
timey = 0;
/* 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, &vid, &uuid);
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);
fname = "tools/";
rc = fsl_checkout_file_rm( f, 0, fname, 1 );
assert(0==rc);
fsl_db_each( fsl_cx_db(f), fsl_stmt_each_f_dump, NULL,
"SELECT * FROM vfile WHERE deleted=1");
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, &ckoutRid, NULL);
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, &vid, NULL);
assert(vid>0);
ex.counter = 0;
rc = fsl_repo_extract(fcli_cx(), vid, 0, 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_filename_to_fnid(){
int rc;
fsl_cx * f = fcli_cx();
fsl_id_t fnid = 0;
f_out("%s()...\n", __func__);
rc = fsl_repo_filename_to_fnid( f, "Makefile.in", &fnid, 0 );
assert(!rc);
assert(fnid>0);
rc = fsl_repo_filename_to_fnid(f, "NoSuchFile", &fnid, 0);
assert(0==rc);
assert(!fnid);
}
static void test_checkin_file_list(){
int rc;
fsl_cx * f = fcli_cx();
char const * fn;
fsl_id_bag const * fList = &f->ckin.selectedIds;
f_out("%s()...\n", __func__);
fsl_db_transaction_begin(fsl_cx_db_checkout(f));
fn = "f-sanity.c";
rc = fsl_checkin_file_enqueue(f, fn, 1);
assert(0==rc);
assert(1 == fList->entryCount);
fn = "src/fsl.c";
rc = fsl_checkin_file_enqueue(f, fn, 0);
assert(0==rc);
assert(2 == fList->entryCount);
fn = "../fsl.c";
rc = fsl_checkin_file_enqueue(f, fn, 0);
assert((FSL_RC_RANGE==rc) && "File is outside of checkout tree.");
assert(2 == fList->entryCount);
fcli_err_reset();
fn = "../src/fsl.c";
rc = fsl_checkin_file_enqueue(f, fn, 1);
assert(0==rc);
assert((2 == fList->entryCount) && "This is a dupe.");
assert(fsl_checkin_file_is_enqueued(f, fn, 1));
assert(!fsl_checkin_file_is_enqueued(f, fn, 0));
fn = "no-such.file";
rc = fsl_checkin_file_enqueue(f, fn, 0);
/* fcli_err_report(1); */
assert(FSL_RC_NOT_FOUND==rc);
fcli_err_reset();
fn = "../src/fsl.c";
rc = fsl_checkin_file_dequeue(f, fn, 1);
assert(0==rc);
assert(1==fList->entryCount);
fn = "../src";
rc = fsl_checkin_file_enqueue(f, fn, 1);
assert(0==rc);
assert(fsl_checkin_file_is_enqueued(f, "src/fsl_diff.c", 0));
{
fsl_checkin_opt opt = fsl_checkin_opt_empty;
opt.message = "Blah";
opt.messageMimeType = "text/plain";
rc = fsl_checkin_commit( f, &opt, NULL, NULL );
fcli_err_report(1);
assert(!rc || (FSL_RC_RANGE==rc && "no files to commit"));
}
fcli_err_reset();
fsl_db_transaction_rollback(fsl_cx_db_checkout(f));
}
/* static */ void test_repo_is_readonly(){
/* TODO */
}
#include <unistd.h>
int main(int argc, char * const * argv ){
int rc = 0;
fsl_cx * f;
char singleTest;
fsl_timer_state timer = fsl_timer_state_empty;
fsl_timer_start(&timer);
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();
test_repo_filename_to_fnid();
test_checkin_file_list();
}
}else{
/* placeholder for use while developing tests. */
if(0) test_repo_extract() /* VERY memory-hungry! */;
test_file_rm();
}
{
char const * ckoutUuid = NULL;
fsl_id_t ckoutRid = 0;
fsl_checkout_version_info(f, &ckoutRid, &ckoutUuid);
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");
{
fsl_double_t runTime = fsl_timer_reset(&timer) / 1000.0;
#if 0
assert(timer.user);
assert(timer.system) /* lol - for single tests we
don't always use any system time */;
#endif
f_out("Total run time: %"FSL_DOUBLE_T_PFMT" seconds of CPU time\n",
runTime/1000);
}
return (fcli_err_report(0)||rc)
? EXIT_FAILURE : EXIT_SUCCESS;
}