Login
Documentation
Login
/* -*- 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/
**
*******************************************************************************
** Test/demo code for the fossil library API.
*/
/* Force assert() to always work... */
#if defined(NDEBUG)
#undef NDEBUG
#define DEBUG 1
#endif
#include "fossil-scm/fossil-internal.h"
#include "fossil-scm/fossil-cli.h"
#include <assert.h>

static int test_buffer_0(){
  fsl_buffer buf = fsl_buffer_empty;
  int rc = 0;
  fsl_cx * f = fcli.f;
  fsl_size_t sz, szOrig;
  char const * infile = __FILE__;
  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);


  
  return rc;  
}

static int 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=?";
  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);

  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);
  rc = 0;

  f_out("Statement caching seems to work.\n");
  return rc;
}

/* static */ int test_db_beforeCommit(){
  int rc;
  fsl_cx * f = fcli.f;
  fsl_db * db = fsl_cx_db_checkout(f);
  char * str;

  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);
  return rc;
}


static int test0(){
 fsl_cx * f = fcli.f;
 int rc = 0;
  if(fsl_cx_db_repo(f)){
    f_out("ID of tag 'sym-trunk'=%"FSL_ID_T_PFMT"\n",
            fsl_tag_id(f, "sym-trunk", 0));
    f_out("ID of tag 'tag-creation-test2'=%"FSL_ID_T_PFMT"\n",
            fsl_tag_id(f, "tag-creation-test2", 1));
    assert(0 == fsl_tag_id(f, "no-such-tag", 0));
  }
  if(fsl_cx_db_checkout(f)){
    fsl_id_t id = 0, id2 = 0;
    char * uuid = 0;
    f_out("Running sym-to-rid tests...\n");
    id = 0;
    rc = fsl_sym_to_rid(f, "tip", FSL_ATYPE_ANY, &id);
    assert(0==rc);
    assert(id>0);

    id = 0;
    rc = fsl_sym_to_rid(f, "current", FSL_ATYPE_ANY, &id);
    f_out("rc=%d/%s\n", rc, fsl_rc_cstr(rc));
    assert(0==rc);
    assert(id>0);
    assert(!fsl_content_is_private(f,id));


    id = 0;
    rc = fsl_sym_to_rid(f, "9d8c610ad6", FSL_ATYPE_CHECKIN, &id);
    assert(0==rc);
    assert(id>0);

    id = 0;
    rc = fsl_sym_to_rid(f, "9d8c610ad6", FSL_ATYPE_WIKI, &id);
    assert(FSL_RC_NOT_FOUND==rc);
    assert(id==0);
    fsl_cx_err_reset(f) /* stop propagation of that error. */;

    id = 0;
    rc = fsl_sym_to_rid(f, "trunk", FSL_ATYPE_ANY, &id);
    assert(0==rc);
    assert(id>0);
    assert(fsl_rid_is_leaf(f, id));
    assert(0 == fsl_count_nonbranch_children(f, id));

    if(0){
      fsl_buffer buf = fsl_buffer_empty;
      rc = fsl_content_blob(f, id, &buf);
      assert(!rc);
      f_out("Control artifact for rid #%"FSL_ID_T_PFMT":\n%s",
              id, fsl_buffer_cstr(&buf));
      fsl_buffer_clear(&buf);
    }
    
    id = 0;
    rc = fsl_sym_to_rid(f, "prev", FSL_ATYPE_CHECKIN, &id);
    assert(0==rc);
    assert(id>0);
    assert(!fsl_rid_is_leaf(f, id));
    assert(0 < fsl_count_nonbranch_children(f, id));

    
    id = 0;
    rc = fsl_sym_to_rid(f, "current", FSL_ATYPE_CHECKIN, &id);
    assert(0==rc);
    assert(id>0);

    rc = fsl_sym_to_uuid(f, "current", FSL_ATYPE_CHECKIN, &uuid, &id2);
    assert(0==rc);
    assert(uuid);
    assert(id2 == id);
    assert(FSL_UUID_STRLEN==fsl_strlen(uuid));
    f_out("Current checkout: %.*s\n", FSL_UUID_STRLEN, uuid);
    fsl_free(uuid);
    
  }
  return rc;
}

