@Test
  public void testTypedKeySet() throws Exception {
    fact.register(HasCollections.class);
    TestObjectify ofy = this.fact.begin();

    Key<Trivial> key7 = Key.create(Trivial.class, 7);
    Key<Trivial> key8 = Key.create(Trivial.class, 8);
    Key<Trivial> key9 = Key.create(Trivial.class, 9);

    HasCollections hc = new HasCollections();
    hc.typedKeySet = new HashSet<Key<Trivial>>();
    hc.typedKeySet.add(key7);
    hc.typedKeySet.add(key8);
    hc.typedKeySet.add(key9);

    Key<HasCollections> key = ofy.save().entity(hc).now();
    hc = ofy.load().key(key).get();

    assert hc.typedKeySet instanceof HashSet<?>;
    assert hc.typedKeySet.size() == 3;

    assert hc.typedKeySet.contains(key7);
    assert hc.typedKeySet.contains(key8);
    assert hc.typedKeySet.contains(key9);
  }
  @Test
  public void testBasicSets() throws Exception {
    fact.register(HasCollections.class);
    TestObjectify ofy = this.fact.begin();

    HasCollections hc = new HasCollections();
    hc.integerSet = new HashSet<Integer>();
    hc.integerSet.add(1);
    hc.integerSet.add(2);
    hc.integerSet.add(3);

    hc.integerSortedSet = new TreeSet<Integer>(hc.integerSet);
    hc.integerHashSet = new HashSet<Integer>(hc.integerSet);
    hc.integerTreeSet = new TreeSet<Integer>(hc.integerSet);
    hc.integerLinkedHashSet = new LinkedHashSet<Integer>(hc.integerSet);

    Key<HasCollections> key = ofy.save().entity(hc).now();
    hc = ofy.load().key(key).get();

    assertContains123(hc.integerSet, HashSet.class);
    assertContains123(hc.integerSortedSet, TreeSet.class);
    assertContains123(hc.integerHashSet, HashSet.class);
    assertContains123(hc.integerTreeSet, TreeSet.class);
    assertContains123(hc.integerLinkedHashSet, LinkedHashSet.class);
  }
  @Test
  public void testMonotonic() throws Exception {
    this.fact.register(HasNumber.class);

    TestObjectify ofy = fact.begin();

    HasNumber hn = new HasNumber();
    hn.number = Monotonic.next(ofy, HasNumber.class, "number");
    assert hn.number == 1;

    ofy.put(hn);

    hn = new HasNumber();
    hn.number = Monotonic.next(ofy, HasNumber.class, "number");
    assert hn.number == 2;
  }
  @Test
  public void testCustomSet() throws Exception {
    fact.register(HasCollections.class);
    TestObjectify ofy = this.fact.begin();

    HasCollections hc = new HasCollections();
    hc.customSet = new CustomSet();
    hc.customSet.add(1);
    hc.customSet.add(2);
    hc.customSet.add(3);

    Key<HasCollections> key = ofy.save().entity(hc).now();
    hc = ofy.load().key(key).get();

    assertContains123(hc.customSet, CustomSet.class);
  }
  @Test
  public void testGrouping() throws Exception {
    fact.register(HasEntitiesWithGroups.class);

    HasEntitiesWithGroups he = new HasEntitiesWithGroups();
    he.single = Ref.create(k1);
    he.multi.add(Ref.create(k1));
    he.multi.add(Ref.create(k2));
    HasEntitiesWithGroups fetched = this.putClearGet(he);

    Key<HasEntitiesWithGroups> hekey = Key.create(he);

    assert fetched.single.key().equals(k1);
    assertRefUninitialzied(fetched.single);

    assert fetched.multi.get(0).equals(fetched.single);
    for (Ref<Trivial> ref : fetched.multi) assertRefUninitialzied(ref);

    TestObjectify ofy = fact.begin();

    fetched = ofy.load().group(Single.class).key(hekey).get();
    assert fetched.single.get().getId().equals(t1.getId());
    assert fetched.single.get().getSomeString().equals(t1.getSomeString());
    assert fetched.multi.get(0).equals(fetched.single);
    for (Ref<Trivial> ref : fetched.multi) assertRefUninitialzied(ref);

    fetched = ofy.load().group(Multi.class).key(hekey).get();
    assert fetched.multi.get(0).get().getId().equals(t1.getId());
    assert fetched.multi.get(0).get().getSomeString().equals(t1.getSomeString());
    assert fetched.multi.get(1).get().getId().equals(t2.getId());
    assert fetched.multi.get(1).get().getSomeString().equals(t2.getSomeString());
    // Not valid anymore, everything is done separately
    // assert fetched.single.get() == fetched.multi.get(0).get();

    // This is an interesting question - do we "downgrade" refs which are no longer included in the
    // subsequent load?
    // Seems the answer should be no, but not sure.
    // assertRefUninitialzied(fetched.single);

    fetched = ofy.load().group(Single.class).group(Multi.class).key(hekey).get();
    assert fetched.multi.get(0).get().getId().equals(t1.getId());
    assert fetched.multi.get(0).get().getSomeString().equals(t1.getSomeString());
    assert fetched.multi.get(1).get().getId().equals(t2.getId());
    assert fetched.multi.get(1).get().getSomeString().equals(t2.getSomeString());
    assert fetched.single.get() == fetched.multi.get(0).get();
  }
  @Before
  public void createTwo() {
    fact.register(Trivial.class);
    TestObjectify ofy = fact.begin();

    t1 = new Trivial("foo", 11);
    k1 = ofy.put(t1);

    t2 = new Trivial("bar", 22);
    k2 = ofy.put(t2);

    tNone1 = new Trivial(123L, "fooNone", 33);
    tNone2 = new Trivial(456L, "barNone", 44);

    kNone1 = Key.create(tNone1);
    kNone2 = Key.create(tNone2);
  }
  @Test
  public void testBasicLists() throws Exception {
    fact.register(HasCollections.class);
    TestObjectify ofy = this.fact.begin();

    HasCollections hc = new HasCollections();
    hc.integerList = Arrays.asList(1, 2, 3);

    hc.integerArrayList = new ArrayList<Integer>(hc.integerList);
    hc.integerLinkedList = new LinkedList<Integer>(hc.integerList);

    Key<HasCollections> key = ofy.save().entity(hc).now();
    ofy.clear();
    hc = ofy.load().key(key).get();

    assertContains123(hc.integerList, ArrayList.class);
    assertContains123(hc.integerArrayList, ArrayList.class);
    assertContains123(hc.integerLinkedList, LinkedList.class);
  }
  /** Rule: never store a null Collection, always leave it alone when loaded */
  @Test
  public void testNullCollections() throws Exception {
    fact.register(HasCollections.class);
    TestObjectify ofy = this.fact.begin();

    HasCollections hc = new HasCollections();
    hc.integerList = null;

    Key<HasCollections> key = ofy.save().entity(hc).now();
    hc = ofy.load().key(key).get();

    ofy.save().entity(hc).now();
    hc = ofy.load().key(key).get();
    assert hc.integerList == null; // not loaded

    Entity e = ds().get(fact.getRawKey(key));
    // rule : never store a null collection
    assert !e.hasProperty("integerList");
  }
  @Test
  public void testMissingTail() throws Exception {
    fact.register(ListNode.class);

    TestObjectify ofy = fact.begin();

    // Node2 should not exist but should have a concrete id for node1
    ListNode node2 = new ListNode();
    node2.id = 999L;

    ListNode node1 = new ListNode();
    node1.foo = "foo1";
    node1.next = Ref.create(Key.create(node2));
    ofy.put(node1);

    ofy.clear();
    ListNode fetched = ofy.get(Key.create(node1));

    assert fetched.foo.equals(node1.foo);
    assert fetched.next.get() == null; // it was fetched, so this should be initialized and null.
  }
  @Test
  public void testTwoLevelsOfFetch() throws Exception {
    fact.register(ListNode.class);

    TestObjectify ofy = fact.begin();

    ListNode node3 = new ListNode();
    node3.foo = "foo3";
    ofy.put(node3);

    ListNode node2 = new ListNode();
    node2.foo = "foo2";
    node2.next = Ref.create(node3);
    ofy.put(node2);

    ListNode node1 = new ListNode();
    node1.foo = "foo1";
    node1.next = Ref.create(node2);
    ofy.put(node1);

    ofy.clear();
    ListNode fetched = ofy.get(Key.create(node1));

    assert fetched.foo.equals(node1.foo);
    assert fetched.next.get().id.equals(node2.id);
    assert fetched.next.get().foo.equals(node2.foo);
    assert fetched.next.get().next.get().id.equals(node3.id);
    assert fetched.next.get().next.get().foo.equals(node3.foo);
    assert fetched.next.get().next.get().next == null;
  }
  @Test
  public void testCollectionContainingNull() throws Exception {
    fact.register(HasCollections.class);
    TestObjectify ofy = this.fact.begin();

    HasCollections hc = new HasCollections();
    hc.integerList = Arrays.asList((Integer) null);

    Key<HasCollections> key = ofy.save().entity(hc).now();
    hc = ofy.load().key(key).get();

    assert hc.integerList != null;
    assert hc.integerList.size() == 1;
    assert hc.integerList.get(0) == null;

    Entity e = ds().get(fact.getRawKey(key));
    assert e.hasProperty("integerList");
    List<?> l = (List<?>) e.getProperty("integerList");
    assert l != null;
    assert l.size() == 1;
    assert l.get(0) == null;
  }
  /** Test rule: never store an empty Collection, leaves value as null */
  @Test
  public void testEmptyCollections() throws Exception {
    fact.register(HasCollections.class);
    TestObjectify ofy = this.fact.begin();

    HasCollections hc = new HasCollections();
    hc.integerList = new ArrayList<Integer>();

    Key<HasCollections> key = ofy.save().entity(hc).now();
    ofy.clear();
    hc = ofy.load().key(key).get();

    System.out.println(ds().get(fact.getRawKey(hc)));

    // This wouldn't be valid if we didn't clear the session
    assert hc.integerList == null;

    Entity e = ds().get(fact.getRawKey(key));
    // rule : never store an empty collection
    assert !e.hasProperty("integerList");

    assert hc.initializedList != null;
    assert hc.initializedList instanceof LinkedList<?>;
  }
  @Test
  public void testGrouping() throws Exception {
    fact.register(HasEntitiesWithGroups.class);

    HasEntitiesWithGroups he = new HasEntitiesWithGroups();
    he.single = Ref.create(k1);
    he.multi.add(Ref.create(k1));
    he.multi.add(Ref.create(k2));
    HasEntitiesWithGroups fetched = this.putClearGet(he);

    Key<HasEntitiesWithGroups> hekey = Key.create(he);

    assert fetched.single.key().equals(k1);
    assertRefUninitialzied(fetched.single);

    assert fetched.multi.get(0).equals(fetched.single);
    for (Ref<Trivial> ref : fetched.multi) assertRefUninitialzied(ref);

    TestObjectify ofy = fact.begin();

    ofy.clear();
    fetched = ofy.load().group(Single.class).key(hekey).get();
    assert fetched.single.get().getId().equals(t1.getId());
    assert fetched.single.get().getSomeString().equals(t1.getSomeString());
    assert fetched.multi.get(0).equals(fetched.single);
    for (Ref<Trivial> ref : fetched.multi) assertRefUninitialzied(ref);

    ofy.clear();
    fetched = ofy.load().group(Multi.class).key(hekey).get();
    assert fetched.multi.get(0).get().getId().equals(t1.getId());
    assert fetched.multi.get(0).get().getSomeString().equals(t1.getSomeString());
    assert fetched.multi.get(1).get().getId().equals(t2.getId());
    assert fetched.multi.get(1).get().getSomeString().equals(t2.getSomeString());

    assert fetched.multi.get(0).equals(fetched.single);
    assertRefUninitialzied(fetched.single);

    ofy.clear();
    fetched = ofy.load().group(Single.class).group(Multi.class).key(hekey).get();
    assert fetched.multi.get(0).get().getId().equals(t1.getId());
    assert fetched.multi.get(0).get().getSomeString().equals(t1.getSomeString());
    assert fetched.multi.get(1).get().getId().equals(t2.getId());
    assert fetched.multi.get(1).get().getSomeString().equals(t2.getSomeString());
    assert fetched.single.get() == fetched.multi.get(0).get();
  }