/* * Copyright (c) 2011-2014 Jeppetto and Jonathan Thompson * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.iternine.jeppetto.enhance; import org.junit.Before; import org.junit.Test; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; public class EnhancerTest { public static void main(String[] args) throws Exception { for (int i = 0; i < 5; i++) { doMain(); System.gc(); //Thread.sleep(2500L); //System.gc(); } System.out.println("OK"); Thread.sleep(25000000L); } public static void doMain() throws IllegalAccessException, InstantiationException { int n = 1000000; Set<SampleClass> test = new HashSet<SampleClass>(); long before = System.nanoTime(); for (int i = 0; i < n; i++) { test.add(new SampleClass()); } System.out.format("%,dms to create %,d with constructor.%n", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - before), test.size()); test.clear(); Enhancer<SampleClass> e = EnhancerHelper.makePersistentEnhancer(SampleClass.class); test.add(e.enhance(new SampleClass())); n -= 1; before = System.nanoTime(); for (int i = 0; i < n; i++) { test.add(e.enhance(new SampleClass())); } System.out.format("%,dms to wrap %,d with enhancer.%n", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - before), test.size()); test.clear(); n += 1; Class<? extends SampleClass> eclass = e.enhance(new SampleClass()).getClass(); before = System.nanoTime(); for (int i = 0; i < n; i++) { test.add(eclass.newInstance()); } System.out.format("%,dms to create new %,d with enhanced class constructor.%n", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - before), test.size()); test.clear(); before = System.nanoTime(); for (int i = 0; i < n; i++) { test.add(e.newInstance()); } System.out.format("%,dms to create new %,d with enhancer.%n", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - before), test.size()); before = System.nanoTime(); Persistent p = (Persistent) test.iterator().next(); boolean b = false; for (int i = 0; i < n; i++) { b |= p.isDirty(); } System.out.format("%,dms to call isDirty %,d times (result: %b).%n", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - before), test.size(), b); } private Enhancer<SampleClass> enhancer; // ok to create multiple per test, should support that private Enhancer<SampleClass2> enhancer2; // ok to create multiple per test, should support that private Enhancer<SampleClassThatImplementsSnapshot> noop; @Before public void setup() { enhancer = EnhancerHelper.makePersistentEnhancer(SampleClass.class); enhancer2 = EnhancerHelper.makePersistentEnhancer(SampleClass2.class); noop = EnhancerHelper.makePersistentEnhancer(SampleClassThatImplementsSnapshot.class); } @Test @org.junit.Ignore public void doOnePerfRunForFunInTests() throws InstantiationException, IllegalAccessException { doMain(); } @Test(expected = IllegalArgumentException.class) public void cannotCreateEnhancerForInterface() { EnhancerHelper.makePersistentEnhancer(Comparable.class); } @Test public void noopPassesBackObject() { SampleClassThatImplementsSnapshot o = new SampleClassThatImplementsSnapshot(); assertSame(o, noop.enhance(o)); assertSame(o.getClass(), noop.getEnhancedClass()); } @Test public void implementsGetsDirty() { SampleClass enhanced = enhancer.enhance(new SampleClass()); assertNotNull(enhanced); assertTrue(enhanced instanceof Persistent); } @Test public void enhancedClassIsNotSame() { assertNotSame(SampleClass.class, enhancer.getEnhancedClass()); } @Test public void newInstanceIsDirtyWorks() { SampleClass enhanced = enhancer.newInstance(); assertDirty(enhanced, true); ((Snapshot) enhanced).snapshot(); assertDirty(enhanced, false); enhanced.setFoo("foo"); assertDirty(enhanced, true); ((Snapshot) enhanced).snapshot(); assertDirty(enhanced, false); enhanced.setFoo(null); assertDirty(enhanced, true); enhanced.setFoo("bar"); assertDirty(enhanced, true); enhanced.setFoo("foo"); assertDirty(enhanced, false); } @Test public void dirtyDetectionWorksInsideArrays() { SampleClass enhanced = enhancer.newInstance(); ((Snapshot) enhanced).snapshot(); enhanced.setBits(new boolean[] { false, true }); assertDirty(enhanced, true); enhanced.setBits(new SampleClass().getBits()); assertDirty(enhanced, false); } @Test public void enhancedIsDirtyWorks() { SampleClass testObject = new SampleClass(); testObject.setFoo("bar"); SampleClass enhanced = enhancer.enhance(testObject); ((Snapshot) enhanced).snapshot(); assertDirty(enhanced, false); assertEquals("bar", enhanced.getFoo()); enhanced.setFoo(null); assertNull(enhanced.getFoo()); assertDirty(enhanced, true); ((Snapshot) enhanced).snapshot(); assertDirty(enhanced, false); } @Test public void enhancedAnonymousSubclassWorks() { SampleClass testObject = new SampleClass() {{ setFoo("bar"); }}; SampleClass enhanced = enhancer.enhance(testObject); ((Snapshot) enhanced).snapshot(); assertDirty(enhanced, false); assertEquals("bar", enhanced.getFoo()); enhanced.setFoo(null); assertNull(enhanced.getFoo()); assertDirty(enhanced, true); ((Snapshot) enhanced).snapshot(); assertDirty(enhanced, false); } @Test public void enhancedSubclassIsDirtyWorks() { SampleClass2 testObject2 = new SampleClass2(); testObject2.setFoo(toString()); long bar = System.nanoTime(); testObject2.setBar(bar); assertEquals(toString(), testObject2.getFoo()); assertEquals(bar, testObject2.getBar()); SampleClass2 enhanced = enhancer2.enhance(testObject2); ((Snapshot) enhanced).snapshot(); assertNotSame(testObject2, enhanced); assertNotNull(enhanced); assertDirty(enhanced, false); assertEquals(toString(), enhanced.getFoo()); assertEquals(bar, enhanced.getBar()); enhanced.setFoo(null); assertNull(enhanced.getFoo()); assertDirty(enhanced, true); enhanced.setFoo(toString()); assertDirty(enhanced, false); enhanced.setBar(0L); assertEquals(0L, enhanced.getBar()); assertDirty(enhanced, true); enhanced.setFoo("something else"); ((Snapshot) enhanced).snapshot(); // was dirty on two counts, one from super, one from sub assertDirty(enhanced, false); } @Test public void detectsMutationOfOriginal() { SampleClass testObject = new SampleClass(); testObject.setFoo("bar"); SampleClass enhanced = enhancer.enhance(testObject); ((Snapshot) enhanced).snapshot(); assertDirty(enhanced, false); assertEquals("bar", enhanced.getFoo()); testObject.setFoo(null); assertNull(enhanced.getFoo()); assertDirty(enhanced, true); ((Snapshot) enhanced).snapshot(); assertDirty(enhanced, false); testObject.setNumberObject(1234L); assertDirty(enhanced, true); assertEquals(1234L, enhanced.getNumberObject().longValue()); ((Snapshot) enhanced).snapshot(); assertDirty(enhanced, false); } @Test public void enhanceClassThatDoesntNeedIt() { SampleClass sampleClass = new SampleClassThatImplementsSnapshot(); SampleClass enhanced = enhancer.enhance(sampleClass); assertNotNull(enhanced); assertTrue(enhanced instanceof Persistent); assertTrue(enhanced instanceof Snapshot); assertSame(sampleClass, enhanced); } @Test public void ifYouEnhancedNullYouGetNull() { assertNull(enhancer.enhance(null)); } @Test public void enhancerAppearsThreadsafe() throws InterruptedException { int iter = 5000; int threads = 10; ExecutorService exec = Executors.newFixedThreadPool(threads); final CountDownLatch start = new CountDownLatch(1); final CountDownLatch done = new CountDownLatch(threads * iter); final List<SampleClass2> newObjects = Collections.synchronizedList(new ArrayList<SampleClass2>()); final List<Enhancer<SampleClass2>> enhancers = new ArrayList<Enhancer<SampleClass2>>(); enhancers.add(EnhancerHelper.makePersistentEnhancer(SampleClass2.class)); enhancers.add(EnhancerHelper.makePersistentEnhancer(SampleClass2.class)); enhancers.add(EnhancerHelper.makePersistentEnhancer(SampleClass2.class)); enhancers.add(EnhancerHelper.makePersistentEnhancer(SampleClass2.class)); enhancers.add(EnhancerHelper.makePersistentEnhancer(SampleClass2.class)); enhancers.add(EnhancerHelper.makePersistentEnhancer(SampleClass2.class)); enhancers.add(EnhancerHelper.makePersistentEnhancer(SampleClass2.class)); enhancers.add(EnhancerHelper.makePersistentEnhancer(SampleClass2.class)); for (int i = 0; i < threads * iter; i++) { exec.submit(new Runnable() { public void run() { try { Enhancer<SampleClass2> enhancer = enhancers.get(newObjects.size() % enhancers.size()); SampleClass2 test = enhancer.newInstance(); ((Snapshot) test).snapshot(); start.await(); long bar = System.nanoTime(); assertDirty(test, false); test.setBar(bar); assertEquals(bar, test.getBar()); assertDirty(test, true); newObjects.add(test); } catch (Exception e) { e.printStackTrace(); // failure will be caught by not enough objects in collection at end } finally { done.countDown(); } } }); } start.countDown(); done.await(); assertEquals("Bug in test", 0, exec.shutdownNow().size()); assertEquals(threads * iter, newObjects.size()); } private void assertDirty(Object obj, boolean isDirty) { assertTrue(obj instanceof Persistent); assertEquals(isDirty, ((Persistent) obj).isDirty()); } }