package xapi.dev.source; import java.util.regex.Pattern; import org.junit.Assert; import org.junit.Test; import xapi.source.write.Template; public class TemplateTest { @Test public void testSimpleTemplate() { Template generator = new Template("(<>[])$.toArray()", "<>", "$"); String result = generator.apply("String", "this.value"); Assert.assertEquals(result, "(String[])this.value.toArray()"); } @Test public void testComplexTemplate() { Template generator = new Template( "Hello $2!\n" + "$1.$3($4, $5);\n" + "$4.$1($2, $3, $1, $5);" , "$1", "$2", "$3", "$4", "$5"); String result = generator.apply( "hello", "world", "three", "four", "5"); Assert.assertEquals(result, "Hello world!\n" + "hello.three(four, 5);\n" + "four.hello(world, three, hello, 5);" ); } @Test public void testRandomTemplates() { if (Boolean.getBoolean("xapi.benchmark")) for (int i = 0; i<14;i++){ testRandomTemplates((int)Math.pow(2, i)); } else testRandomTemplates(1000); } private void testRandomTemplates(final int iters) { int loops = 50; int numItems = 32; final String[] templates = new String[loops]; final Object[] manualCases = new Object[loops]; final Object[] templateCases = new Object[loops]; // Prepare the cases before running them, so we get accurate timing. while (loops --> 0) { final int loop = loops; numItems = 8 +(numItems%23) % 28; final String[] replacers = new String[numItems]; final String[] values = new String[numItems]; while (numItems --> 0) { char c = Character.forDigit(numItems, 36); values[numItems] = "_"+c+"_"; replacers[numItems] = String.valueOf(c); } templates[loop] = randomChars(16 + (int)(Math.random()*128)); templateCases[loop] = new Runnable() { @Override public void run() { int iterations = iters; Template t = new Template(templates[loop], replacers); String templateResult=null; while (iterations --> 0) { templateResult = t.apply((Object[])values); } templateCases[loop] = templateResult; } }; manualCases[loop] = new Runnable() { @Override public void run() { int iterations = iters; // Our control case of performing runtime string operations // is naive enough that we can actually use regex patterns // (which does speed up it's performance to only 1.5-3x slower). // The Template class does not require regex syntax compatibility. Pattern[] patterns = new Pattern[values.length]; for (int i = values.length; i --> 0; ) { patterns[i] = Pattern.compile(replacers[i]); } while (iterations --> 0) { String manualResult = templates[loop]; for (int i = 0; i < values.length; i++) { manualResult = patterns[i].matcher(manualResult).replaceAll(values[i]); } manualCases[loop] = manualResult; } } }; } // Run the cases. System.gc(); long start = System.nanoTime(); for (Object o : manualCases) ((Runnable)o).run(); float manualTime = (System.nanoTime() - start); start = System.nanoTime(); for (Object o : templateCases) ((Runnable)o).run(); float templateTime = (System.nanoTime() - start); System.out.println("Template is "+(float)(manualTime/templateTime)+ " faster than runtime " + "string operations for " +iters+" iterations."); for (int i = templateCases.length; i --> 0 ;) { Assert.assertEquals( "Original: "+templates[i]+"\n"+ "Manual: "+manualCases[i]+"\n"+ "Template: "+templateCases[i]+"\n", (String)manualCases[i], (String)templateCases[i]); } } private String randomChars(int numChars) { StringBuilder b = new StringBuilder(); for (int i = 0, m = (int)(Math.random()*numChars); i < m; i++ ) { b.append(Character.forDigit((int)(Math.random()*36), 36)); } return b.toString(); } }