package com.yahoo.dtf.junit; import java.util.Enumeration; import java.util.HashMap; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicInteger; import junit.framework.Assert; import junit.framework.JUnit4TestAdapter; import org.junit.Test; import com.yahoo.dtf.config.Properties; import com.yahoo.dtf.exception.DTFException; /** * Properties needs to be tested throughly because its a custom implementation * of the Java Properties class that tries to be more memory efficient while * giving the right property isolation between parent and children threads. * * @author rlgomes */ public class PropertiesSuite extends DTFJUnitTest { private static int CLONE_DEPTH = 20; /** * Validate that properties are correctly isolated in underlying clones in * a way that does not overwrite the value of the original Properties object * and they always contain the correct value for this specific clone. * * P1 has {{a,a}} * * P2 = P1.clone() * * P1 has {{a,a}} * P2 has {{a,a}} * * P2.set(a,1) * * P1 has {{a,a}} * P2 has {{a,1}} */ @Test public void propertyIsolation() { Properties props = new Properties(); props.put("a","a"); Properties[] clones = new Properties[CLONE_DEPTH+1]; clones[0] = props; for (int i = 1; i < CLONE_DEPTH+1; i++) { clones[i] = (Properties) clones[i-1].clone(); clones[i].put("a","" + i); Assert.assertTrue(props.get("a").equals("a")); Assert.assertTrue(clones[i].get("a").equals("" + i)); for (int j = i; j > 0; j--) { Assert.assertTrue(clones[j].get("a").equals("" + j)); } } } /** * Validate that property inheritance works correctly independently of how * many cloned generations there are. Basically the property defined in the * original Properties object should never be lost. * * P1 has {{a,a},{z,z}} * * P2 = P1.clone() * * P1 has {{a,a},{z,z}} * P2 has {{a,a},{z,z}} * * P2.set(a,1) * * P1 has {{a,a},{z,z}} * P2 has {{a,1},{z,z}} */ @Test(timeout=60000) public void propertyInheritance() { Properties props = new Properties(); props.put("a","a"); props.put("z","z"); Properties[] clones = new Properties[CLONE_DEPTH+1]; clones[0] = props; for (int i = 1; i < CLONE_DEPTH+1; i++) { clones[i] = (Properties) clones[i-1].clone(); clones[i].put("a","" + i); Assert.assertTrue(clones[i].get("z").equals("z")); for (int j = i; j > 0; j--) { Assert.assertTrue(clones[j].get("z").equals("z")); } } } /** * Verify that property resolution works even for properties that have been * moved into the read/write list and that access to that same property is * not done in a wrong manner just because of the cloning. * * P1 has {{a,a}} # but a is now known to be in the r/w properties list * * P2 = P1.clone() * * P1 has {{a,a}} * P2 has {{a,a}} * * P2.set(a,1) * * P1 has {{a,a}} * P2 has {{a,1}} */ @Test(timeout=60000) public void propertyResolution() { Properties props = new Properties(); props.put("a","a"); props.put("a","a"); Properties[] clones = new Properties[CLONE_DEPTH+1]; clones[0] = props; for (int i = 1; i < CLONE_DEPTH+1; i++) { clones[i] = (Properties) clones[i-1].clone(); clones[i].put("a",""+i); Assert.assertTrue(clones[i].get("a").equals(""+i)); for (int j = i; j > 0; j--) { Assert.assertTrue(clones[j].get("a").equals(""+j)); } } } private static double ITERATIONS = 1000000; /** * Put performance test for comparison if anyone tries to make a better * implementation. */ @Test(timeout=60000) public void putPerf() { Properties props = new Properties(); double start = System.currentTimeMillis(); for(long i = 0; i < ITERATIONS; i++) { props.put("key" + i, "value" + i); } double stop = System.currentTimeMillis(); double duration = (stop-start)/1000; double ops_per_s = ITERATIONS/duration; info("puts/sec: " + ops_per_s); } /** * Put performance test for comparison if anyone tries to make a better * implementation. This test differs from the previous because its done * on the cloned Properties object and not the original Properties object. */ @Test(timeout=60000) public void putPerfInCopy() { Properties pprops = new Properties(); Properties props = null; String data = new String(new byte[32]); for(long i = 0; i < 5000; i++) pprops.put("key" + i, data + i); props = (Properties) pprops.clone(); props = (Properties) props.clone(); double start = System.currentTimeMillis(); for(long i = 0; i < ITERATIONS; i++) props.put("key" + i, data + i); double stop = System.currentTimeMillis(); double duration = (stop-start)/1000; double ops_per_s = ITERATIONS/duration; info("puts/sec: " + ops_per_s); } /** * Clone performance test. */ @Test(timeout=60000) public void clonePerf() { Properties props = new Properties(); String data = new String(new byte[32]); for(long i = 0; i < ITERATIONS; i++) { props.put("key" + i, data + i); } double start = System.currentTimeMillis(); for(long i = 0 ; i < ITERATIONS; i++) { props.clone(); } double stop = System.currentTimeMillis(); double duration = (stop-start)/1000; double ops_per_s = ITERATIONS/duration; info("clones/sec: " + ops_per_s); } /** * Validate that removal of a property from a cloned Propertie object does * not remove the same key from the original Properties object. */ @Test(timeout=60000) public void removeTest() { Properties pprops = new Properties(); pprops.put("a", "b"); Properties cprops = (Properties) pprops.clone(); Assert.assertTrue(pprops.containsKey("a")); Assert.assertEquals(pprops.get("a"),"b"); Assert.assertTrue(cprops.containsKey("a")); Assert.assertEquals(cprops.get("a"),"b"); cprops.remove("a"); Assert.assertFalse(cprops.containsKey("a")); Assert.assertEquals(cprops.get("a"),null); } /** * Simple enumeration test to validate that when we print all the * available keys in a cloned Properties object that it doesn't include * repeated keys. * @throws DTFException */ @Test public void enumerationTest() throws DTFException { Properties pprops = new Properties(); pprops.put("a", "b"); pprops.put("c", "c"); pprops.put("a", "a"); Properties cprops = (Properties) pprops.clone(); cprops.put("c", "c"); cprops.put("z", "z"); cprops.put("a", "a"); cprops.put("d", "d"); cprops.put("b", "b"); Enumeration<Object> elements = cprops.keys(); HashMap<String,AtomicInteger> expected_elements = new HashMap<String, AtomicInteger>(); expected_elements.put("a", new AtomicInteger()); expected_elements.put("b", new AtomicInteger()); expected_elements.put("c", new AtomicInteger()); expected_elements.put("z", new AtomicInteger()); expected_elements.put("d", new AtomicInteger()); while ( elements.hasMoreElements() ) { String elem = elements.nextElement().toString(); expected_elements.get(elem).incrementAndGet(); } for (Entry<String, AtomicInteger> entry : expected_elements.entrySet()) { if ( entry.getValue().intValue() != 1 ) { Assert.fail(entry.getKey() + " => " + entry.getValue() + ", expected 1"); } } } public static junit.framework.Test suite() { return new JUnit4TestAdapter(PropertiesSuite.class); } }