/******************************************************************************* * Copyright (c) 2010 Association for Decentralized Information Management in * Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.databoard.tests; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import gnu.trove.map.hash.TObjectIntHashMap; import java.io.File; import java.net.InetAddress; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; import java.util.TreeMap; import java.util.TreeSet; import org.junit.Before; import org.junit.Test; import org.simantics.databoard.Accessors; import org.simantics.databoard.Bindings; import org.simantics.databoard.Datatypes; import org.simantics.databoard.accessor.Accessor; import org.simantics.databoard.accessor.ArrayAccessor; import org.simantics.databoard.accessor.BooleanAccessor; import org.simantics.databoard.accessor.ByteAccessor; import org.simantics.databoard.accessor.DoubleAccessor; import org.simantics.databoard.accessor.FloatAccessor; import org.simantics.databoard.accessor.IntegerAccessor; import org.simantics.databoard.accessor.LongAccessor; import org.simantics.databoard.accessor.MapAccessor; import org.simantics.databoard.accessor.OptionalAccessor; import org.simantics.databoard.accessor.RecordAccessor; import org.simantics.databoard.accessor.StringAccessor; import org.simantics.databoard.accessor.UnionAccessor; import org.simantics.databoard.accessor.VariantAccessor; import org.simantics.databoard.accessor.error.AccessorException; import org.simantics.databoard.accessor.event.Event; import org.simantics.databoard.accessor.event.InvalidatedEvent; import org.simantics.databoard.accessor.file.FileVariantAccessor; import org.simantics.databoard.accessor.impl.ChangeSet; import org.simantics.databoard.accessor.impl.CompositeRecord; import org.simantics.databoard.accessor.impl.DirectoryMap; import org.simantics.databoard.accessor.impl.EventCollection; import org.simantics.databoard.accessor.interestset.InterestSet; import org.simantics.databoard.accessor.wire.WireClient; import org.simantics.databoard.accessor.wire.WireServer; import org.simantics.databoard.adapter.AdaptException; import org.simantics.databoard.binding.ArrayBinding; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.BooleanBinding; import org.simantics.databoard.binding.ByteBinding; import org.simantics.databoard.binding.DoubleBinding; import org.simantics.databoard.binding.FloatBinding; import org.simantics.databoard.binding.IntegerBinding; import org.simantics.databoard.binding.LongBinding; import org.simantics.databoard.binding.MapBinding; import org.simantics.databoard.binding.OptionalBinding; import org.simantics.databoard.binding.RecordBinding; import org.simantics.databoard.binding.StringBinding; import org.simantics.databoard.binding.UnionBinding; import org.simantics.databoard.binding.VariantBinding; import org.simantics.databoard.binding.factory.BindingScheme; import org.simantics.databoard.binding.factory.MutableBindingFactory; import org.simantics.databoard.binding.impl.ObjectArrayBinding; import org.simantics.databoard.binding.mutable.MutableVariant; import org.simantics.databoard.binding.util.RandomValue; import org.simantics.databoard.method.Client; import org.simantics.databoard.method.Server; import org.simantics.databoard.serialization.Serializer; import org.simantics.databoard.type.Datatype; import org.simantics.databoard.type.UnionType; import org.simantics.databoard.util.binary.BinaryMemory; /** * 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 */ public class TestAccessor { RandomValue rv; Map repository; BindingScheme scheme; ///////////////////////////////////// public @Before void init() { rv = new RandomValue(); repository = new HashMap(); scheme = new MutableBindingFactory( repository ); } public static File createTmpDir() { String tmp = System.getenv("tmp"); if (tmp==null) tmp = "c:/temp"; Random r = new Random(); String randomName = "tmp-"+(r.nextInt(10000)+10000); File tmpDir = new File(tmp+"/"+randomName); Boolean ok = tmpDir.mkdirs(); assertTrue( ok ); return tmpDir; } public boolean isKeyShortEnough(Binding binding, Object value) throws AdaptException { String key = (String) Bindings.adapt(value, binding, Bindings.STR_VARIANT); return key.length()<=200; } public @Test void testWireAccessor() throws Exception { System.out.println("Wire accessor test"); // TODO: Fix this! if (true) return; for (int i=0; i<10000; i++) { Bindings.defaultBindingRepository.clear(); Bindings.bindingRepository.clear(); Bindings.serializerRepository.clear(); repository.clear(); System.out.println(i+": "); rv = new RandomValue(i); rv.refereableRecords = false; Datatype type = rv.randomType(0, 3); Binding binding = scheme.getBindingUnchecked(type); Object instance = binding.accept(rv); // System.out.println(binding.printValue(instance, true)); Accessor accessor = Accessors.getAccessor(binding, instance); WireServer wireServer = new WireServer(accessor); Server server = new Server(0, wireServer.getMethodInterface()); WireClient wireClient = new WireClient(); Client client = new Client(InetAddress.getByName("localhost"), server.getPort(), wireClient.getClientMethodInterface()); wireClient.setServerMethodInterface(client.getConnection().getRemoteMethodInterface()); Accessor remoteAccessor = (Accessor) wireClient.getAccessor(null); testAccessor( remoteAccessor, false ); wireClient.close(); client.close(); server.close(); } } public @Test void testBinaryAccessor() throws Exception { System.out.println("Test Binary Memory:"); Datatype type = Datatypes.VARIANT; Binding binding = scheme.getBindingUnchecked( type ); Serializer s = binding.serializer(); TObjectIntHashMap identities = new TObjectIntHashMap(); for (int i=0; i<10000; i++) { Bindings.defaultBindingRepository.clear(); Bindings.bindingRepository.clear(); Bindings.serializerRepository.clear(); repository.clear(); rv = new RandomValue(i); rv.getRandom().nextLong(); rv.refereableRecords = false; System.out.println(i+": "); BinaryMemory ram = new BinaryMemory(0); Object instance = binding.accept(rv); binding.assertInstaceIsValid(instance); // System.out.println(instance); identities.clear(); int size = s.getSize(instance, identities); ram.setLength(size); ram.position(0); identities.clear(); s.serialize(ram, identities, instance); identities.clear(); ram.position(0L); Object instance2 = s.deserialize(ram); assertTrue( binding.equals(instance, instance2) ); binding.assertInstaceIsValid(instance2); VariantAccessor a = (VariantAccessor) Accessors.getAccessor(ram, type); testAccessor(a); } } public @Test void testJavaAccessor() throws Exception { System.out.println("Test Java Objects:"); for (int i=0; i<10000; i++) { Bindings.defaultBindingRepository.clear(); Bindings.bindingRepository.clear(); Bindings.serializerRepository.clear(); repository.clear(); System.out.println(i+": "); rv = new RandomValue(i); rv.refereableRecords = false; Datatype type = rv.randomType(0, 3); Binding binding = scheme.getBindingUnchecked(type); Object instance = binding.accept(rv); // System.out.println(binding.printValue(instance, true)); Accessor accessor = Accessors.getAccessor(binding, instance); testAccessor(accessor); } } /** * This test tests composite record by adding 10 fields, and running the * composition as a record accessor */ public @Test void testCompositeRecord() throws Exception { System.out.println("Test composite accessor"); for (int i=0; i<100; i++) { CompositeRecord record = new CompositeRecord(); for (int j=1; j<=10; j++) { Binding binding = Bindings.MUTABLE_VARIANT; rv = new RandomValue(i*543+j*23); rv.getRandom().nextLong(); rv.refereableRecords = false; MutableVariant instance = (MutableVariant) binding.accept(rv); String fieldName = "Field"+j; Accessor fa = Accessors.getAccessor(instance.getBinding(), instance.getValue()); record.addField(fieldName, fa); } System.out.println(i); testAccessor( record ); } } public @Test void testFolderMap() throws Exception { // 1000 tests proves failure for (int i=0; i<100; i++) { Bindings.defaultBindingRepository.clear(); Bindings.bindingRepository.clear(); Bindings.serializerRepository.clear(); repository.clear(); System.out.println(i+": "); File dir = createTmpDir(); DirectoryMap map = Accessors.openDirectory(dir); Binding keyBinding = Bindings.STR_VARIANT; try { System.out.println("Test Folder Map: "+dir); // Create 10 files for (int j=0; j<10; j++) { rv = new RandomValue(i*231231243+j*213); rv.refereableRecords = false; // Create Key String key = ""; do { key = (String) keyBinding.accept(rv); } while (key.length() > 240); // Create value Datatype valueType = rv.randomType(0, 2); Binding valueBinding = scheme.getBindingUnchecked(valueType); Object value = valueBinding.accept(rv); System.out.println(key); MutableVariant vv = new MutableVariant(valueBinding, value); map.put(Bindings.STR_VARIANT, key, Bindings.MUTABLE_VARIANT, vv); } // Test the map testAccessor(map); } finally { // Remove all files in the folder map.clear(); map.close(); System.out.println( dir.listFiles().length + " files."); dir.delete(); } } } 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(); Bindings.serializerRepository.clear(); Bindings.defaultBindingRepository.clear(); repository.clear(); 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(); } ///////////////////////////////////// public void testAccessor(Accessor a) throws Exception { testAccessor(a, true); } 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); // Test Accessor#getValue(binding, instance); Binding mutableBinding = Bindings.getMutableBinding(type); Object mutableInstance = mutableBinding.createDefault(); a.getValue(mutableBinding, mutableInstance); assertTrue( Bindings.equals(binding, origValue, mutableBinding, mutableInstance) ); 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 rollback = new LinkedList(); List 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); 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); assertTrue( binding.equals( x, origValue) ); if (runTypeSpecific) { // 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); assertTrue( binding.equals( x, origValue) ); } } 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); // getAll(Binding, Binding, Map) TreeMap om = new TreeMap(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; i2) { // Get all try { Object keys[] = new Object[ len ]; Object values[] = new Object[ len ]; int c = a.getEntries(kb, a.getFirstKey(kb), true, a.getLastKey(kb), true, new ObjectArrayBinding(kb), keys, new ObjectArrayBinding(vb), values, -1); assertEquals(len, c); for (int i=0; i keys = new TreeSet(kb); for (int i=0; i<10; i++) { Object key = null; if (kb.type().equals(Datatypes.VARIANT)) { do { key = kb.accept(rv); } while( !kb.type().equals(Datatypes.VARIANT) || !isKeyShortEnough(kb, key) ); } else { key = kb.accept(rv); } keys.add( key ); } int testValueCount = keys.size(); Object tks[] = keys.toArray( new Object[testValueCount] ); Object tvs[] = new Object[ testValueCount ]; for (int i=0; i=3) { // getFirstKey(Binding) Object fk = a.getFirstKey(kb); assertTrue( kb.equals(fk, oks[0]) ); // getLastKey(Binding) Object lk = a.getLastKey(kb); assertTrue( kb.equals(lk, oks[len-1]) ); // getLowerKey(Binding, Object) Object k = a.getLowerKey(kb, fk); assertNull(k); 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); 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(); assertEquals( 0, size ); a.putAll(kb, vb, oks, ovs); size = a.size(); assertEquals( len, size ); // clear // putAll(Binding, Binding, Map) a.clear(); assertEquals( 0, a.size() ); a.putAll(kb, vb, om); assertEquals( len, a.size() ); // containsKey // containsValue for (int i=0; i1) { 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 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 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); } }