]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.databoard/testcases/org/simantics/databoard/tests/TestAccessor.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / testcases / org / simantics / databoard / tests / TestAccessor.java
diff --git a/bundles/org.simantics.databoard/testcases/org/simantics/databoard/tests/TestAccessor.java b/bundles/org.simantics.databoard/testcases/org/simantics/databoard/tests/TestAccessor.java
new file mode 100644 (file)
index 0000000..0d0e3f3
--- /dev/null
@@ -0,0 +1,997 @@
+/*******************************************************************************\r
+ *  Copyright (c) 2010 Association for Decentralized Information Management in\r
+ *  Industry THTH ry.\r
+ *  All rights reserved. This program and the accompanying materials\r
+ *  are made available under the terms of the Eclipse Public License v1.0\r
+ *  which accompanies this distribution, and is available at\r
+ *  http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ *  Contributors:\r
+ *      VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.databoard.tests;
+
+import static org.junit.Assert.assertEquals;\r
+import static org.junit.Assert.assertFalse;\r
+import static org.junit.Assert.assertNull;\r
+import static org.junit.Assert.assertTrue;\r
+import gnu.trove.map.hash.TObjectIntHashMap;\r
+\r
+import java.io.File;\r
+import java.net.InetAddress;\r
+import java.util.HashMap;\r
+import java.util.LinkedList;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Random;\r
+import java.util.TreeMap;\r
+import java.util.TreeSet;\r
+\r
+import org.junit.Before;\r
+import org.junit.Test;\r
+import org.simantics.databoard.Accessors;\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.Datatypes;\r
+import org.simantics.databoard.accessor.Accessor;\r
+import org.simantics.databoard.accessor.ArrayAccessor;\r
+import org.simantics.databoard.accessor.BooleanAccessor;\r
+import org.simantics.databoard.accessor.ByteAccessor;\r
+import org.simantics.databoard.accessor.DoubleAccessor;\r
+import org.simantics.databoard.accessor.FloatAccessor;\r
+import org.simantics.databoard.accessor.IntegerAccessor;\r
+import org.simantics.databoard.accessor.LongAccessor;\r
+import org.simantics.databoard.accessor.MapAccessor;\r
+import org.simantics.databoard.accessor.OptionalAccessor;\r
+import org.simantics.databoard.accessor.RecordAccessor;\r
+import org.simantics.databoard.accessor.StringAccessor;\r
+import org.simantics.databoard.accessor.UnionAccessor;\r
+import org.simantics.databoard.accessor.VariantAccessor;\r
+import org.simantics.databoard.accessor.error.AccessorException;\r
+import org.simantics.databoard.accessor.event.Event;\r
+import org.simantics.databoard.accessor.event.InvalidatedEvent;\r
+import org.simantics.databoard.accessor.file.FileVariantAccessor;\r
+import org.simantics.databoard.accessor.impl.ChangeSet;\r
+import org.simantics.databoard.accessor.impl.CompositeRecord;\r
+import org.simantics.databoard.accessor.impl.DirectoryMap;\r
+import org.simantics.databoard.accessor.impl.EventCollection;\r
+import org.simantics.databoard.accessor.interestset.InterestSet;\r
+import org.simantics.databoard.accessor.wire.WireClient;\r
+import org.simantics.databoard.accessor.wire.WireServer;\r
+import org.simantics.databoard.adapter.AdaptException;\r
+import org.simantics.databoard.binding.ArrayBinding;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.BooleanBinding;\r
+import org.simantics.databoard.binding.ByteBinding;\r
+import org.simantics.databoard.binding.DoubleBinding;\r
+import org.simantics.databoard.binding.FloatBinding;\r
+import org.simantics.databoard.binding.IntegerBinding;\r
+import org.simantics.databoard.binding.LongBinding;\r
+import org.simantics.databoard.binding.MapBinding;\r
+import org.simantics.databoard.binding.OptionalBinding;\r
+import org.simantics.databoard.binding.RecordBinding;\r
+import org.simantics.databoard.binding.StringBinding;\r
+import org.simantics.databoard.binding.UnionBinding;\r
+import org.simantics.databoard.binding.VariantBinding;\r
+import org.simantics.databoard.binding.factory.BindingScheme;\r
+import org.simantics.databoard.binding.factory.MutableBindingFactory;\r
+import org.simantics.databoard.binding.impl.ObjectArrayBinding;\r
+import org.simantics.databoard.binding.mutable.MutableVariant;\r
+import org.simantics.databoard.binding.util.RandomValue;\r
+import org.simantics.databoard.method.Client;\r
+import org.simantics.databoard.method.Server;\r
+import org.simantics.databoard.serialization.Serializer;\r
+import org.simantics.databoard.type.Datatype;\r
+import org.simantics.databoard.type.UnionType;\r
+import org.simantics.databoard.util.binary.BinaryMemory;\r
+
+/**
+ * o Set Value
+ * o Get Value
+ * o Listening
+ * o apply
+ * o Rollback
+ * 
+ * o Java Object
+ * o Memory Binary
+ * o File Binary
+ * 
+ * TODO Test {@link InvalidatedEvent}
+ *
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
+ */
+public class TestAccessor {
+
+       RandomValue rv;
+       Map<Datatype, Binding> repository;\r
+       BindingScheme scheme;\r
+       /////////////////////////////////////
+       
+
+       public @Before void init() {\r
+               rv = new RandomValue();\r
+               repository = new HashMap<Datatype, Binding>();\r
+               scheme = new MutableBindingFactory( repository );\r
+       }
+\r
+       public static File createTmpDir()\r
+       {\r
+               String tmp = System.getenv("tmp");\r
+               if (tmp==null) tmp = "c:/temp";\r
+               Random r = new Random();\r
+               String randomName = "tmp-"+(r.nextInt(10000)+10000);\r
+               File tmpDir = new File(tmp+"/"+randomName);\r
+               Boolean ok = tmpDir.mkdirs();\r
+               assertTrue( ok );\r
+               return tmpDir;\r
+       }       \r
+\r
+       public boolean isKeyShortEnough(Binding binding, Object value) throws AdaptException {\r
+               String key = (String) Bindings.adapt(value, binding, Bindings.STR_VARIANT); \r
+               return key.length()<=200;\r
+       }\r
+\r
+       public @Test void testWireAccessor() throws Exception {\r
+               System.out.println("Wire accessor test");\r
+               \r
+               // TODO: Fix this!\r
+               if (true) return;\r
+\r
+               for (int i=0; i<10000; i++) {\r
+                       Bindings.defaultBindingRepository.clear();\r
+                       Bindings.bindingRepository.clear();\r
+                       Bindings.serializerRepository.clear();\r
+                       repository.clear();\r
+                       System.out.println(i+": ");\r
+                       rv = new RandomValue(i); \r
+                       rv.refereableRecords = false;\r
+                       Datatype type = rv.randomType(0, 3);\r
+                       Binding binding = scheme.getBindingUnchecked(type);\r
+                       Object instance = binding.accept(rv);\r
+//                     System.out.println(binding.printValue(instance, true));\r
+                       Accessor accessor = Accessors.getAccessor(binding, instance);\r
+                       \r
+                       WireServer wireServer = new WireServer(accessor);\r
+                       Server server = new Server(0, wireServer.getMethodInterface());\r
+                       WireClient wireClient = new WireClient();\r
+                       Client client = new Client(InetAddress.getByName("localhost"), server.getPort(), wireClient.getClientMethodInterface());\r
+                       wireClient.setServerMethodInterface(client.getConnection().getRemoteMethodInterface());\r
+                       Accessor remoteAccessor = (Accessor) wireClient.getAccessor(null);\r
+                       testAccessor( remoteAccessor, false );\r
+                       \r
+                       wireClient.close();\r
+                       client.close();\r
+                       server.close();\r
+               }\r
+               \r
+       }\r
+               \r
+       public @Test void testBinaryAccessor() throws Exception {\r
+               System.out.println("Test Binary Memory:");\r
+               \r
+               Datatype type = Datatypes.VARIANT;\r
+               Binding binding = scheme.getBindingUnchecked( type );\r
+               Serializer s = binding.serializer();\r
+               TObjectIntHashMap<Object> identities = new TObjectIntHashMap<Object>();\r
+               \r
+               for (int i=0; i<10000; i++) {\r
+                       Bindings.defaultBindingRepository.clear();\r
+                       Bindings.bindingRepository.clear();\r
+                       Bindings.serializerRepository.clear();                  \r
+                       repository.clear();                     \r
+                       rv = new RandomValue(i); \r
+                       rv.getRandom().nextLong();\r
+                       rv.refereableRecords = false;\r
+                       System.out.println(i+": ");\r
+                       BinaryMemory ram = new BinaryMemory(0);\r
+                       Object instance = binding.accept(rv);\r
+                       \r
+                       binding.assertInstaceIsValid(instance);\r
+//                     System.out.println(instance);\r
+                       \r
+                       identities.clear();\r
+                       int size = s.getSize(instance, identities);\r
+                       \r
+                       ram.setLength(size);                    \r
+                       ram.position(0);\r
+                       identities.clear();\r
+                       s.serialize(ram, identities, instance);\r
+                       identities.clear();\r
+\r
+                       ram.position(0L);\r
+                       Object instance2 = s.deserialize(ram);\r
+                       assertTrue( binding.equals(instance, instance2) );\r
+                       binding.assertInstaceIsValid(instance2);\r
+                       \r
+                       VariantAccessor a = (VariantAccessor) Accessors.getAccessor(ram, type);\r
+                       \r
+                       testAccessor(a);\r
+                       \r
+               }\r
+               \r
+       }\r
+\r
+       \r
+       public @Test void testJavaAccessor() throws Exception {\r
+               System.out.println("Test Java Objects:");\r
+               for (int i=0; i<10000; i++) {\r
+                       Bindings.defaultBindingRepository.clear();\r
+                       Bindings.bindingRepository.clear();\r
+                       Bindings.serializerRepository.clear();\r
+                       repository.clear();                     \r
+                       System.out.println(i+": ");\r
+                       rv = new RandomValue(i); \r
+                       rv.refereableRecords = false;\r
+                       Datatype type = rv.randomType(0, 3);\r
+                       Binding binding = scheme.getBindingUnchecked(type);\r
+                       Object instance = binding.accept(rv);\r
+//                     System.out.println(binding.printValue(instance, true));\r
+                       Accessor accessor = Accessors.getAccessor(binding, instance);\r
+                       testAccessor(accessor);\r
+               }\r
+       }       \r
+\r
+       /**\r
+        * This test tests composite record by adding 10 fields, and running the \r
+        * composition as a record accessor\r
+        */\r
+       public @Test void testCompositeRecord() throws Exception {\r
+               System.out.println("Test composite accessor");\r
+               for (int i=0; i<100; i++) {\r
+                       CompositeRecord record = new CompositeRecord();\r
+                       for (int j=1; j<=10; j++) {\r
+                               \r
+                               Binding binding = Bindings.MUTABLE_VARIANT;\r
+                               rv = new RandomValue(i*543+j*23); \r
+                               rv.getRandom().nextLong();\r
+                               rv.refereableRecords = false;   \r
+                               MutableVariant instance = (MutableVariant) binding.accept(rv);\r
+                               String fieldName = "Field"+j;\r
+                               Accessor fa = Accessors.getAccessor(instance.getBinding(), instance.getValue());\r
+                               record.addField(fieldName, fa);\r
+                       }\r
+                       System.out.println(i);\r
+                       \r
+                       testAccessor( record );\r
+               }\r
+       }\r
+       \r
+       \r
+       public @Test void testFolderMap() throws Exception {\r
+               // 1000 tests proves failure\r
+               for (int i=0; i<100; i++) {\r
+                       Bindings.defaultBindingRepository.clear();\r
+                       Bindings.bindingRepository.clear();\r
+                       Bindings.serializerRepository.clear();\r
+                       repository.clear();\r
+                       \r
+                       System.out.println(i+": ");\r
+                       File dir = createTmpDir();\r
+                       DirectoryMap map = Accessors.openDirectory(dir);\r
+                       Binding keyBinding = Bindings.STR_VARIANT; \r
+                       try {\r
+                               System.out.println("Test Folder Map: "+dir);                    \r
+                               \r
+                               // Create 10 files\r
+                               for (int j=0; j<10; j++) {\r
+                                       rv = new RandomValue(i*231231243+j*213); \r
+                                       rv.refereableRecords = false;\r
+                                       \r
+                                       // Create Key                           \r
+                                       String key = "";\r
+                                       do {\r
+                                               key = (String) keyBinding.accept(rv);\r
+                                       } while (key.length() > 240);\r
+                                       \r
+                                       // Create value \r
+                                       Datatype valueType = rv.randomType(0, 2);                                       \r
+                                       Binding valueBinding = scheme.getBindingUnchecked(valueType);\r
+                                       Object value = valueBinding.accept(rv);                                 \r
+\r
+                                       System.out.println(key);\r
+                                       MutableVariant vv = new MutableVariant(valueBinding, value);\r
+                                       map.put(Bindings.STR_VARIANT, key, Bindings.MUTABLE_VARIANT, vv);\r
+                               }\r
+                               \r
+                               // Test the map\r
+                               testAccessor(map);\r
+                               \r
+                       } finally {\r
+                               // Remove all files in the folder\r
+                               map.clear();\r
+                               map.close();\r
+                               System.out.println( dir.listFiles().length + " files.");\r
+                               dir.delete();\r
+                       }\r
+                       \r
+               }\r
+               \r
+       }       \r
+       
+       public @Test void testBinaryFile() throws Exception {
+               File tmpFile = File.createTempFile("TestAccessor", ".dat");
+               System.out.println("Test Binary File: "+tmpFile);
+               tmpFile.deleteOnExit();
+               FileVariantAccessor fa = Accessors.createFile(tmpFile);         
+               Datatype type = Datatypes.VARIANT;
+               Binding binding = scheme.getBindingUnchecked( type );
+               
+               for (int i=0; i<10000; i++) {
+                       Bindings.bindingRepository.clear();\r
+                       Bindings.serializerRepository.clear();\r
+                       Bindings.defaultBindingRepository.clear();\r
+                       repository.clear();\r
+                       \r
+                       System.out.println(i+": ");
+                       rv = new RandomValue(i);                        
+                       rv.getRandom().nextLong();
+                       rv.refereableRecords = false;
+                       
+                       Object instance = binding.accept(rv);
+                       System.out.print(i+": "+ binding.printValueDefinition(instance, true));
+                       fa.setValue(binding, instance);
+                       testAccessor(fa);
+               }
+               fa.close();
+               
+       }
+               \r
+       
+       /////////////////////////////////////
+\r
+    public void testAccessor(Accessor a) throws Exception {\r
+        testAccessor(a, true);\r
+    }\r
+       
+       public void testAccessor(Accessor a, boolean runTypeSpecific) throws Exception {
+               // Init
+               Datatype type = a.type();
+               Binding binding = scheme.getBindingUnchecked(type);
+               InterestSet is = InterestSet.newInterestSet(type, true, true, true);
+               boolean mutable = !binding.isImmutable();
+               
+               // Original value and test value
+               Object origValue = a.getValue(binding);
+               binding.assertInstaceIsValid(origValue);
+               Object testValue = binding.accept(rv);\r
+\r
+               // Test Accessor#getValue(binding, instance);\r
+               Binding mutableBinding = Bindings.getMutableBinding(type);\r
+               Object mutableInstance = mutableBinding.createDefault();\r
+               a.getValue(mutableBinding, mutableInstance);\r
+               assertTrue( Bindings.equals(binding, origValue, mutableBinding, mutableInstance) );\r
+                               
+               boolean same = binding.equals(origValue, testValue);
+               // Create a reference value and an accessor
+               Object refValue = binding.clone(origValue);
+               Accessor ra = Accessors.getAccessor(binding, refValue);
+               
+               if ( mutable ) {
+       
+                       // Write test, without listeners
+                       ra.setValue(binding, testValue);
+                       Object refClone = ra.getValue(binding);
+                       assertTrue( binding.equals(testValue, refClone) );
+                       ra.setValue(binding, binding.clone(origValue) );
+                       refClone = ra.getValue(binding);
+                       assertTrue( binding.equals(origValue, refClone) );
+                       
+                       // Write test, with listeners
+                       ChangeSet cs = new ChangeSet();      // Collects Transformation form origValue to testValue
+                       a.addListener(cs, is, null, null);
+                       a.setValue(binding, testValue); 
+                       // There must be events in change set 
+                       if (!same) assertFalse(cs.isEmpty());
+                                       
+                       // Verify the accessor now contains the test value
+                       Object x = a.getValue(binding);
+                       assertTrue( binding.equals(testValue, x ) ); 
+                       
+                       // Convert reference value to testValue
+                       LinkedList<Event> rollback = new LinkedList<Event>();
+                       List<Event> events = cs.getAndClearEvents();  // Transformation form origValue to testValue
+                       ra.apply(events, rollback);         // Apply transformation from origValue to testValue, and gather rollback, a transformation from testValue to origValue
+                       x = ra.getValue(binding);
+                       assertTrue( binding.equals(x, testValue) );
+                       
+                       // Revert reference value to origValue using rollback
+                       a.removeListener(cs);\r
+                       a.apply(rollback, null);            // Apply transformation from testValue to origValue
+                       x = a.getValue(binding);
+                       assertTrue( binding.equals( x, origValue ) );
+               }
+
+               // Ensure the accessor has the correct value
+               Object x = a.getValue(binding);\r
+               assertTrue( binding.equals( x, origValue) );\r
+               \r
+               if (runTypeSpecific) {\r
+               // Type specific tests
+               if (a instanceof ArrayAccessor) testArrayAccessor((ArrayAccessor) a); 
+               if (a instanceof RecordAccessor) testRecordAccessor((RecordAccessor) a); 
+               if (a instanceof MapAccessor) testMapAccessor((MapAccessor) a); 
+               if (a instanceof BooleanAccessor) testBooleanAccessor((BooleanAccessor) a); 
+               if (a instanceof ByteAccessor) testByteAccessor((ByteAccessor) a); 
+               if (a instanceof DoubleAccessor) testDoubleAccessor((DoubleAccessor) a); 
+               if (a instanceof FloatAccessor) testFloatAccessor((FloatAccessor) a); 
+               if (a instanceof IntegerAccessor) testIntegerAccessor((IntegerAccessor) a); 
+               if (a instanceof LongAccessor) testLongAccessor((LongAccessor) a); 
+               if (a instanceof OptionalAccessor) testOptionalAccessor((OptionalAccessor) a); 
+               if (a instanceof StringAccessor) testStringAccessor((StringAccessor) a); 
+               if (a instanceof UnionAccessor) testUnionAccessor((UnionAccessor) a); 
+               if (a instanceof VariantAccessor) testVariantAccessor((VariantAccessor) a);
+       
+               // Ensure the accessor has the correct value
+               a.setValue(binding, origValue);                 
+               x = a.getValue(binding);\r
+               assertTrue( binding.equals( x, origValue) );\r
+               }               
+       }
+       
+       public void testMapAccessor(MapAccessor a) throws Exception {
+               Datatype type = a.type();
+               MapBinding binding = (MapBinding) scheme.getBindingUnchecked(type);
+               Binding kb = binding.getKeyBinding();
+               Binding vb = binding.getValueBinding();
+               InterestSet is = InterestSet.newInterestSet(type, true, true, false);
+               boolean mutable = !binding.isImmutable();
+               int len = a.size();
+               
+               // getAll(Binding, Binding, Object[], Object[])
+               Object oks[] = new Object[ len ];
+               Object ovs[] = new Object[ len ];
+               a.getAll(kb, vb, oks, ovs);
+\r
+               // getAll(Binding, Binding, Map<Object, Object>)
+               TreeMap<Object, Object> om = new TreeMap<Object, Object>(kb);
+               a.getAll(kb, vb, om);
+
+               // getKeys(Binding), getValues(Binding)
+               Object oks2[] = a.getKeys(kb);
+               Object ovs2[] = a.getValues(vb);                
+               
+               // assert the results are the same for the getAll()s and getKeys and getValues 
+               for (int i=0; i<len; i++) {
+                       assertTrue( om.containsKey(oks[i]) );
+                       assertTrue( om.containsKey(oks2[i]) );
+                       Object v = om.get(oks[i]);
+                       assertTrue( vb.equals( v, ovs[i] ) );
+                       assertTrue( vb.equals( v, ovs2[i] ) );
+                       v = om.get(oks2[i]);
+                       assertTrue( vb.equals( v, ovs[i] ) );
+                       assertTrue( vb.equals( v, ovs2[i] ) );
+               }
+\r
+               // Test Get entries\r
+               if (len>2) {\r
+                       // Get all\r
+                       try {\r
+                   Object keys[] = new Object[ len ];\r
+                   Object values[] = new Object[ len ];\r
+                       int c = a.getEntries(kb, a.getFirstKey(kb), true, a.getLastKey(kb), true, \r
+                               new ObjectArrayBinding(kb), keys, \r
+                               new ObjectArrayBinding(vb), values, -1);\r
+    \r
+                       assertEquals(len, c);\r
+                       for (int i=0; i<len; i++) {                             \r
+                               assertTrue( kb.equals( oks[i], keys[i] ) );\r
+                               assertTrue( vb.equals( ovs[i], values[i] ) );\r
+                       }\r
+                       \r
+                       // Exclude first and last\r
+                       keys = new Object[ len-2 ];\r
+                       values = new Object[ len-2 ];\r
+                       c = a.getEntries(kb, a.getFirstKey(kb), false, a.getLastKey(kb), false, \r
+                                       new ObjectArrayBinding(kb), keys, \r
+                                       new ObjectArrayBinding(vb), values, -1);\r
+                       assertEquals(len-2, c);\r
+                       for (int i=1; i<len-1; i++) {                           \r
+                               assertTrue( kb.equals( oks[i], keys[i-1] ) );\r
+                               assertTrue( vb.equals( ovs[i], values[i-1] ) );\r
+                       }\r
+       \r
+                       // Get one\r
+                       c = a.getEntries(kb, a.getFirstKey(kb), true, a.getLastKey(kb), false, \r
+                                       new ObjectArrayBinding(kb), keys, \r
+                                       new ObjectArrayBinding(vb), values, 1);\r
+                       assertEquals(1, c);\r
+                       assertTrue( kb.equals( oks[0], keys[0] ) );\r
+                       assertTrue( vb.equals( ovs[0], values[0] ) );\r
+                       }\r
+                       catch (AccessorException e) {\r
+                           assertEquals( "Not implemented", e.getMessage() );\r
+                       }\r
+                       \r
+               }
+               
+               // Create test values
+               TreeSet<Object> keys = new TreeSet<Object>(kb);
+               for (int i=0; i<10; i++) {\r
+                       Object key = null;\r
+                       if (kb.type().equals(Datatypes.VARIANT)) {\r
+                               do {\r
+                                       key = kb.accept(rv);\r
+                               } while( !kb.type().equals(Datatypes.VARIANT) || !isKeyShortEnough(kb, key) );                  \r
+                       } else {\r
+                               key = kb.accept(rv);                            \r
+                       }\r
+                       keys.add( key );\r
+               }
+               int testValueCount = keys.size();
+               Object tks[] = keys.toArray( new Object[testValueCount] );
+               Object tvs[] = new Object[ testValueCount ];
+               for (int i=0; i<testValueCount; i++) tvs[i] = vb.accept(rv);
+
+               if (len>=3) {
+                       // getFirstKey(Binding)
+                       Object fk = a.getFirstKey(kb);
+                       assertTrue( kb.equals(fk, oks[0]) );
+
+                       // getLastKey(Binding)
+                       Object lk = a.getLastKey(kb);\r
+                       assertTrue( kb.equals(lk, oks[len-1]) );
+                       
+                       // getLowerKey(Binding, Object)
+                       Object k = a.getLowerKey(kb, fk);
+                       assertNull(k);                  \r
+                       k = a.getLowerKey(kb, lk);
+                       assertTrue( kb.equals(k, oks[len-2]) );  
+                       
+                       // getFloorKey(Binding, Object)
+                       k = a.getFloorKey(kb, fk);
+                       assertTrue( kb.equals(k, oks[0]) );  
+                       k = a.getFloorKey(kb, lk);
+                       assertTrue( kb.equals(k, oks[len-1]) );  
+                       
+                       // getCeilingKey(Binding, Object)
+                       k = a.getCeilingKey(kb, lk);
+                       assertTrue( kb.equals(k, oks[len-1]) );  
+                       k = a.getCeilingKey(kb, fk);
+                       assertTrue( kb.equals(k, oks[0]) );  
+                       
+                       // getHigherKey(Binding, Object)
+                       k = a.getHigherKey(kb, lk);\r
+                       assertNull(k);                  
+                       k = a.getHigherKey(kb, fk);
+                       assertTrue( kb.equals(k, oks[1]) );  
+               }
+
+               // clear
+               // putAll(Binding, Binding, Object[], Object[])
+               a.clear();
+               int size = a.size();\r
+               assertEquals( 0, size );
+               a.putAll(kb, vb, oks, ovs);\r
+               size = a.size();                \r
+               assertEquals( len, size );
+               
+               // clear
+               // putAll(Binding, Binding, Map<Object, Object>)
+               a.clear();
+               assertEquals( 0, a.size() );
+               a.putAll(kb, vb, om);
+               assertEquals( len, a.size() );
+               
+               // containsKey
+               // containsValue
+               for (int i=0; i<len; i++) {
+                       assertTrue( a.containsKey(kb, oks[i]) );
+                       assertTrue( a.containsValue(vb, ovs[i]) );
+               }
+                               
+               // Add, Remove
+               if ( mutable ) {
+                       // put (insert)
+                       int count = len;
+                       for (int i=0; i<testValueCount; i++) {
+                               if (!a.containsKey(kb, tks[i])) count++;
+                               a.put(kb, tks[i], vb, tvs[i]);
+                       }
+                       assertEquals(count, a.size());
+                       for (int i=0; i<testValueCount; i++) {
+                               Object o = a.get(kb, tks[i], vb);
+                               assertTrue( vb.equals(tvs[i], o) );
+                       }
+                       // remove(Binding, Object)
+                       for (int i=0; i<testValueCount; i++) {
+                               a.remove(kb, tks[i]);
+                       }
+                       for (int i=0; i<len; i++) {
+                               a.remove(kb, oks[i]);
+                       }                       
+                       a.putAll(kb, vb, oks, ovs);
+               }
+               
+               // Set
+               int common = Math.min(len, testValueCount);
+               
+               for (int i=0; i<common; i++) {
+                       a.put(kb, oks[i], vb, tvs[i]);
+                       Object x = a.get(kb, oks[i], vb);
+                       assertTrue( vb.equals( tvs[i], x) );                    
+               }
+               for (int i=0; i<common; i++) {
+                       a.put(kb, oks[i], vb, ovs[i]);
+                       Object x = a.get(kb, oks[i], vb);
+                       assertTrue( vb.equals( ovs[i], x ) );                   
+               }
+               
+               //// Test sub-accessors with recursion
+               for (int i=0; i<len; i++) {
+                       Accessor sa = a.getValueAccessor(kb, oks[i]);
+                       testAccessor(sa);
+               }                       
+       }
+       
+       public void testArrayAccessor(ArrayAccessor a) throws Exception {
+               Datatype type = a.type();
+               ArrayBinding binding = (ArrayBinding) scheme.getBindingUnchecked(type);
+               Binding cb = binding.getComponentBinding();
+               InterestSet is = InterestSet.newInterestSet(type, true, true, false);
+               boolean mutable = !binding.isImmutable();
+
+               int len = a.size();
+               Object ovs[] = new Object[ len ];
+               a.getAll(cb, ovs);
+
+               //// Create test values
+               int testValueCount = rv.getRandom().nextInt(10)+10;
+               Object tvs[] = new Object[ testValueCount ];
+               Object tvs2[] = new Object[ testValueCount ];
+               for (int i=0; i<testValueCount; i++) {
+                       tvs[i] = cb.accept(rv);
+                       tvs2[i] = cb.accept(rv);                                
+               }                       
+               
+               // Add, Remove
+               if ( mutable ) {
+                       
+                       //// Add in values to end
+                       a.addAll(cb, tvs);                      
+                       // Test get 
+                       for (int i=0; i<testValueCount; i++) {
+                               Object o1 = a.get(i + len, cb);
+                               Object o2 = tvs[i];
+                               assertTrue( cb.equals(o1, o2) );
+                       }\r
+                       
+                       // Test get to existing instance\r
+                       {\r
+                               Object o1 = cb.createDefault();\r
+                               for (int i=0; i<testValueCount; i++) {\r
+                                       a.get(i + len, cb, o1);\r
+                                       Object o2 = tvs[i];\r
+                                       assertTrue( cb.equals(o1, o2) );\r
+                               }\r
+                       }\r
+\r
+                       // Test getAll
+                       Object all[] = new Object[ testValueCount + len ];
+                       a.getAll(cb, all);
+                       for (int i=0; i<testValueCount; i++) {
+                               Object o1 = all[i+len];
+                               Object o2 = tvs[i];
+                               assertTrue( cb.equals(o1, o2) );
+                       }                       
+                       a.remove(len, testValueCount);
+                       for (int i=0; i<len; i++) {
+                               Object o1 = ovs[i];
+                               Object o2 = a.get(i, cb);
+                               assertTrue( cb.equals(o1, o2) );
+                       }                                               
+                       assertEquals(len, a.size());
+                       
+                       
+                       //// Add in values to the beginning
+                       a.addAll(0, cb, tvs);
+                       for (int i=0; i<testValueCount; i++) {
+                               Object o1 = a.get(i, cb);
+                               Object o2 = tvs[i];
+                               assertTrue( cb.equals(o1, o2) );
+                       }
+                       // Test getAll
+                       a.getAll(cb, all);
+                       for (int i=0; i<testValueCount; i++) {
+                               Object o1 = all[i];
+                               Object o2 = tvs[i];
+                               assertTrue( cb.equals(o1, o2) );
+                       }                       
+                       a.remove(0, testValueCount);
+                       assertEquals(len, a.size());
+               }\r
+               \r
+               int oldSize = a.size();\r
+               a.setSize( oldSize + 10);\r
+               assertEquals( oldSize+10, a.size() );\r
+               a.setSize( oldSize );
+               assertEquals( oldSize, a.size() );\r
+
+               //// Test setValue()
+               int common = Math.min(len, testValueCount);
+               for (int i=0; i<common; i++) \r
+                       a.set(i, cb, tvs[i]);\r
+               
+               for (int i=0; i<common; i++) assertTrue( cb.equals( tvs[i], a.get(i, cb)) );
+               for (int i=0; i<common; i++) a.set(i, cb, ovs[i]);              
+               for (int i=0; i<common; i++) assertTrue( cb.equals( ovs[i], a.get(i, cb)) );
+               
+               //// Test sub-accessors with recursion
+               for (int i=0; i<len; i++) {
+                       Accessor sa = a.getAccessor(i);
+                       testAccessor(sa);
+               }               
+               
+       }
+       
+       public void testRecordAccessor(RecordAccessor a) throws Exception {
+               Datatype type = a.type();
+               RecordBinding binding = (RecordBinding) scheme.getBindingUnchecked(type);
+               Binding cbs[] = binding.getComponentBindings();
+               InterestSet is = InterestSet.newInterestSet(type, true, true, false);
+
+               int len = a.count();
+               Object ovs[] = new Object[ len ];
+               for (int i=0; i<len; i++) ovs[i] = a.getFieldValue(i, cbs[i]);
+
+               //// Create test values
+               Object tvs[] = new Object[ len ];
+               Object tvs2[] = new Object[ len ];
+               for (int i=0; i<len; i++) {
+                       tvs[i] = cbs[i].accept(rv);
+                       tvs2[i] = cbs[i].accept(rv);                            
+               }                       
+
+               //// Test setValue()\r
+//             System.gc();
+               for (int i=0; i<len; i++) \r
+                       a.setFieldValue(i, cbs[i], tvs[i]);
+               for (int i=0; i<len; i++) assertTrue( cbs[i].equals( tvs[i], a.getFieldValue(i, cbs[i])) );
+               for (int i=0; i<len; i++) a.setFieldValue(i, cbs[i], ovs[i]);           
+               for (int i=0; i<len; i++) assertTrue( cbs[i].equals( ovs[i], a.getFieldValue(i, cbs[i])) );\r
+               
+               //// Test sub-accessors with recursion
+               for (int i=0; i<len; i++) {
+                       Accessor sa = a.getFieldAccessor(i);
+                       testAccessor(sa);
+               }               
+               
+       }
+       
+       public void testOptionalAccessor(OptionalAccessor a) throws Exception {
+               Datatype type = a.type();
+               OptionalBinding binding = (OptionalBinding) scheme.getBindingUnchecked(type);
+               Binding cb = binding.getComponentBinding();
+               InterestSet is = InterestSet.newInterestSet(type, true, true, true);
+               
+               // Remember original value
+               boolean hadValue = a.hasValue();
+               Object ov = hadValue ? a.getComponentValue(cb) : null;          
+               Object tv = cb.accept(rv);
+
+               a.setComponentValue(cb, tv);
+               assertTrue( cb.equals(a.getComponentValue(cb), tv) );
+               assertEquals( true, a.hasValue());
+               
+               a.setNoValue();
+               assertEquals( false, a.hasValue());
+
+               // Test Invalidated Event
+               {                       
+                       a.setNoValue();
+                       a.setComponentValue(cb, tv);
+                       Accessor sa = a.getComponentAccessor();
+                       EventCollection ec = new EventCollection();
+                       a.addListener(ec, is, null, null);
+                       a.setNoValue();         
+                       a.removeListener(ec);
+                       boolean invalidatedEventOk = false;
+                       for (Event e : ec.getAndClearEvents()) {
+                               invalidatedEventOk |= (e instanceof InvalidatedEvent) && (e.reference !=null);
+                       }
+                       sa.getClass();
+                       assertTrue( invalidatedEventOk );                               
+               }
+               
+               // Restore value
+               if (hadValue) {
+                       a.setComponentValue(cb, ov);
+               } else {
+                       a.setNoValue();
+               }
+       }
+       
+       public void testUnionAccessor(UnionAccessor a) throws Exception {
+               UnionType type = a.type();
+               UnionBinding binding = (UnionBinding) scheme.getBindingUnchecked(type);
+               Binding cbs[] = binding.getComponentBindings();
+               InterestSet is = InterestSet.newInterestSet(type, true, true, true);
+               int len = a.count();
+
+               // Get original values
+               int ot = a.getTag();
+               Object ov = a.getComponentValue(cbs[ot]);
+               
+               //// Create test values
+               Object tvs[] = new Object[ len ];
+               Object tvs2[] = new Object[ len ];
+               for (int i=0; i<len; i++) {
+                       tvs[i] = cbs[i].accept(rv);
+                       tvs2[i] = cbs[i].accept(rv);                            
+               }                       
+
+               //// Test setValue()
+               for (int i=0; i<len; i++) {
+                       a.setComponentValue(i, cbs[i], tvs[i]);
+                       assertEquals( i, a.getTag() );
+                       assertTrue( Bindings.equals(cbs[i], a.getComponentValue(cbs[i]), cbs[i], tvs[i]) );
+               }
+               
+               // Test Invalidated Event
+               if (type.getComponentCount()>1)
+               {
+                       int i=0;
+                       a.setComponentValue(i, cbs[i], tvs[i]);
+                       Accessor sa = a.getComponentAccessor();
+                       EventCollection ec = new EventCollection();
+                       a.addListener(ec, is, null, null);
+                       i = 1;
+                       a.setComponentValue(i, cbs[i], tvs[i]);
+                       boolean invalidatedEventOk = false;
+                       List<Event> events = ec.getAndClearEvents();
+                       for (Event e : events) {
+                               invalidatedEventOk |= (e instanceof InvalidatedEvent) && (e.reference !=null);
+                       }
+                       a.removeListener(ec);
+                       sa.getClass();
+                       assertTrue( invalidatedEventOk );                               
+               }
+               
+               a.setComponentValue(ot, cbs[ot], ov);
+               
+               //// Test sub-accessors with recursion
+               Accessor sa = a.getComponentAccessor();
+               testAccessor(sa);
+               
+       }
+       
+       public void testVariantAccessor(VariantAccessor a) throws Exception {
+               Datatype type = a.type();
+               VariantBinding binding = (VariantBinding) scheme.getBindingUnchecked(type);
+               InterestSet is = InterestSet.newInterestSet(type, true, true, true);
+               
+               // Remember the original value 
+               Binding ob = scheme.getBindingUnchecked( type );
+               Object ov = a.getValue(ob);
+               ob.assertInstaceIsValid(ov);
+               boolean mutable = !binding.isImmutable();
+               
+               if (mutable) {
+                       // Create test values
+                       int testValueCount = 10;
+                       Binding tvb[] = new Binding[testValueCount];
+                       Object tvs[] = new Object[testValueCount];
+                       for (int i=0; i<testValueCount; i++) {
+                               tvb[i] = scheme.getBindingUnchecked( rv.randomType(0, 2) );
+                               tvs[i] = tvb[i].accept(rv);
+                       }
+               
+                       // Set & Get
+                       for (int i=0; i<testValueCount; i++) {
+                               a.setContentValue(tvb[i], tvs[i]);                              
+                               Object o = a.getContentValue(tvb[i]);
+                               assertTrue( tvb[i].equals(o, tvs[i]) );
+                       }
+                       
+                       // Test Invalidated Event
+                       {
+                               Binding tb = tvb[1];
+                               Object tv = tvs[1];
+                               Datatype ct = tvb[0].type();
+                               a.setContentValue(tvb[0], tvs[0]);                                      
+                               EventCollection ec = new EventCollection();
+                               a.addListener(ec, is, null, null);
+                               Accessor sa = a.getContentAccessor();
+                               a.setContentValue(tvb[1], tvs[1]);                                      
+                               a.removeListener(ec);
+                               
+                               // Assigned same type
+                               if (ct.getClass().equals(tb.type().getClass())) {
+                                       
+                               } else {                                
+                                       boolean invalidatedEventOk = false;
+                                       List<Event> events = ec.getAndClearEvents();                            
+                                       for (Event e : events) {
+                                               invalidatedEventOk |= (e instanceof InvalidatedEvent) && (e.reference !=null);
+                                       }
+                                       assertTrue( invalidatedEventOk );
+                               }
+                       }                       
+               }
+               
+               //// Test sub-accessors with recursion
+               Accessor sa = a.getContentAccessor();
+               testAccessor(sa);
+               
+               // Restore
+               a.setValue(ob, ov);             
+       }
+       
+       public void testByteAccessor(ByteAccessor a) throws Exception {
+               byte ov = a.getValue();
+               ByteBinding b = Bindings.BYTE;
+               for (int i=0; i<10; i++) {
+                       Byte r = (Byte) rv.visit(b);            
+                       a.setValue(r);
+                       assertTrue( r == a.getValue() );
+               }
+               a.setValue(ov);                 
+       }
+       
+       public void testDoubleAccessor(DoubleAccessor a) throws Exception {
+               double ov = a.getValue();
+               DoubleBinding b = Bindings.DOUBLE;
+               for (int i=0; i<10; i++) {
+                       Double r = (Double) rv.visit(b);                
+                       a.setValue(r);
+                       assertTrue( r == a.getValue() );
+               }
+               a.setValue(ov);                 
+       }
+       
+       public void testFloatAccessor(FloatAccessor a) throws Exception {
+               float ov = a.getValue();
+               FloatBinding b = Bindings.FLOAT;
+               for (int i=0; i<10; i++) {
+                       Float r = (Float) rv.visit(b);          
+                       a.setValue(r);
+                       assertTrue( r == a.getValue() );
+               }
+               a.setValue(ov);                 
+       }
+       
+       public void testIntegerAccessor(IntegerAccessor a) throws Exception {
+               int ov = a.getValue();
+               IntegerBinding b = Bindings.INTEGER;
+               for (int i=0; i<10; i++) {
+                       Integer r = (Integer) rv.visit(b);              
+                       a.setValue(r);
+                       assertTrue( r == a.getValue() );
+               }
+               a.setValue(ov);                 
+       }
+       
+       public void testLongAccessor(LongAccessor a) throws Exception {
+               long ov = a.getValue();
+               LongBinding b = Bindings.LONG;
+               for (int i=0; i<10; i++) {
+                       Long r = (Long) rv.visit(b);            
+                       a.setValue(r);
+                       assertTrue( r == a.getValue() );
+               }
+               a.setValue(ov);                 
+       }
+               
+       public void testStringAccessor(StringAccessor a) throws Exception {
+               String ov = a.getValue();
+               StringBinding b = Bindings.STRING;
+               for (int i=0; i<10; i++) {
+                       String r = (String) rv.visit(b);                
+                       a.setValue(r);
+                       assertEquals( r, a.getValue() );
+               }
+               a.setValue(ov);                 
+       }
+       
+       public void testBooleanAccessor(BooleanAccessor a) throws Exception {
+               Boolean ov = a.getValue();
+               BooleanBinding b = Bindings.BOOLEAN;
+               for (int i=0; i<10; i++) {
+                       Boolean r = (Boolean) rv.visit(b);              
+                       a.setValue(r);
+                       assertEquals( r, a.getValue() );
+               }
+               a.setValue(ov); 
+       }
+               
+       
+}
+