Commit f4bc404f authored by František Haas's avatar František Haas

added junit testing

improved docs
simplified example
parent 3d8621b2
......@@ -8,5 +8,7 @@
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="build/eclipse-dynamicbypass.jar"/>
<classpathentry kind="lib" path="lib/asm-debug-all-4.1.jar"/>
<classpathentry kind="lib" path="lib/hamcrest-core-1.3.jar"/>
<classpathentry kind="lib" path="lib/junit-4.11.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
Overview
========
Tests are implemented using JUnit. There are classic unit tests that are
located in the ch.usi.dag.disl.test2.junit package and complex tests in the
ch.usi.dag.disl.test2.suite package that invoke all parts of the framework
(client, server, ...) and verify computed results.
All tests are performed calling
- ant test2
To use the for debuging purpose during development a single suite test (for
example "after" test) can be invoked calling
- ant suite-test2 -Dtest2.name=after
Parameters can specified to ant at command line starting with "-Ddisl." are
passed to the test. For example to show tests output specify
- -Ddisl.test.verbose=true
When built with "ant prepare-test2" tests can be also run directly. They are
packed in the "build-test" directory.
asm.lib=asm-debug-all-4.1.jar
asm.path=lib/${asm.lib}
junit.core.lib=junit-4.11.jar
junit.core.path=lib/${junit.core.lib}
junit.hamcrest.lib=hamcrest-core-1.3.jar
junit.hamcrest.path=lib/${junit.hamcrest.lib}
src.disl=src
src.dynbypass=src-dynbypass
src.dynbypass.act=src-dynbypass-act
......@@ -15,6 +20,7 @@ bin=bin
build=build
build.thread=build-thread
build.afterbootstrap=build-abs
build.test=build-test
dist=dist
test=test
......
......@@ -4,8 +4,17 @@
<property file="disl.version" />
<path id="buildpath">
<pathelement location="${bin}" />
<pathelement location="${asm.path}" />
<pathelement location="${junit.core.path}" />
<pathelement location="${junit.hamcrest.path}" />
</path>
<path id="classpath">
<pathelement location="${bin}" />
<pathelement location="${asm.path}" />
<pathelement location="${junit.core.path}" />
<pathelement location="${junit.hamcrest.path}" />
</path>
<!-- it automatically compiles all other necessary stuff :) -->
......@@ -166,10 +175,10 @@
<target name="clean">
<delete dir="${bin}" />
<delete dir="${build}" />
<delete dir="${build.test}" />
<delete dir="${build.thread}" />
<delete dir="${build.afterbootstrap}" />
<delete dir="${dist}" />
<delete dir="${test}" />
<exec executable="make" dir="${src.agent.c}/javahome">
<arg value="clean" />
</exec>
......@@ -217,6 +226,116 @@
<!-- *** test instrumentaion package *** -->
<!-- iterates over files and runs targets on them, dir is passed as property -->
<!-- property passed in the targets must be used directly -->
<scriptdef name="package-test2-script" language="javascript">
<element name="dirset" type="dirset" />
<![CDATA[
dirsets = elements.get("dirset");
for (i = 0; i < dirsets.size(); ++i) {
var it = dirsets.get(i).iterator();
while (it.hasNext()) {
dir = it.next().getName();
self.project.setProperty( "tmp.test2.name", dir);
self.project.executeTarget( "package-test2-app");
self.project.executeTarget( "package-test2-instr");
}
}
]]>
</scriptdef>
<target name="package-test2-app">
<jar jarfile="${build.test}/disl-app-${tmp.test2.name}.jar"
basedir="${bin}"
includes="ch/usi/dag/disl/test2/suite/${tmp.test2.name}/app/**">
<manifest>
<attribute name="Main-Class" value="ch.usi.dag.disl.test2.suite.${tmp.test2.name}.app.TargetClass" />
</manifest>
</jar>
</target>
<target name="package-test2-instr">
<jar jarfile="${build.test}/disl-instr-${tmp.test2.name}.jar"
basedir="${bin}"
includes="ch/usi/dag/disl/test2/suite/${tmp.test2.name}/instr/**"
excludes="ch/usi/dag/disl/test2/suite/${tmp.test2.name}/instr/MANIFEST.MF">
<manifest>
<attribute name="DiSL-Classes" value="ch.usi.dag.disl.test2.suite.${tmp.test2.name}.instr.DiSLClass" />
</manifest>
</jar>
</target>
<target name="package-test2">
<mkdir dir="${build.test}"/>
<package-test2-script>
<dirset dir="src-test/ch/usi/dag/disl/test2/suite/">
<include name="*" />
</dirset>
</package-test2-script>
</target>
<target name="copy-test2">
<copy todir="${bin}">
<fileset dir="${src.test}">
<include name="**/*.resource" />
</fileset>
</copy>
</target>
<target name="prepare-test2" depends="prepare-all, copy-test2, package-test2" />
<target name="test2" depends="prepare-test2">
<junit
haltonfailure="no"
haltonerror="no"
failureproperty="junit.failure">
<classpath refid="classpath" />
<formatter type="brief" usefile="false" />
<!-- allows properties starting with "-Ddisl." to be passed to tests -->
<syspropertyset>
<propertyref prefix="disl" />
</syspropertyset>
<batchtest fork="yes">
<fileset dir="${src.test}">
<include name="*ch/usi/dag/disl/test2/junit/*Test.java" />
<include name="*ch/usi/dag/disl/test2/suite/*/junit/*Test.java" />
</fileset>
</batchtest>
</junit>
</target>
<target
name="report-test2-name"
depends="check-test2-name"
unless="test2.name.set">
<fail message="Property test2.name is not set. Set it using -Dtest2.name=value" />
</target>
<target name="check-test2-name">
<condition property="test2.name.set">
<isset property="test2.name"/>
</condition>
</target>
<target name="suite-test2" depends="prepare-test2, report-test2-name">
<junit
haltonfailure="no"
haltonerror="no"
failureproperty="junit.failure">
<classpath refid="classpath" />
<formatter type="brief" usefile="false" />
<!-- allows properties starting with "-Ddisl." to be passed to tests -->
<syspropertyset>
<propertyref prefix="disl" />
</syspropertyset>
<batchtest fork="yes">
<fileset dir="${src.test}">
<include name="*ch/usi/dag/disl/test2/suite/${test2.name}/junit/*Test.java" />
</fileset>
</batchtest>
</junit>
</target>
<target name="check-test-property">
<condition property="test.set">
<isset property="test.name"/>
......@@ -233,7 +352,7 @@
<mkdir dir="${build}"/>
<jar jarfile="${build}/${instr.jar.name}.jar"
basedir="${bin}"
includes="${test.path}/**"
includes="${test.gath}/**"
excludes="${test.path}/TargetClass*.class ${test.path}/MANIFEST.MF"
manifest="${src.test}/${test.path}/MANIFEST.MF">
</jar>
......
This diff is collapsed.
......@@ -9,34 +9,51 @@ The example contains simple user application that prints hello string on
command line and instrumentation printing strings before and after application
main.
HOW TO RUN THE EXAMPLE
HOW TO RUN THE EXAMPLE
======================
First be sure that you compiled DiSL (see README in the root directory). Then
follow the steps:
1) compile application calling "ant" in the app directory
2) compile instrumentation calling "ant" in the instrumentation directory
3) run "./runApp.sh instr/build/disl-instr.jar -jar app/build/example-app.jar"
Easiest way to run the example is to execute "ant run".
This command builds and packs the application and the instrumentation
application. And then starts the instrumentation server and later on also the
application
The output of the application and the applied instrumentation should be visible
on command line.
When compiled the example can be also run using "disl.py" script directly. This
enables more detailed settings. To learn more about this run "./disl.py -h"
The last command starts the application together with DiSL. The output of the
application and the applied instrumentation should be visible on command line.
NOTE
====
NOTE: If you want to use "runApp.sh" to run your application, don't forget to set
"DISL_LIB_P" variable correctly. Also, you can modify DiSL setting in
"runServer.sh".
If you want to use "disl.py" to run your application, don't forget to set
"DISL_HOME" variable correctly or it can be specified to "disl.py" at command
line or inside the script as a default value.
NOTE
====
If you want to include use some library in your instrumentation you
shlould insert it directly to the instrumentation jar using jarjar.
http://code.google.com/p/jarjar/
NOTE: If you want to include use some library in your instrumentation you shlould
insert it directly to the instrumentation jar using jarjar.
http://code.google.com/p/jarjar/
DIRECTORIES (FILES)
===================
app - contains sources of simple application
instr - contains sources of simple instrumentation
runApp.sh - bash script starting the application together with
DiSL (+ instrumentation)
runServer.sh - starts instrumentation server
runClient.sh - starts application vm with necessary agents
app
- contains sources of simple application
instr
- contains sources of simple instrumentation
disl.py
- python script starting the application together with DiSL (+instrumentation)
<project name="disl-example" default="prepare-all" basedir=".">
<target name="prepare-all">
<ant antfile="app/build.xml" target="package" useNativeBasedir="true" />
<ant antfile="instr/build.xml" target="package" useNativeBasedir="true" />
</target>
<target name="clean">
<ant antfile="app/build.xml" target="clean" useNativeBasedir="true" />
<ant antfile="instr/build.xml" target="clean" useNativeBasedir="true" />
</target>
<target name="run" depends="prepare-all">
<exec executable="./disl.py">
<arg value="--" />
<arg value="instr/build/disl-instr.jar" />
<arg value="-jar" />
<arg value="app/build/example-app.jar" />
</exec>
</target>
</project>
......@@ -410,6 +410,15 @@ def run_client(args, parser):
client = Popen(c_cmd, stdout=c_out_f, stderr=c_err_f, shell=False)
client.wait()
try:
with open(".server.pid", "r") as pid_file:
pid = pid_file.readline()
kill = Popen(["kill", pid], stdout=PIPE, shell=False)
except IOError:
pass
os.remove(".server.pid")
return
......
# /bin/sh
# default DiSL lib path
if [ -z "${DISL_LIB_P}" ]; then
DISL_LIB_P=../build
fi
# test number of arguments
EXPECTED_ARGS=2
if [ $# -lt $EXPECTED_ARGS ]
then
echo "Usage: `basename $0` instr-lib java-params"
exit
fi
# set server file
SERVER_FILE=.server.pid
export SERVER_FILE
# kill running server
if [ -e ${SERVER_FILE} ]
then
kill -KILL `cat ${SERVER_FILE}`
rm ${SERVER_FILE}
fi
export DISL_LIB_P
# start server (with instrumentation library) and take pid
./runServer.sh $1
# wait for server startup
sleep 3
# run client
./runClient.sh $*
# wait for server shutdown
sleep 1
# kill server
kill -KILL `cat ${SERVER_FILE}` 2> /dev/null
rm ${SERVER_FILE}
#!/bin/bash
# set default lib path
if [ -z "${DISL_LIB_P}" ]; then
DISL_LIB_P=./build
fi
# test number of arguments
EXPECTED_ARGS=2
if [ $# -lt $EXPECTED_ARGS ]
then
echo "Usage: `basename $0` instr-lib java-params"
exit
fi
# set proper lib depending on OS
OS=`uname`
if [ "${OS}" = "Darwin" ]; then
C_AGENT="${DISL_LIB_P}/libdislagent.jnilib"
else
C_AGENT="${DISL_LIB_P}/libdislagent.so"
fi
# get instrumentation library and shift parameters
INSTR_LIB=$1
shift
# start client
java -agentpath:${C_AGENT} \
-javaagent:${DISL_LIB_P}/disl-agent.jar \
-Xbootclasspath/a:${DISL_LIB_P}/disl-agent.jar:${INSTR_LIB} \
$*
#!/bin/sh
# set default lib path
if [ -z "${DISL_LIB_P}" ]; then
DISL_LIB_P=./build
fi
# available options
# -Ddebug=true \
# -Ddisl.classes="list of disl classes (: - separator)"
# -Ddisl.noexcepthandler=true \
# -Ddisl.exclusionList="path" \
# -Ddislserver.instrumented="path" \
# -Ddislserver.uninstrumented="path" \
# -Ddislserver.port="portNum" \
# -Ddislserver.timestat=true \
# -Ddislserver.continuous=true \
# get instrumentation library and shift parameters
INSTR_LIB=$1
shift
# start server
java $* \
-cp ${INSTR_LIB}:${DISL_LIB_P}/disl-server.jar \
ch.usi.dag.dislserver.DiSLServer \
&
# print pid to the server file
if [ -n "${SERVER_FILE}" ]; then
echo $! > ${SERVER_FILE}
fi
package ch.usi.dag.disl.test2.junit;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.Ignore;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class SmokeTest {
@Test
public void thisAlwaysPasses() {
}
@Test
@Ignore
public void thisIsIgnored() {
}
}
package ch.usi.dag.disl.test2.suite.after.app;
public class TargetClass {
public void print(boolean flag) {
try {
System.out.println("app: TargetClass.print(..) - try:begin");
if (flag) {
String float_one = "1.0";
int int_one = Integer.valueOf(float_one);
System.out.println("app: UNREACHABLE " + int_one);
}
System.out.println("app: TargetClass.print(..) - try:end");
} finally {
System.out.println("app: TargetClass.print(..) - finally");
}
}
// FIXME
// this main would fail the test
// could be fixed by adding app's code to the server's classpath
//
//public static void main(String[] args) {
// try {
// TargetClass t = new TargetClass();
// System.out.println("app: main - .print(false)");
// t.print(false);
// System.out.println("app: main - .print(true)");
// t.print(true);
// } catch (Throwable e) {
// System.out.println("app: main - catch");
// }
//}
public static void main(String[] args) {
TargetClass t = new TargetClass();
System.out.println("app: TargetClass.main(..) - TargetClass.print(false)");
t.print(false);
System.out.println("app: TargetClass.main(..) - TargetClass.~print(false)");
System.out.println("app: TargetClass.main(..) - TargetClass.print(true)");
t.print(true);
System.out.println("app: TargetClass.main(..) - TargetClass.~print(true)");
}
}
package ch.usi.dag.disl.test2.suite.after.instr;
import ch.usi.dag.disl.annotation.After;
import ch.usi.dag.disl.annotation.AfterThrowing;
import ch.usi.dag.disl.marker.BodyMarker;
public class DiSLClass {
@After(marker = BodyMarker.class, scope = "TargetClass.print(boolean)", order = 0)
public static void after() {
System.out.println("disl: after");
}
@AfterThrowing(marker = BodyMarker.class, scope = "TargetClass.print(boolean)", order = 1)
public static void afterThrowning() {
System.out.println("disl: afterThrowning");
}
}
package ch.usi.dag.disl.test2.suite.after.junit;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import ch.usi.dag.disl.test2.utils.*;
@RunWith(JUnit4.class)
public class AfterTest {
ClientServerRunner r = new ClientServerRunner(this.getClass());
@Test
public void test()
throws Exception {
r.start();
r.waitFor();
r.assertIsNotRunning();
//r.flushClientOut();
//r.flushClientErr();
// FIXME
//r.assertIsFinished();
r.assertClientOut("client.out.resource");
// FIXME
//r.assertClientErrNull();
r.assertServerOutNull();
r.assertServerErrNull();
if(Boolean.parseBoolean(System.getProperty("disl.test.verbose"))) {
r.verbose();
}
}
@After
public void cleanup() {
r.destroy();
}
}
app: TargetClass.main(..) - TargetClass.print(false)
app: TargetClass.print(..) - try:begin
app: TargetClass.print(..) - try:end
app: TargetClass.print(..) - finally
disl: after
app: TargetClass.main(..) - TargetClass.~print(false)
app: TargetClass.main(..) - TargetClass.print(true)
app: TargetClass.print(..) - try:begin
app: TargetClass.print(..) - finally
disl: after
disl: afterThrowning
package ch.usi.dag.disl.test2.suite.after2.app;
public class TargetClass {
public static void main(String[] args) {
// NOTE: this test is better to use on large project and not this simple class
System.out.println("app: TargetClass.main(..)");
}
}
package ch.usi.dag.disl.test2.suite.after2.instr;
import java.util.Stack;
import ch.usi.dag.disl.annotation.After;
import ch.usi.dag.disl.annotation.Before;
import ch.usi.dag.disl.annotation.ThreadLocal;
import ch.usi.dag.disl.marker.BodyMarker;
import ch.usi.dag.disl.staticcontext.uid.UniqueMethodId;
import ch.usi.dag.disl.test2.suite.after2.instr.runtime.Analysis;
public class DiSLClass {
@ThreadLocal
static Stack<Integer> stackTL;
@Before(marker = BodyMarker.class, order = 0, scope = "*.*", guard = NotInitNorClinit.class)
public static void onMethodEntryObjectArgs(UniqueMethodId id) {
Stack<Integer> thisStack;
if((thisStack = stackTL) == null) {
thisStack = (stackTL = new Stack<Integer>());
}
thisStack.push(id.get());
}
@After(marker = BodyMarker.class, order = 0, scope = "*.*", guard = NotInitNorClinit.class)
public static void onMethodExit(UniqueMethodId id) {
Analysis.onExit(stackTL, id.get());
}
}
package ch.usi.dag.disl.test2.suite.after2.instr;
import ch.usi.dag.disl.annotation.GuardMethod;
import ch.usi.dag.disl.staticcontext.MethodStaticContext;
public class NotInitNorClinit {
@GuardMethod
public static boolean isApplicable(MethodStaticContext msc) {
return (msc.thisMethodName().endsWith("init>")) ? false : true;
}
}
package ch.usi.dag.disl.test2.suite.after2.instr.runtime;
import java.util.Stack;
public class Analysis {
public static void onExit(Stack<Integer> stack, int methodUID) {
int topOfTheStack = stack.pop();
if (topOfTheStack != methodUID) {
System.err.println("After method: " + methodUID);
int size = stack.size();
System.err.println("Stack[" + size + "]: " + topOfTheStack);
for(int i = size - 1; i >= 0; i--) {
System.err.println("Stack[" + i + "]: " + stack.elementAt(i));
}
System.exit(1);
}
}
}
package ch.usi.dag.disl.test2.suite.after2.junit;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import ch.usi.dag.disl.test2.utils.*;
@RunWith(JUnit4.class)
public class After2Test {
ClientServerRunner r = new ClientServerRunner(this.getClass());
@Test
public void test()
throws Exception {
r.start();
r.waitFor();
r.assertIsNotRunning();
//r.flushClientOut();
//r.flushClientErr();
r.assertIsFinished();
r.assertClientOut("client.out.resource");
r.assertClientErrNull();
r.assertServerOutNull();
r.assertServerErrNull();
if(Boolean.parseBoolean(System.getProperty("disl.test.verbose"))) {
r.verbose();
}
}
@After
public void cleanup() {
r.destroy();
}
}
package ch.usi.dag.disl.test2.suite.after3.app;
public class TargetClass {
public static void main(String[] args) {
try {
System.out.println("app: TargetClass.main(..) - .ethrowing1()");
ethrowing1();
System.out.println("app: TargetClass.main(..) - ~ethrowing1()");
} catch (Exception e) {
System.out.println("app: TargetClass.main(..) - catch");
}
try {
System.out.println("app: TargetClass.main(..) - .ereturning1()");
ereturning1();
System.out.println("app: TargetClass.main(..) - ~ereturning1()");
} catch (Exception e) {
System.out.println("app: TargetClass.main(..) - catch");
}
try {
System.out.println("app: TargetClass.main(..) - .ethrowing2()");
ethrowing2();
System.out.println("app: TargetClass.main(..) - ~ethrowing2()");