// tstore4.cpp -- Regression test program, storage tests, part 4
// $Id$
// This is part of Metakit, see http://www.equi4.com/metakit/

#include "regress.h"

void TestStores4()
{
  B(s30, Memo storage, 0) W(s30a);
  {
    c4_Bytes hi ("hi", 2);
    c4_Bytes gday ("gday", 4);
    c4_Bytes hello ("hello", 5);

    c4_MemoProp p1 ("p1");
    c4_Storage s1 ("s30a", 1);
    s1.SetStructure("a[p1:B]");
    c4_View v1 = s1.View("a");

    v1.Add(p1 [hi]);
      A(p1 (v1[0]) == hi);
    v1.Add(p1 [hello]);
      A(p1 (v1[0]) == hi);
      A(p1 (v1[1]) == hello);
    v1.InsertAt(1, p1 [gday]);
      A(p1 (v1[0]) == hi);
      A(p1 (v1[1]) == gday);
      A(p1 (v1[2]) == hello);
    s1.Commit();
      A(p1 (v1[0]) == hi);
      A(p1 (v1[1]) == gday);
      A(p1 (v1[2]) == hello);

  } D(s30a); R(s30a); E;

    // this failed in the unbuffered 1.8.5a interim release in Mk4tcl 1.0.5
  B(s31, Check sort buffer use, 0) W(s31a);
  {
    c4_IntProp p1 ("p1");
    c4_Storage s1 ("s31a", 1);
    s1.SetStructure("a[p1:I]");
      c4_View v1 = s1.View("a");
      v1.Add(p1 [3]);
      v1.Add(p1 [1]);
      v1.Add(p1 [2]);
    s1.Commit();

    c4_View v2 = v1.SortOn(p1);
      A(v2.GetSize() == 3);
      A(p1 (v2[0]) == 1);
      A(p1 (v2[1]) == 2);
      A(p1 (v2[2]) == 3);

  } D(s31a); R(s31a); E;

    // this failed in 1.8.6, fixed 19990828
  B(s32, Set memo empty or same size, 0) W(s32a);
  {
    c4_Bytes empty;
    c4_Bytes full ("full", 4);
    c4_Bytes more ("more", 4);

    c4_MemoProp p1 ("p1");
    c4_Storage s1 ("s32a", 1);
    s1.SetStructure("a[p1:B]");
    c4_View v1 = s1.View("a");

    v1.Add(p1 [full]);
      A(p1 (v1[0]) == full);
    s1.Commit();
      A(p1 (v1[0]) == full);
    
    p1 (v1[0]) = empty;
      A(p1 (v1[0]) == empty);
    s1.Commit();
      A(p1 (v1[0]) == empty);

    p1 (v1[0]) = more;
      A(p1 (v1[0]) == more);
    s1.Commit();
      A(p1 (v1[0]) == more);

    p1 (v1[0]) = full;
      A(p1 (v1[0]) == full);
    s1.Commit();
      A(p1 (v1[0]) == full);

  } D(s32a); R(s32a); E;

    // this failed in 1.8.6, fixed 19990828
  B(s33, Serialize memo fields, 0) W(s33a); W(s33b); W(s33c);
  {
    c4_Bytes hi ("hi", 2);
    c4_Bytes gday ("gday", 4);
    c4_Bytes hello ("hello", 5);

    c4_MemoProp p1 ("p1");

    c4_Storage s1 ("s33a", 1);
    s1.SetStructure("a[p1:B]");
    c4_View v1 = s1.View("a");

    v1.Add(p1 [hi]);
    v1.Add(p1 [gday]);
    v1.Add(p1 [hello]);
      A(p1 (v1[0]) == hi);
      A(p1 (v1[1]) == gday);
      A(p1 (v1[2]) == hello);
    s1.Commit();
      A(p1 (v1[0]) == hi);
      A(p1 (v1[1]) == gday);
      A(p1 (v1[2]) == hello);

    {
      c4_FileStream fs1 (fopen("s33b", "wb"), true);
      s1.SaveTo(fs1);
    }

    c4_Storage s2 ("s33c", 1);

    c4_FileStream fs2 (fopen("s33b", "rb"), true);
    s2.LoadFrom(fs2);

    c4_View v2 = s2.View("a");
      A(p1 (v2[0]) == hi);
      A(p1 (v2[1]) == gday);
      A(p1 (v2[2]) == hello);
    s2.Commit();
      A(p1 (v2[0]) == hi);
      A(p1 (v2[1]) == gday);
      A(p1 (v2[2]) == hello);
    s2.Commit();
      A(p1 (v2[0]) == hi);
      A(p1 (v2[1]) == gday);
      A(p1 (v2[2]) == hello);

  } D(s33a); D(s33b); D(s33c); R(s33a); R(s33b); R(s33c); E;

    // check smarter commit and commit failure on r/o
  B(s34, Smart and failed commits, 0) W(s34a);
  {
    c4_IntProp p1 ("p1");
    {
      c4_Storage s1 ("s34a", 1);
      s1.SetStructure("a[p1:I]");
      c4_View v1 = s1.View("a");
      v1.Add(p1 [111]);
        A(v1.GetSize() == 1);
        A(p1 (v1[0]) == 111);
      bool f1 = s1.Commit();
        A(f1);
        A(v1.GetSize() == 1);
        A(p1 (v1[0]) == 111);
      bool f2 = s1.Commit();
        A(f2); // succeeds, but should not write anything
        A(v1.GetSize() == 1);
        A(p1 (v1[0]) == 111);
    }
    {
      c4_Storage s1 ("s34a", 0);
      c4_View v1 = s1.View("a");
      v1.Add(p1 [222]);
        A(v1.GetSize() == 2);
        A(p1 (v1[0]) == 111);
        A(p1 (v1[1]) == 222);
      bool f1 = s1.Commit();
        A(!f1);
        A(v1.GetSize() == 2);
        A(p1 (v1[0]) == 111);
        A(p1 (v1[1]) == 222);
    }
  } D(s34a); R(s34a); E;

  B(s35, Datafile with preamble, 0) W(s35a);
  {
    {
      c4_FileStream fs1 (fopen("s35a", "wb"), true);
      fs1.Write("abc", 3);
    }
    c4_IntProp p1 ("p1");
    {
      c4_Storage s1 ("s35a", 1);
      s1.SetStructure("a[p1:I]");
      c4_View v1 = s1.View("a");
      v1.Add(p1 [111]);
        A(v1.GetSize() == 1);
        A(p1 (v1[0]) == 111);
      bool f1 = s1.Commit();
        A(f1);
        A(v1.GetSize() == 1);
        A(p1 (v1[0]) == 111);
      bool f2 = s1.Commit();
        A(f2); // succeeds, but should not write anything
        A(v1.GetSize() == 1);
        A(p1 (v1[0]) == 111);
    }
    {
      c4_FileStream fs1 (fopen("s35a", "rb"), true);
    char buffer [10];
      int n1 = fs1.Read(buffer, 3);
    A(n1 == 3);
    A(c4_String (buffer, 3) == "abc");
    }
    {
      c4_Storage s1 ("s35a", 0);
      c4_View v1 = s1.View("a");
        A(v1.GetSize() == 1);
        A(p1 (v1[0]) == 111);
      v1.Add(p1 [222]);
        A(v1.GetSize() == 2);
        A(p1 (v1[0]) == 111);
        A(p1 (v1[1]) == 222);
      bool f1 = s1.Commit();
        A(!f1);
        A(v1.GetSize() == 2);
        A(p1 (v1[0]) == 111);
        A(p1 (v1[1]) == 222);
    }
  } D(s35a); R(s35a); E;

  B(s36, Commit after load, 0) W(s36a); W(s36b);
  {
    c4_IntProp p1 ("p1");
  
    c4_Storage s1 ("s36a", 1);
    s1.SetStructure("a[p1:I]");
    c4_View v1 = s1.View("a");
    v1.Add(p1 [111]);
      A(v1.GetSize() == 1);
      A(p1 (v1[0]) == 111);

    {
      c4_FileStream fs1 (fopen("s36b", "wb"), true);
      s1.SaveTo(fs1);
    }

    p1 (v1[0]) = 222;
    v1.Add(p1 [333]);
    bool f1 = s1.Commit();
      A(f1);
      A(v1.GetSize() == 2);
      A(p1 (v1[0]) == 222);
      A(p1 (v1[1]) == 333);

    c4_FileStream fs2 (fopen("s36b", "rb"), true);
    s1.LoadFrom(fs2);
      //A(v1.GetSize() == 0); // should be detached, but it's still 2

    c4_View v2 = s1.View("a");
      A(v2.GetSize() == 1);
      A(p1 (v2[0]) == 111);

      // this fails in 2.4.0, reported by James Lupo, August 2001
    bool f2 = s1.Commit();
      A(f2);
  } D(s36a); D(s36b); R(s36a); R(s36b); E;

    // fails in 2.4.1, reported Oct 31. 2001 by Steve Baxter
  B(s37, Change short partial fields, 0) W(s37a);
  {
    c4_BytesProp p1 ("p1");
    c4_Storage s1( "s37a", true );
    c4_View v1 = s1.GetAs("v1[key:I,p1:B]");

    v1.Add(p1 [c4_Bytes ("12345", 6)]);
      A(v1.GetSize() == 1);
    s1.Commit();

    c4_Bytes buf = p1 (v1[0]);
      A(buf.Size() == 6);
      A(buf == c4_Bytes ("12345", 6));
    buf = p1(v1[0]).Access(1,3);
      A(buf == c4_Bytes ("234", 3));
    p1 (v1[0]).Modify(c4_Bytes ("ab", 2), 2, 0);
    s1.Commit();

    buf = p1 (v1[0]);
      A(buf == c4_Bytes ("12ab5", 6));
  } D(s37a); R(s37a); E;

    // Gross memory use (but no leaks), January 2002, Murat Berk
  B(s38, Lots of empty subviews, 0) W(s38a);
  {
    c4_BytesProp p1 ("p1");
    {
      c4_Storage s1( "s38a", true );
      c4_View v = s1.GetAs("v[v1[p1:S]]");

      v.SetSize(100000);
      s1.Commit();
    }
    {
      c4_Storage s2( "s38a", true );
      c4_View v2 = s2.View("v");
      // this should not materialize all the empty subviews
      v2.SetSize(v2.GetSize() + 1);
      // nor should this
      s2.Commit();
    }
    {
      c4_Storage s3( "s38a", true );
      c4_View v3 = s3.View("v");
      v3.RemoveAt(1, v3.GetSize() - 2);
	A(v3.GetSize() == 2);
      s3.Commit();
    }
  } D(s38a); R(s38a); E;

    // Fix bug introduced on 7-2-2002, as reported by M. Berk
  B(s39, Do not detach empty top-level views, 0) W(s39a);
  {
    c4_IntProp p1 ("p1");
    c4_Storage s1( "s39a", true );
    c4_View v1 = s1.GetAs("v1[p1:I]");
    s1.Commit();
      A(v1.GetSize() == 0);
    v1.Add(p1 [123]);
      A(v1.GetSize() == 1);
    s1.Commit();
    c4_View v2 = s1.View("v1");
      A(v2.GetSize() == 1); // fails with 0 due to recent bug
  } D(s39a); R(s39a); E;
}
