Related documents:
Main.java
and expect an error on line 10 in a file ajcSample.xml
:
<!DOCTYPE suite SYSTEM "../tests/ajcTestSuite.dtd"> <suite> <ajc-test dir="new" title="simple error test"> <compile files="Main.java"> <message kind="error" line="10"/> </compile> </ajc-test> </suite>
Here is an example to compile
pack/Aspect.java
and
pack2/Main.java
and
run the main class:
<ajc-test dir="new" title="simple run test"> <compile files="pack/Aspect.java,pack1/Main.java"/> <run class="pack1.Main"/> </ajc-test>The compile and run steps of a given ajc-test share a common sandbox, so (e.g.,) the run step knows to set its classpath using the classes directory generated by the compile step.
More complex compilations are discussed in Compiler Options below.
dir
attribute in the ajc-test
element specifies a base test directory
relative to the directory of the test specification file.
All paths are specified relative to this base test directory.
E.g., the last example used dir="new"
and
presumed the following directory structure:
{some dir} # test specification directory {testDefinition}.xml new/ # test base directory pack/Aspect.java pack2/Main.javaTest cases with only one file in the default package can often share directories (e.g., see the many files in new/), but usually a test case has its own directory.
<ajc-test dir="new/incremental1" title="incremental test"> <compile staging="true" sourceroots="." options="-incremental" /> <run class="Main"/> <inc-compile tag="20"> <message kind="error" line="15"> </inc-compile> <inc-compile tag="30"/> <run class="Main"/> </ajc-test>To understand what's happening in this test would require looking at the source directory to see which files are tagged "20" and "30". But before walking through that, there's a variation of incremental building for AJDE. (The AJDE wrapper around the
ajc
compiler can also be driven by the
test harness.)
In AJDE, incremental tests also involve the notion of "fresh builds", i.e., when the test reuses the same compiler and build configuration but rebuilds from scratch. In that case, there is still the question of whether source files should be updated; if not, the tag can have the special value "same". For example, if the last example had two more lines:
... <inc-compile tag="30"/> <run class="Main"/> <inc-compile fresh="true" tag="same"/> <run class="Main"/> </ajc-test>The test would complete by completely rebuilding the same files and then running the main class. This option has no effect on the normal (ajc) compiler, and requires specifying
-ajdeCompiler
to the harness or compile step
as an argument.
To recap the attributes of note for setting up incremental tests:
compile
staging="true"
:
Incremental tests require staging, which copies the
test source files to a temporary directory so they can be
updated during the test without changing the actual sources.
compile
sourceroots="{..}"
:
incremental mode only takes source files in the form of
-sourceroots
entries.
compile
options="-incremental{,..}"
:
Specify the -incremental
option to signal
incremental mode to the compiler. If you do not include this,
the harness will still run an incremental test, but the compiler
will not do its additional checking it on input options.
inc-compile
tag="{##}"
:
The required tag attribute is a suffix identifying
files in the test source directory specifying how the sources should
be changed before that incremental compilation step.
If there is a prefixing suffix "delete", then the file is deleted;
otherwise, the file is copied (with the effect either of updating
an existing file or adding a new file).
If the tag is "same", then no files are changed.
inc-compile
fresh="true"
:
With the AJDE compiler, you can rebuild the current build
configuration in its entirety. (By contrast, doing another
<compile> task would re-initialize the AJDE compiler.)
This option is ignored unless -ajdeCompiler
is passed to the harness on the command line or to the
immediately preceding <compile> task in the options.
{some dir} {testDefinition}.xml new/ incremental1/ DeleteMe.delete.30.java DeleteMe.java Main.20.java Main.30.java Main.java NewFile.30.javaComparing this with the test specification, you can see the harness will run one compile and two re-compiles:
Main.java
and DeleteMe.java
<compile staging="true" files="Main.java,DeleteMe.java"/> {some dir} {testDefinition}.xml new/ incremental1/ ... DeleteMe.java ... Main.java ...
Main.java
with the contents of
Main.20.java
and recompile, expecting an error on line 15:
<inc-compile tag="20"> <message kind="error" line="15"> </inc-compile> {some dir} {testDefinition}.xml new/ incremental1/ ... Main.20.java ...
DeleteMe.java
,
add NewFile.java
,
update Main.java
with the contents of
Main.30.java
and recompile with no error or warning messages:
<inc-compile tag="30"/> {some dir} {testDefinition}.xml new/ incremental1/ DeleteMe.delete.30.java ... Main.30.java ... NewFile.30.java
Detect | Evaluate |
---|---|
Exceptions | signal failure |
Result value | heuristically compare with expected: compiles not expecting errors are expected to return a normal result status, and vice-versa. |
Messages (e.g., compiler warnings and errors) | Compare with expected messages |
Directory changes (e.g., .class files created) |
Compare with expected changes |
Runtime behavior | Use Tester in test source code
to signal events for comparison with expected events.
To signal failure when running a forked test without Tester,
throw an exception. |
message
element
specifies a condition on the successful completion of the nesting
ajc-test sub-element. In the earlier example, if
the harness does not detect an error message on line 10 or if
there are unexpected messages, then the compile step will be
reported as failing:
<ajc-test dir="new" title="simple error test"> <compile files="Main.java"> <message kind="error" line="10"/> </compile> </ajc-test>Expected messages can be specified as sub-elements for the three
ajc-test
elements
compile
,
inc-compile
, and
run
.
Messages require a kind (error or warning) and a line.
To make specification easier, if an error is specified for a line,
the harness accepts as expected any number of errors on that line.
Most messages fall into those categories. However, an IMessage also has a Throwable thrown, a String detail, and a List of ISourceLocation (essentially, "see also", to point to other relevant places in the code). The thrown element is not supported, but you can specify the others:
<ajc-test dir="new" title="simple error test"> <compile files="Main.java"> <message kind="error" line="10" file="Main.java" text="This join point should never happen!" detail="(Find the declaring code below.)"> <source line="12" file="Main.java"/> <source line="14" file="Main.java"/> <message> </compile> </ajc-test>This compiler-error test specifies a single error message triggered on line 10 of Main.java, with some text and details and two other source locations that are relevant, on lines 12 and 14 of the same file.
When specifying messages, be sure to provide enough detail that the harness can distinguish expected messages. For example, if you only specify the line number, then it will match the message in any file (if there is more than one). If there are two or more messages expected on a line, provide enough information to distinguish them. If you are using text or detail attributes, do not use one string that is a prefix of the other, since it will match either message, and the other message might not match.
The "info" messages are special in that they are normally ignored.
To specify expected "info" messages, you have to list all the
messages the compiler will issue, which can vary depending on
the compiler settings. Use the option ^verbose
to
force the compiler's -verbose
option off.
By the same token, if you don't specify any extra source locations, then they will not be checked. If you think it is a bug if they are issued, then you have to specify one if them. (There is currently no way to specify that a message has no extra source locations.)
Directory changes have been used only to validate changes in
the classes directory.
The current harness defaults to using the classes directory,
and when using the classes directory uses .class
as a default suffix.
Here's an example specification:
<ajc-test dir="new/dirchanges-test" title="dir-changes test"> <compile staging="true" files="Main.java,DeleteMe.java,Unchanged.java"/> <inc-compile tag="20"> <dir-changes updated="Main" removed="DeleteMe" unchanged="Unchanged"/> </inc-compile> </ajc-test>It checks after a recompile that
Main.class
was updatedDeleteMe.class
was deletedUnchanged.class
was not touchedTester
utility class provides API's for signalling
actual and expecting events and comparing the two.
Typically, events are symbolized and compared as String.
Here's a small sample test case that for the scenario above:
import org.aspectj.testing.Tester; public class Main implements Runnable { public static void main(String[] args) { Tester.expectEvent("before advice"); Tester.expectEvent("execute run"); new Main().run(); Tester.checkAllEvents(); } public void run() { Tester.event("execute run"); } } aspect A { before () : target(Runnable) && execution(void run()) { Tester.event("before advice"); } }If either the advice or the method does not run, the harness will report a failure.
Tester
also has methods that operate like
JUnit assertions as idioms to detect differences in
expected and actual values, signalling appropriately.
Tester
is at
../testing-client/src/org/aspectj/testing/Tester.java
and is built into
../lib/tests/testing-client.jar
which is included on the classpath by the compile and run steps.
You can write runtime test cases without using Tester; simply throw some exception from the main thread to signal failure.
You can fork the tests that are run by setting global properties
or by setting attributes to run. One attribute,
aspectpath
, forces the run to fork and uses
load-time weaving to weave aspects with the classpath.
To fork for all tests, set the global system property
javarun.fork
to "true". You can also
set other properties to control how forking happens.
(By default, forking uses the java executable that was
used to run the harness.)
The following causes all run
steps to
fork into the specified Java 1.1 VM.
java -Djavarun.fork=true \ -Djavarun.java=c:/home/jdk11/bin/java.exe \ -Djavarun.java.home=c:/home/jdk11 \ -Djavarun.bootclasspath=c:/home/jdk11/lib/classes.zip \ -Djavarun.vmargs=-Dname=value,-Dname2="value 2" \ org.aspectj.testing.drivers.Harness ...
You can fork a specific test by setting
the fork
attribute (and optionally the
vmargs
attribute):
<ajc-test dir="new" title="show forking"> <compile files="Main.java"/> <run class="Main" fork="true" vmargs="-Dname=value,-Dname2="value 2"/> </ajc-test>
As a special case of forking, load-time weaving sets up
a Java 1.4 or later VM using our weaving class loader,
if you specify the ltw
attribute to
run
.
In the following example, the main class and the aspect
are compiled separately. The main class is then woven
and run using the aop.xml file specified in the ltw
attribute.
<ajc-test dir="new" title="show LTW"> <compile files="Aspect.java" outjar="out.jar"/> <compile files="Main.java"/> <run class="Main" ltw="aop-test.xml"/> </ajc-test>
options
attribute:
<ajc-test dir="new" title="lint test"> <compile files="LintTest.java" options="-Xlint,-emacssym,-source,1.4"> <message kind="warning" line="22"> </compile>This should work even for complex single-arg options like
-g:none
, but will fail for comma-delimited single-arg options like
-g:lines,vars
because the comma delimiters
are ambiguous (yes, a design bug!).
The compile
element has the following attributes
which handle most of the other compiler arguments:
files
: .aj and .java files are treated as source files,
but .jar/zip files are extracted and passed to the compiler
as -injars
and readable directories are passed as -inpath
.
classpath
: directories and jar files for the classpath
aspectpath
: binary aspects in jar files
argfiles
: argument list files
sourceroots
: root directories for source files
xlintfile
: override org.aspectj.weaver.XlintDefault.properties
compiler
attributes:
<ajc-test dir="new" title="attributes test"> <compile files="Main.java,injar.jar,some-directory" staging="true" options="-Xlint,-g:none" argfiles="debug.lst,aspects/test.lst" aspectpath="jars/requiredAspects.jar" xlintfile="ignore-all-but-typenotfound.properties" classpath="providedClassesDir,jars/required.jar"/> <inc-compile tag="20"/> </ajc-test>
badInput
attribute
to "true
". This prevents the harness from aborting a test
because a specified input file was not found. (However, there is no way
to specify bad input for a directory in the files attribute intended for
-inpath, because the harness selects readable directories.)
-outjar {file}, -log {file}
.
(-d {dir}
is used but specification is not supported.)
inc-compile
and run
elements
use information set up earlier by compile
,
some of which is only implicit.
When a test is run, the harness creates a staging directory
for temporary files and a sandbox component for sharing information
between test components, particularly classpath entries
shared between the compile and run components.
The compile and run components share classpath information
through the sandbox, adding default libraries:
compile
always includes the jars
../lib/tests/aspecjrt.jar and
../lib/tests/testing-client.jar
on the compile classpath.
run
sets up its classpath as the compile
classpath plus the compile output (classes) directory
plus any entries on the aspectpath.