concept Bla { new Ref ref; } svoid test_cset_with_Ref() { // unlisted concepts do not track back references Bla a = unlisted Bla(); Bla b = unlisted Bla(); assertEqualsVerbose(1, cset(a, ref := b)); assertSame(a.ref!, b); assertEmptyVerbose(findBackRefs Bla(b)); new Concepts cc; // test with registered concepts a = cnew(cc, Bla); b = cnew(cc, Bla); assertEqualsVerbose(1, cset(a, ref := b)); assertSame(a.ref!, b); assertEqualsVerbose(ll(a), findBackRefs Bla(b)); assertEqualsVerbose(1, cset(a, ref := null)); assertNull(a.ref!); // make sure the Ref object itself is not deleted assertEqualsVerbose(ll(), findBackRefs Bla(b)); // test that back ref is gone // Now test a "dynamic" (undeclared) field assertEqualsVerbose(1, cset(a, ref2 := b)); assertEqualsVerbose(0, cset(a, ref2 := b)); assertSame(cget ref2(a), b); assertEqualsVerbose(ll(a), findBackRefs Bla(b)); assertEqualsVerbose(1, cset(a, ref2 := null)); assertEqualsVerbose(0, cset(a, ref2 := null)); assertNull(cget ref2(a)); assertEqualsVerbose(ll(), findBackRefs Bla(b)); // test that back ref is gone assertEmptyVerbose(ConceptsRefChecker(cc).run()); // overwrite reference with string assertEqualsVerbose(1, cset(a, ref2 := b)); assertEqualsVerbose(1, cset(a, ref2 := "")); assertEqualsVerbose(0, cset(a, ref2 := "")); assertEqualsVerbose(cget(a, "ref2"), ""); assertEmptyVerbose(ConceptsRefChecker(cc).run()); // overwrite string with reference assertEqualsVerbose(1, cset(a, ref2 := b)); assertEqualsVerbose(cget(a, "ref2"), b); assertEmptyVerbose(ConceptsRefChecker(cc).run()); // check that a list concepts in dynamic field is converted to RefL assertEqualsVerbose(1, cset(b, dynList := ll(a)); assertEqualsVerbose(0, cset(b, dynList := ll(a)); Concept.RefL refL = cast getOpt dynList(b); assertEqualsVerbose(1, l(refL)); assertEquals(a, first(refL)); // TODO: check updating/overwriting RefL }