/* static */ int test_leaves_rebuild(){
  int rc;
  f_out("Rebuilding leaves...\n");
  rc = fsl_repo_leaves_rebuild(fcli.f);
  assert(!rc);
  f_out("Done rebuilding leaves.\n");
  return rc;
}

static int test_content_get(){

  char const * sym =
    /* some version of th1ish/test-003.th1ish */
    "33b40942db0c00eba3a2a0bf1e61ea5c573e902f" /* first version */
    ;
  int rc;
  fsl_id_t rid = 0;
  fsl_buffer c = fsl_buffer_empty;
  fsl_cx * f = fcli.f;
  rc = fsl_sym_to_rid(f, sym, FSL_ATYPE_ANY, &rid);
  assert(!rc);
  assert(rid>0);
  rc = fsl_content_get(f, rid, &c);
  fsl_cx_err_report(f, 1);
  assert(!rc);
  assert(1032==c.used);
  rc = fsl_sha1sum_buffer( &c, &c );
  assert(!rc);
  assert(FSL_UUID_STRLEN==c.used);
  assert(0==fsl_strcmp( sym, fsl_buffer_cstr(&c) ));

  /* Now fetch a few other versions of that same file
     and ensure that they meet our expectations...
  */
  
  sym = "e9b776a8a72753823e4553a309edae21897cb2c4";
  rid = 0;
  rc = fsl_sym_to_rid(f, sym, FSL_ATYPE_ANY, &rid);
  assert(!rc);
  assert(rid>0);
  rc = fsl_content_get(f, rid, &c);
  assert(!rc);
  assert(2076==c.used);
  rc = fsl_sha1sum_buffer( &c, &c );
  assert(!rc);
  assert(FSL_UUID_STRLEN==c.used);
  assert(0==fsl_strcmp( sym, fsl_buffer_cstr(&c) ));

  sym = "f7d3a2e155d59a96ecc37001b05de26dee23c0cf";
  rid = 0;
  rc = fsl_sym_to_rid(f, sym, FSL_ATYPE_ANY, &rid);
  assert(!rc);
  assert(rid>0);
  rc = fsl_content_get(f, rid, &c);
  assert(!rc);
  assert(2481==c.used);
  rc = fsl_sha1sum_buffer( &c, &c );
  assert(!rc);
  assert(FSL_UUID_STRLEN==c.used);
  assert(0==fsl_strcmp( sym, fsl_buffer_cstr(&c) ));


  sym = "31e01e2a3c";
  rid = 0;
  rc = fsl_sym_to_rid(f, sym, FSL_ATYPE_WIKI, &rid);
  assert(!rc);
  assert(rid>0);
  rc = fsl_content_get(f, rid, &c);

  
#if 1
  if(0){
    f_out("got content [%s]:\n%.*s", sym, (int)c.used,
            (char const *)c.mem);
  }else{
    f_out("got content [%.*s]: %d byte(s)\n", 8, sym, (int)c.used);
  }
#endif
  fsl_buffer_clear(&c);
  return rc;
}

static int test_hash_this_file(){
  fsl_buffer hash = fsl_buffer_empty;
  int rc;

  rc = fsl_md5sum_filename(__FILE__, &hash);
  assert(!rc);
  f_out("MD5 of this file: %s\n", fsl_buffer_cstr(&hash));

  rc = fsl_sha1sum_filename(__FILE__, &hash);
  assert(!rc);
  f_out("SHA1 of this file: %s\n", fsl_buffer_cstr(&hash));
  fsl_buffer_clear(&hash);
  return rc;
}

