package org.intracetest.agent; import static org.easymock.EasyMock.isA; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import junit.framework.TestCase; import org.easymock.IAnswer; import org.easymock.EasyMock; import org.intrace.output.AgentHelper; import org.intrace.output.IInstrumentationHandler; import org.intrace.shared.AgentConfigConstants; import org.intrace.shared.TraceConfigConstants; /** * This test tests the Agent. To run this test you must run the test with this * JVM argument: "-javaagent:built/traceagent_test.jar=" */ public class AgentTest extends TestCase { private Receiver receiver; private Sender sender; private Socket socket; @Override protected void setUp() throws Exception { super.setUp(); deleteOldClassFiles(); AgentHelper.setInstrumentationHandler(null); // Wait for agent to startup Thread.sleep(500); connectToAgent(); testSetting(AgentConfigConstants.INSTRU_ENABLED, "false"); } private void deleteOldClassFiles() { File genbin = new File("./genbin/"); File[] files = genbin.listFiles(); if (files != null) { for (int i = 0; i < files.length; i++) { if (files[i].isDirectory()) { deleteDirectory(files[i]); } else { files[i].delete(); } } } } public static boolean deleteDirectory(File path) { if (path.exists()) { File[] files = path.listFiles(); for (int i = 0; i < files.length; i++) { if (files[i].isDirectory()) { deleteDirectory(files[i]); } else { files[i].delete(); } } } return (path.delete()); } public void testGetSettings() throws Exception { sender.sendMessage("getsettings"); Object settingsResponse = receiver.incomingMessages.take(); assertNotNull(settingsResponse); assertTrue(settingsResponse instanceof Map<?, ?>); } public void testHelp() throws Exception { sender.sendMessage("help"); Object helpResponse = receiver.incomingMessages.take(); assertNotNull(helpResponse); assertTrue(helpResponse instanceof Set<?>); Set<String> expectedHelpResponse = new HashSet<String>(); expectedHelpResponse.addAll(AgentConfigConstants.COMMANDS); expectedHelpResponse.addAll(TraceConfigConstants.COMMANDS); assertEquals(helpResponse, expectedHelpResponse); } public void testSettings() throws Exception { // Boolean settings testSetting(AgentConfigConstants.INSTRU_ENABLED, "true"); testSetting(AgentConfigConstants.INSTRU_ENABLED, "false"); testSetting(AgentConfigConstants.SAVE_TRACED_CLASSFILES, "true"); testSetting(AgentConfigConstants.SAVE_TRACED_CLASSFILES, "false"); testSetting(AgentConfigConstants.VERBOSE_MODE, "true"); testSetting(AgentConfigConstants.VERBOSE_MODE, "false"); // Regex (Test in verbose mode to exercise the verbose code testSetting(AgentConfigConstants.CLASS_REGEX, "foo.*"); testSetting(AgentConfigConstants.INSTRU_ENABLED, "true"); testSetting(AgentConfigConstants.VERBOSE_MODE, "true"); testSetting(AgentConfigConstants.CLASS_REGEX, ".*"); //Need to learn more about why these don't work here. --ETO 12/1/2013 //testSetting(TraceConfigConstants.BRANCH, "true"); //testSetting(TraceConfigConstants.BRANCH, "false"); } private BlockingQueue<String> capturedTrace = null; public void testBranchPatterns() throws Throwable { // Create and init the mock IInstrumentationHandler testHandler = EasyMock .createMock(IInstrumentationHandler.class); EasyMock.expect(testHandler.getResponse(isA(String.class))).andReturn(null) .anyTimes(); EasyMock.expect(testHandler.getSettingsMap()) .andReturn(new HashMap<String, String>()).anyTimes(); // Capture objects //final BlockingQueue<String> capturedTrace = new LinkedBlockingQueue<String>(); capturedTrace = new LinkedBlockingQueue<String>(); IAnswer<Object> entryTraceWriter = new IAnswer<Object>() { @Override public Object answer() throws Throwable { Object[] args = EasyMock.getCurrentArguments(); capturedTrace.add("Enter:##:" + Arrays.toString(args)); return null; } }; IAnswer<Object> branchTraceWriter = new IAnswer<Object>() { @Override public Object answer() throws Throwable { Object[] args = EasyMock.getCurrentArguments(); capturedTrace.add("Branch:##:" + Arrays.toString(args)); return null; } }; IAnswer<Object> caughtTraceWriter = new IAnswer<Object>() { @Override public Object answer() throws Throwable { Object[] args = EasyMock.getCurrentArguments(); capturedTrace.add("Throwable:##:" + Arrays.toString(args)); return null; } }; IAnswer<Object> exitTraceWriter = new IAnswer<Object>() { @Override public Object answer() throws Throwable { Object[] args = EasyMock.getCurrentArguments(); capturedTrace.add("Exit:##:" + Arrays.toString(args)); return null; } }; // Expect trace calls testHandler.enter(isA(String.class), isA(String.class), EasyMock.anyInt()); EasyMock.expectLastCall().andAnswer(entryTraceWriter).anyTimes(); testHandler.branch(isA(String.class), isA(String.class), EasyMock.anyInt()); EasyMock.expectLastCall().andAnswer(branchTraceWriter).anyTimes(); testHandler.exit(isA(String.class), isA(String.class), EasyMock.anyInt()); EasyMock.expectLastCall().andAnswer(exitTraceWriter).anyTimes(); testHandler.val(isA(String.class), isA(String.class), isA(String.class), EasyMock.anyInt(), isA(Throwable.class)); EasyMock.expectLastCall().andAnswer(caughtTraceWriter).anyTimes(); testHandler.val(isA(String.class), isA(String.class), isA(String.class), EasyMock.anyBoolean()); EasyMock.expectLastCall().anyTimes(); testHandler.val(isA(String.class), isA(String.class), isA(String.class), EasyMock.anyObject()); EasyMock.expectLastCall().anyTimes(); testHandler.val(isA(String.class), isA(String.class), isA(String.class), EasyMock.anyInt()); EasyMock.expectLastCall().anyTimes(); EasyMock.replay(testHandler); AgentHelper.setInstrumentationHandler(testHandler); // Setup agent testSetting(AgentConfigConstants.INSTRU_ENABLED, "false"); testSetting(AgentConfigConstants.CLASS_REGEX, "org.intracetest.agent.BranchPatterns"); testSetting(AgentConfigConstants.VERBOSE_MODE, "true"); testSetting(AgentConfigConstants.SAVE_TRACED_CLASSFILES, "true"); testSetting(AgentConfigConstants.INSTRU_ENABLED, "true"); // Run Patterns thread BranchPatterns branchPatterns = new BranchPatterns(); Thread patternThread = new Thread(branchPatterns); patternThread.start(); patternThread.join(5 * 1000); if (branchPatterns.th != null) { throw branchPatterns.th; } // Verify that we got all of the expected calls EasyMock.verify(testHandler); // Parse the trace Map<String, TraceData> parsedTraceData = new HashMap<String, TraceData>(); String traceLine = capturedTrace.poll(); while (traceLine != null) { parseLine(parsedTraceData, traceLine); traceLine = capturedTrace.poll(); } assertEquals(12,parsedTraceData.size()); // Verify the trace assertNotNull(parsedTraceData.get("switchblock")); { TraceData trData = parsedTraceData.get("switchblock"); assertTrue(trData.seenEnter); assertTrue(trData.seenExit); assertEquals(4, trData.branchLines.size()); } assertNotNull(parsedTraceData.get("ternary")); { TraceData trData = parsedTraceData.get("ternary"); assertTrue(trData.seenEnter); assertTrue(trData.seenExit); assertEquals(0, trData.branchLines.size()); } assertNotNull(parsedTraceData.get("trycatchfinally")); { TraceData trData = parsedTraceData.get("trycatchfinally"); assertTrue(trData.seenEnter); assertTrue(trData.seenExit); assertEquals(0, trData.branchLines.size()); assertEquals(1, trData.caughtLines.size()); } assertNotNull(parsedTraceData.get("whileloop")); { TraceData trData = parsedTraceData.get("whileloop"); assertTrue(trData.seenEnter); assertTrue(trData.seenExit); // Number depends on code generated by JVM assertTrue((2 == trData.branchLines.size()) || (5 == trData.branchLines.size())); } assertNotNull(parsedTraceData.get("forloop")); { TraceData trData = parsedTraceData.get("forloop"); assertTrue(trData.seenEnter); assertTrue(trData.seenExit); // Number depends on code generated by JVM assertTrue((2 == trData.branchLines.size()) || (3 == trData.branchLines.size())); } assertNotNull(parsedTraceData.get("singlelineif")); { TraceData trData = parsedTraceData.get("singlelineif"); assertTrue(trData.seenEnter); assertTrue(trData.seenExit); assertEquals(1, trData.branchLines.size()); } assertNotNull(parsedTraceData.get("ifthenelse")); { TraceData trData = parsedTraceData.get("ifthenelse"); assertTrue(trData.seenEnter); assertTrue(trData.seenExit); assertEquals(2, trData.branchLines.size()); } assertNotNull(parsedTraceData.get("ifthenelseifelse")); { TraceData trData = parsedTraceData.get("ifthenelseifelse"); assertTrue(trData.seenEnter); assertTrue(trData.seenExit); assertEquals(5, trData.branchLines.size()); } assertNotNull(parsedTraceData.get("dowhile")); { TraceData trData = parsedTraceData.get("dowhile"); assertTrue(trData.seenEnter); assertTrue(trData.seenExit); assertEquals(2, trData.branchLines.size()); } } public void testArgumentTypes() throws Throwable { // Create and init the mock final BlockingQueue<String> capturedTrace = new LinkedBlockingQueue<String>(); IInstrumentationHandler testHandler = new ArgCapture(capturedTrace); AgentHelper.setInstrumentationHandler(testHandler); // Setup agent testSetting(AgentConfigConstants.INSTRU_ENABLED, "false"); testSetting(AgentConfigConstants.CLASS_REGEX, "org.intracetest.agent.ArgumentTypes"); testSetting(AgentConfigConstants.VERBOSE_MODE, "false"); testSetting(AgentConfigConstants.SAVE_TRACED_CLASSFILES, "true"); testSetting(AgentConfigConstants.INSTRU_ENABLED, "true"); // Run Patterns thread ArgumentTypes argTypes = new ArgumentTypes(); Thread patternThread = new Thread(argTypes); patternThread.start(); patternThread.join(5 * 1000); if (argTypes.th != null) { throw argTypes.th; } // Parse the trace Map<String, TraceData> parsedTraceData = new HashMap<String, TraceData>(); String traceLine = capturedTrace.poll(); while (traceLine != null) { parseLine(parsedTraceData, traceLine); traceLine = capturedTrace.poll(); } // Verify the trace assertNotNull(parsedTraceData.get("byteArg")); { TraceData trData = parsedTraceData.get("byteArg"); assertEquals(1, trData.args.size()); assertEquals("0", trData.args.get(0)); } assertNotNull(parsedTraceData.get("byteArrayArg")); { TraceData trData = parsedTraceData.get("byteArrayArg"); assertEquals(1, trData.args.size()); assertEquals("[1]", trData.args.get(0)); } assertNotNull(parsedTraceData.get("shortArg")); { TraceData trData = parsedTraceData.get("shortArg"); assertEquals(1, trData.args.size()); assertEquals("2", trData.args.get(0)); } assertNotNull(parsedTraceData.get("shortArrayArg")); { TraceData trData = parsedTraceData.get("shortArrayArg"); assertEquals(1, trData.args.size()); assertEquals("[3]", trData.args.get(0)); } assertNotNull(parsedTraceData.get("intArg")); { TraceData trData = parsedTraceData.get("intArg"); assertEquals(1, trData.args.size()); assertEquals("4", trData.args.get(0)); } assertNotNull(parsedTraceData.get("intArrayArg")); { TraceData trData = parsedTraceData.get("intArrayArg"); assertEquals(1, trData.args.size()); assertEquals("[5]", trData.args.get(0)); } assertNotNull(parsedTraceData.get("longArg")); { TraceData trData = parsedTraceData.get("longArg"); assertEquals(1, trData.args.size()); assertEquals("6", trData.args.get(0)); } assertNotNull(parsedTraceData.get("longArrayArg")); { TraceData trData = parsedTraceData.get("longArrayArg"); assertEquals(1, trData.args.size()); assertEquals("[7]", trData.args.get(0)); } assertNotNull(parsedTraceData.get("floatArg")); { TraceData trData = parsedTraceData.get("floatArg"); assertEquals(1, trData.args.size()); assertEquals("8.0", trData.args.get(0)); } assertNotNull(parsedTraceData.get("floatArrayArg")); { TraceData trData = parsedTraceData.get("floatArrayArg"); assertEquals(1, trData.args.size()); assertEquals("[9.0]", trData.args.get(0)); } assertNotNull(parsedTraceData.get("doubleArg")); { TraceData trData = parsedTraceData.get("doubleArg"); assertEquals(1, trData.args.size()); assertEquals("10.0", trData.args.get(0)); } assertNotNull(parsedTraceData.get("doubleArrayArg")); { TraceData trData = parsedTraceData.get("doubleArrayArg"); assertEquals(1, trData.args.size()); assertEquals("[11.0]", trData.args.get(0)); } assertNotNull(parsedTraceData.get("boolArg")); { TraceData trData = parsedTraceData.get("boolArg"); assertEquals(2, trData.args.size()); assertEquals("true", trData.args.get(0)); assertEquals("false", trData.args.get(1)); } assertNotNull(parsedTraceData.get("boolArrayArg")); { TraceData trData = parsedTraceData.get("boolArrayArg"); assertEquals(2, trData.args.size()); assertEquals("[true]", trData.args.get(0)); assertEquals("[false]", trData.args.get(1)); } assertNotNull(parsedTraceData.get("boolArrayArrayArg")); { TraceData trData = parsedTraceData.get("boolArrayArrayArg"); assertEquals(2, trData.args.size()); assertEquals("[[true]]", trData.args.get(0)); assertEquals("[[false]]", trData.args.get(1)); } assertNotNull(parsedTraceData.get("charArg")); { TraceData trData = parsedTraceData.get("charArg"); assertEquals(1, trData.args.size()); assertEquals("2", trData.args.get(0)); } assertNotNull(parsedTraceData.get("charArrayArg")); { TraceData trData = parsedTraceData.get("charArrayArg"); assertEquals(1, trData.args.size()); assertEquals("[3]", trData.args.get(0)); } assertNotNull(parsedTraceData.get("objArg")); { TraceData trData = parsedTraceData.get("objArg"); assertEquals(1, trData.args.size()); assertEquals("obj", trData.args.get(0)); } assertNotNull(parsedTraceData.get("objArrayArg")); { TraceData trData = parsedTraceData.get("objArrayArg"); assertEquals(1, trData.args.size()); assertEquals("[obj]", trData.args.get(0)); } } private void parseLine(Map<String, TraceData> parsedTraceData, String traceLine) { //System.out.println("Parse: " + traceLine); if (traceLine.contains("DEBUG")) return; String[] traceParts = traceLine.split(":##:"); String traceType = traceParts[0]; String traceLineData = traceParts[1]; traceLineData = traceLineData.substring(1, traceLineData.length() - 1); String[] dataParts = traceLineData.split(","); String methodSig; if (traceType.equals("Throwable")) { methodSig = dataParts[2].trim(); } else { methodSig = dataParts[1].trim(); } TraceData traceData = parsedTraceData.get(methodSig); if (traceData == null) { traceData = new TraceData(); parsedTraceData.put(methodSig, traceData); } if (traceType.equals("Enter")) { traceData.seenEnter = true; } else if (traceType.equals("Exit")) { traceData.seenExit = true; } else if (traceType.equals("Branch")) { traceData.branchLines.add(Integer.parseInt(dataParts[2].trim())); } else if (traceType.equals("Throwable")) { if (traceLine.contains("Caught")) { traceData.caughtLines.add(Integer.parseInt(dataParts[3].trim())); } } else if (traceType.startsWith("Arg")) { traceData.args.add(dataParts[2].trim()); } } @SuppressWarnings("unchecked") private void testSetting(String configConstant, String configValue) throws Exception { // Set setting sender.sendMessage(configConstant + configValue); Object okResponse = receiver.incomingMessages.take(); assertNotNull(okResponse); assertTrue(okResponse instanceof String); assertEquals(okResponse, "OK"); // Get settings sender.sendMessage("getsettings"); Object settingsResponse = receiver.incomingMessages.take(); assertNotNull(settingsResponse); assertTrue(settingsResponse instanceof Map<?, ?>); Map<String, String> settingsResponseMap = (Map<String, String>) settingsResponse; assertEquals(configValue, settingsResponseMap.get(configConstant)); } @SuppressWarnings("unchecked") private void testGetSetting(String configConstant, String configValue) throws Exception { // Get settings sender.sendMessage("getsettings"); Object settingsResponse = receiver.incomingMessages.take(); assertNotNull(settingsResponse); assertTrue(settingsResponse instanceof Map<?, ?>); Map<String, String> settingsResponseMap = (Map<String, String>) settingsResponse; assertEquals(configValue, settingsResponseMap.get(configConstant)); } @Override protected void tearDown() throws Exception { receiver.stop(); sender.stop(); socket.close(); super.tearDown(); } private void connectToAgent() throws Exception { String host = "localhost"; int port = Integer.parseInt(System.getProperty("org.intrace.port")); socket = new Socket(); socket.connect(new InetSocketAddress(host, port)); // Start threads receiver = new Receiver(socket.getInputStream()); receiver.start(); sender = new Sender(socket.getOutputStream()); sender.start(); } private static class Sender implements Runnable { private final OutputStream outputStream; private final BlockingQueue<String> outgoingMessages = new LinkedBlockingQueue<String>(); private Thread th; public Sender(OutputStream outputStream) { this.outputStream = outputStream; } public void stop() { try { outputStream.close(); } catch (IOException e) { // Throw away } th.interrupt(); } public void start() { th = new Thread(this); th.setDaemon(true); th.setName("Sender"); th.start(); } @Override public void run() { try { while (true) { String message = outgoingMessages.take(); ObjectOutputStream objOut = new ObjectOutputStream(outputStream); objOut.writeObject(message); objOut.flush(); } } catch (Exception e) { // Do something } } public void sendMessage(String message) { try { outgoingMessages.put(message); } catch (InterruptedException e) { // Do nothing } } } private static class Receiver implements Runnable { private final InputStream inputStream; private final BlockingQueue<Object> incomingMessages = new LinkedBlockingQueue<Object>(); private Thread th; public Receiver(InputStream inputStream) { this.inputStream = inputStream; } public void stop() { try { inputStream.close(); } catch (IOException e) { // Throw away } th.interrupt(); } public void start() { th = new Thread(this); th.setDaemon(true); th.setName("Receiver"); th.start(); } @Override public void run() { try { while (true) { ObjectInputStream objIn = new ObjectInputStream(inputStream); Object receivedMessage = objIn.readObject(); // System.out.println("Test received: " + receivedMessage); if (receivedMessage instanceof Map<?, ?>) { Map<?, ?> receivedMap = (Map<?, ?>) receivedMessage; if (receivedMap.containsKey(AgentConfigConstants.NUM_PROGRESS_ID)|| receivedMap.containsKey(AgentConfigConstants.STID)) { continue; } } incomingMessages.put(receivedMessage); } } catch (Exception e) { // Do nothing } } } }