static int test_text_diff(){
  fsl_buffer lhs = fsl_buffer_empty;
  fsl_buffer rhs = fsl_buffer_empty;
  fsl_buffer diff = fsl_buffer_empty;
  int rc;
  int context = 1;
  rc = fsl_buffer_fill_from_filename(&lhs, __FILE__);
  assert(!rc);
  rc = fsl_buffer_append( &rhs, lhs.mem, lhs.used );
  assert(!rc);
  for( rc = 0; rc < 20; ++rc ){
    rhs.mem[rc + 100] = '*';
  }

  rc = fsl_diff_text_to_buffer( &lhs, &rhs, &diff, context, 0,
                                FSL_DIFF_INLINE);
  f_out("diff rc=%s, output len=%"FSL_SIZE_T_PFMT"\n", fsl_rc_cstr(rc), diff.used);
  f_out("Diff from this file and a mangled copy:\n%b", &diff);
  fsl_buffer_clear(&diff);
  f_out("\nInverted...\n");
  rc = fsl_diff_text( &lhs, &rhs, fsl_output_f_FILE, stdout, context, 0,
                      FSL_DIFF_INVERT);

  f_out("\nWith line numbers...\n");
  rc = fsl_diff_text( &lhs, &rhs, fsl_output_f_FILE, stdout, context, 0,
                      FSL_DIFF_INLINE | FSL_DIFF_LINENO);

  f_out("\nSide-by-side...\n");
  rc = fsl_diff_text( &lhs, &rhs, fsl_output_f_FILE, stdout,
                      context, 30,
                      0 /*FSL_DIFF_SIDEBYSIDE is implicit when sbsWidth>0*/);
  f_out("\nInverted...\n");
  rc = fsl_diff_text( &lhs, &rhs, fsl_output_f_FILE, stdout,
                      context, 30,
                      FSL_DIFF_INVERT);

  if(0){
    f_out("\nAnd using HTML...\n");
    rc = fsl_diff_text( &lhs, &rhs, fsl_output_f_FILE, stdout, 3, 80,
                        FSL_DIFF_SIDEBYSIDE | FSL_DIFF_HTML);
  }
  fsl_buffer_clear(&lhs);
  fsl_buffer_clear(&rhs);

#if 0
  printf("[%*s]\n", 7, "-----");
  f_out("[%*s]\n", 7, "-----");
  printf("[%7s]\n", "-----");
  f_out("[%7s]\n", "-----");
  printf("[%*s]\n", 3, "-----");
  f_out("[%*s]\n", 3, "-----");
  printf("[%3s]\n", "-----");
  f_out("[%3s]\n", "-----");
  printf("[%.*s]\n", 7, "-----");
  f_out("[%.*s]\n", 7, "-----");
  printf("[%.*s]\n", 3, "-----");
  f_out("[%.*s]\n", 3, "-----");
  printf("[%*s]\n", 10, "-----");
  f_out("[%*s]\n", 10, "-----");
  printf("[%-*s]\n", 10, "-----");
  f_out("[%-*s]\n", 10, "-----");
#endif
  return rc;
}

static void fcli_local_help(){
  puts("This is a scratchpad app and has no help.");
}

int main(int argc, char * const * argv ){
  int rc = 0;
  fsl_cx * f = NULL;;
  assert(sizeof(fsl_int32_t) >= 4);
  assert(sizeof(fsl_int64_t) >= 8);
  fcli.appHelp = fcli_local_help;
  rc = fcli_setup(argc, argv);
  if(rc) goto end;
  f = fcli.f;

  rc = test0();
  if(!rc) rc = test_buffer_0();
#if 0 && defined(SQLITE_FCNTL_TEMPFILENAME)
  if(!rc) rc = test_tmpfile_0();
#endif

  if(!rc && fsl_cx_db_repo(fcli.f)){
    /* rc = test_leaves_rebuild(); */
    if(!rc) rc = test_content_get();
  }
  if(!rc){
    rc = test_hash_this_file();
  }
  if(!rc && fsl_cx_db_checkout(f)){
    test_db_beforeCommit();
  }
  if(!rc && fsl_cx_db(f)){
    rc = test_stmt_cached();
  }
  if(!rc){
    rc = test_text_diff();
  }
  
  end:
  f_out("Done! rc=%d (%s)\n", rc, fsl_rc_cstr(rc));
  return (fcli_err_report(0) || rc)
    ? EXIT_FAILURE : EXIT_SUCCESS;
}