aboutsummaryrefslogtreecommitdiffstats
path: root/tests/readme-writing-compiler-tests.html
blob: 7266ceebafd7ed38d2248ebf8784e8dda9ad0931 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
<html>
<!-- <![CDATA[ putDataHere ]]> -->
<title>Writing tests for the AspectJ compiler
</title>
<body>
<h2>Writing tests for the AspectJ compiler
</h2>
The AspectJ project has a harness 
which reads test specification files and run tests. 
The tests are usually simple scenarios like 
"compile and run" or "compile expecting error", 
but may involve multiple files, incremental compilation, 
classpath or aspectpath entries, etc.
This document shows how to write tests that can be run
by the harness and suggests some patterns to use in
test code, discussing

<ul>
  <li><a href="#simple">Simple test definitions</a></li>
  <li><a href="#sourceFiles">Test source files</a></li>
  <li><a href="#incremental">Incremental tests</a></li>
  <li><a href="#verifying">Verifying test steps</a></li>
     <ul>
     <li><a href="#messages">Messages</a></li>
     <li><a href="#dirchanges">Changes in an output directory</a></li>
     <li><a href="#tester">Runtime behavior</a></li>
     </ul>
  <li><a href="#compilerOptions">Compiler Options</a></li>
  <li><a href="#background">Harness background</a></li>
</ul>
Most people just writing a test case need to know only the
information in 
  <a href="#simple">Simple test definitions</a> and 
  <a href="#sourceFiles">Test source files</a>.

<p>Related documents:
<ul>
  <li>For information on running the harness, see
      <a href="readme-tests-module.html">
               readme-tests-module.html</a>
    </li>
  <li>For example test definition files, see
      <a href="ajcTests.xml">ajcTests.xml</a> and 
      <a href="ajcTestsFailing.xml">ajcTestsFailing.xml</a>.
    </li>
</ul>

<a name="simple"></a>
<h4>Simple Test definitions</h4>

Test definitions are specified in XML files.
Here is a simple example to compile <code>Main.java</code>
and expect an error on line 10 in a file <code>ajcSample.xml</code>:
<pre>
&lt;!DOCTYPE suite SYSTEM "../tests/ajcTestSuite.dtd">
&lt;suite>
    &lt;ajc-test dir="new" title="simple error test">
        &lt;compile files="Main.java">
            &lt;message kind="error" line="10"/>
        &lt;/compile>
    &lt;/ajc-test>
&lt;/suite>
</pre>

<p>Here is an example to compile 
<code>pack/Aspect.java</code> and 
<code>pack2/Main.java</code> and 
run the main class:
<pre>
    &lt;ajc-test dir="new" title="simple run test">
        &lt;compile files="pack/Aspect.java,pack1/Main.java"/>
        &lt;run class="pack1.Main"/>
    &lt;/ajc-test>
</pre>
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.

<p>More complex compilations are discussed in
   <a href="#compilerOptions">Compiler Options</a> below.

<a name="sourceFiles"></a>
<h4>Test source files</h4>

The <code>dir</code> attribute in the <code>ajc-test</code>
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 <code>dir="new"</code> and
presumed the following directory structure:
<pre>
    {some dir}                  # test specification directory
        {testDefinition}.xml
        new/                    # test base directory
          pack/Aspect.java
          pack2/Main.java
</pre>
Test cases with only one file in the default package can often
share directories (e.g., see the many files in <a href="new">new/</a>),
but usually a test case has its own directory.

<a name="incremental"></a>
<h4>Incremental tests</h4>    

Incremental tests are more complex because they involve 
updating source files before recompiling. 
Here's an example that
<ul>
         <li>compiles Main.java,
    </li><li>runs it,
    </li><li>updates the source to introduce an error,
    </li><li>incrementally compile it (and detect the error)
    </li><li>updates the source to fix the error,
    </li><li>incrementally compile it (successfully), and
    </li><li>runs it again.
    </li>
</ul>
<pre>
    &lt;ajc-test dir="new/incremental1" title="incremental test">
        &lt;compile staging="true" 
             sourceroots="." 
             options="-incremental" />
        &lt;run class="Main"/>
        &lt;inc-compile tag="20">
            &lt;message kind="error" line="15">
        &lt;/inc-compile>
        &lt;inc-compile tag="30"/>
        &lt;run class="Main"/>
    &lt;/ajc-test>
</pre>

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 <code>ajc</code> compiler can also be driven by the
test harness.)

<p>
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:
<pre>
        ...
        &lt;inc-compile tag="30"/>
        &lt;run class="Main"/>
        
        &lt;inc-compile fresh="true" tag="same"/>
        &lt;run class="Main"/>
    &lt;/ajc-test>
</pre>
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 
<code>-ajdeCompiler</code> to the harness or compile step
as an argument.
<p>

To recap the attributes of note for setting up incremental tests:
<ul>
<li><code>compile</code> <code>staging="true"</code>: 
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.
</li>
<li><code>compile</code> <code>sourceroots="{..}"</code>:
incremental mode only takes source files in the form of
<code>-sourceroots</code> entries.
</li>
<li><code>compile</code> <code>options="-incremental{,..}"</code>:
Specify the <code>-incremental</code> 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.
</li>
<li><code>inc-compile</code> <code>tag="{##}"</code>:
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.
</li>
<li><code>inc-compile</code> <code>fresh="true"</code>:
With the AJDE compiler, you can rebuild the current build
configuration in its entirety.  (By contrast, doing another
&lt;compile> task would re-initialize the AJDE compiler.)
This option is ignored unless <code>-ajdeCompiler</code> 
is passed to the harness on the command line or to the
immediately preceding &lt;compile> task in the options.
</li>
</ul>

<p>
Now, to get back to the question of
what exactly is happening in an incremental test.  To do so,
compare the tags with the files specified in
the test source directory; the tagged files are the updates
for that particular step.  (By convention the tags are numeric
and in order, but they need not be.)
For example, here are some sources for the test above:
<pre>
    {some dir}
        {testDefinition}.xml
        new/
          incremental1/
            DeleteMe.delete.30.java
            DeleteMe.java
            Main.20.java
            Main.30.java
            Main.java
            NewFile.30.java
</pre>
Comparing this with the test specification, you can see 
the harness will run one compile and two re-compiles:
<ol>
  <li>Initially compile <code>Main.java</code> and <code>DeleteMe.java</code>
  <pre>
    &lt;compile staging="true"
              files="Main.java,DeleteMe.java"/>

    {some dir}
        {testDefinition}.xml
        new/
          incremental1/
            ...
            DeleteMe.java
            ...
            Main.java
            ...
  </pre>
  </li>
  <li>For incremental tag 20,
      update <code>Main.java</code> with the contents of 
      <code>Main.20.java</code>
      and recompile, expecting an error on line 15:
  <pre>
    &lt;inc-compile tag="20">
        &lt;message kind="error" line="15">
    &lt;/inc-compile>

    {some dir}
        {testDefinition}.xml
        new/
          incremental1/
            ...
            Main.20.java
            ...
  </pre></li>

  <li>For incremental tag 30,
      delete <code>DeleteMe.java</code>, 
      add <code>NewFile.java</code>,
      update <code>Main.java</code> with the contents of 
      <code>Main.30.java</code>
      and recompile with no error or warning messages:
  <pre>
    &lt;inc-compile tag="30"/>

    {some dir}
        {testDefinition}.xml
        new/
          incremental1/
            DeleteMe.delete.30.java
            ...
            Main.30.java
            ...
            NewFile.30.java
  </pre>
  </li>
</ol>

<a name="verifying"></a>
<h4>Verifying test steps</h4>
As seen above, two ways to verify that a compile was successful 
are to run the corresponding class or check the compiler messages. 
More generally, the harness can verify compile/run test steps by detecting the 
following things and comparing against expected behavior:
<p>

<table border="1" cellpadding="1">

<tr><th>Detect </th>
    <th>Evaluate</th>
  </tr>
<tr><td>Exceptions </td>
    <td>signal failure </td>
  </tr>

<tr><td>Result value </td>
    <td>heuristically compare with expected: 
        compiles not expecting errors are expected 
        to return a normal result status, and vice-versa.</td>
  </tr>
<tr><td>Messages (e.g., compiler warnings and errors)</td>
    <td>Compare with expected messages</td>
  </tr>

<tr><td>Directory changes (e.g., <code>.class</code> files created) </td>
    <td>Compare with expected changes</td>
  </tr>
<tr><td>Runtime behavior</td>
    <td>Use <code>Tester</code> in test source code
       to signal events for comparison with expected events.</td>
  </tr>
</table>
<p>

<a name="messages"></a>
<h5>Messages</h5>    

In a test definition, a nested <code>message</code> 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:
<pre>
    &lt;ajc-test dir="new" title="simple error test">
        &lt;compile files="Main.java">
            &lt;message kind="error" line="10"/>
        &lt;/compile>
    &lt;/ajc-test>
</pre>
Expected messages can be specified as sub-elements for the three 
  <code>ajc-test</code> elements 
  <code>compile</code>, 
  <code>inc-compile</code>, and 
  <code>run</code>.  
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.  
<p>
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:

<pre>
    &lt;ajc-test dir="new" title="simple error test">
        &lt;compile files="Main.java">
            &lt;message 
                kind="error" 
                line="10" 
                file="Main.java"
                text="This join point should never happen!"
                detail="(Find the declaring code below.)">
                &lt;source line="12" file="Main.java"/>
                &lt;source line="14" file="Main.java"/>
            &lt;message>
        &lt;/compile>
    &lt;/ajc-test>
</pre>

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.

<p>
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.

<p>
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 <code>^verbose<code> to 
force the compiler's <code>-verbose</code> option off.
<p>
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.)

<a name="dirchanges"></a>
<h5>Changes in an output directory</h5>

As with messages, specifying directory changes as a nested element
operates as a condition on the successful completion of the
nesting element.  The harness will check for files added, removed,
updated, or unchanged from a given directory.  The directory is
specified explicitly or using a token for the shared classes or
run directory.  For even more brevity, the harness supports a 
default suffix for the files.
<p>
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 <code>.class</code> 
as a default suffix.
<p>
Here's an example specification:
<pre>
    &lt;ajc-test dir="new/dirchanges-test" title="dir-changes test">
        &lt;compile staging="true"
                   files="Main.java,DeleteMe.java,Unchanged.java"/>
        &lt;inc-compile tag="20">
            &lt;dir-changes updated="Main" 
                         removed="DeleteMe"
                       unchanged="Unchanged"/>
        &lt;/inc-compile>
    &lt;/ajc-test>
</pre>
It checks after a recompile that
<ul>
  <li><code>Main.class</code> was updated</li>
  <li><code>DeleteMe.class</code> was deleted</li>
  <li><code>Unchanged.class</code> was not touched</li>
</ul>

<a name="tester"></a>
<h5>Runtime behavior</h5>
Code that tests aspects often falls into the pattern of comparing
expected and actual events/signals.  For example, to prove that
before advice in fact ran before a particular method execution,
you might generated and expect signals corresponding to

<ol>
  <li>method-call</li>
  <li>before advice execution</li>
  <li>method-execution</li>
</ol>

The <code>Tester</code> 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:
<pre>
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");
    }
}
</pre>

If either the advice or the method does not run,
the harness will report a failure.

<p>
<code>Tester</code> also has methods that operate like 
JUnit assertions as idioms to detect differences in
expected and actual values, signalling appropriately.
<p>

<code>Tester</code> is at
  <a href="../testing-client/src/org/aspectj/testing/Tester.java">
  ../testing-client/src/org/aspectj/testing/Tester.java</a>
and is built into 
  <a href="../lib/tests/testing-client.jar">
  ../lib/tests/testing-client.jar</a>
which is included on the classpath by the compile and run steps.

<p>You can write runtime test cases without using Tester;
simply throw some exception from the main thread to signal failure.

<a name="compilerOptions"></a>
<h4>Compiler options</h4>
The harness does not support all of the AspectJ 1.1 compiler options.
Flags are mainly supported through the a comma-delimited list in
the <code>options</code> attribute:
<pre>
    &lt;ajc-test dir="new" title="lint test">
        &lt;compile files="LintTest.java" 
                 options="-Xlint,-emacssym,-source,1.4">
            &lt;message kind="warning" line="22">
        &lt;/compile>
</pre>
This should work even for complex single-arg options like
<code>-g:none</code>, but will fail for comma-delimited single-arg options like
<code>-g:lines,vars</code> because the comma delimiters
are ambiguous (yes, a design bug!).
<p>
The <code>compile</code> element has the following attributes
which handle most of the other compiler arguments:
<ul>
        <li><code>files</code>: .aj and .java files are treated as source files,
            but .jar/zip files are extracted and passed to the compiler
            as <code>-injars</code>
            and readable directories are passed as <code>-inpath</code>.
   </li><li><code>classpath</code>: directories and jar files for the classpath
   </li><li><code>aspectpath</code>: binary aspects in jar files
   </li><li><code>argfiles</code>: argument list files
   </li><li><code>sourceroots</code>: root directories for source files
   </li>
</ul>
Paths for these are all relative to the test base directory, and 
multiple entries are separated with commas.
<p>
Here is a cooked example that uses all <code>compiler</code> attributes:
<pre>
    &lt;ajc-test dir="new" title="attributes test">
        &lt;compile files="Main.java,injar.jar,some-directory" 
               staging="true"
               options="-Xlint,-g:none"
              argfiles="debug.lst,aspects/test.lst"
            aspectpath="jars/requiredAspects.jar"
             classpath="providedClassesDir,jars/required.jar"/>
        &lt;inc-compile tag="20"/>
    &lt;/ajc-test>
</pre>

<h5>Test-only compiler attributes</h5>
The following attributes of the compiler entity dictate harness behavior:

<ul>
<li><u>badInput</u>:
To test invalid input, set the compiler <code>badInput</code> attribute
to "</code>true</code>".  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.)
</li>


<li><u>includeClassesDir</u>:
Set this in order to include the output classes directory explicitly
on the classpath.
</li>
<li><u>reuseCompiler</u>:
Set this to re-use a compiler from a previous compiler run.
(This tests statefulness of a compiler across non-incremental runs.)
</li>
</ul>

<h5>Unsupported compiler options</h5>
The harness does not support the following AspectJ compiler
options: <code>-outjar {file}, -log {file}</code>.
(<code>-d {dir}</code> is used but specification is not supported.)

 

<a name="background"></a>
<h4>Background information on the Harness</h4>
To make the test specifications as terse as possible,
harness components for
 <code>inc-compile</code> and <code>run</code> elements
use information set up earlier by <code>compile</code>,
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:
<ul>
  <li>Aside from any explicit classpath entries,
      <code>compile</code> always includes the jars
      <a href="../lib/tests/aspecjrt.jar">
      ../lib/tests/aspecjrt.jar</a> and 
      <a href="../lib/tests/testing-client.jar">
      ../lib/tests/testing-client.jar</a>
      on the compile classpath.
    </li>
  <li><code>run</code> sets up its classpath as the compile
      classpath plus the compile output (classes) directory
      plus any entries on the aspectpath.
    </li>
</ul>

The harness provides some more advance behaviors,
which you might see specified in the tests.
For more information, see the API documentation for the harness.
<ul>
   <li><u>option dominance and overriding</u>:
       Both in test specifications and on the command line
       you can force compiler options on or off.
       (Forcing means that, e.g., even if an option is enabled in the
        test specification, it can be disabled from the command line.)
       These appear in the test specifications as options
       with prefixes
       '!' or '^' rather than '-' (e.g., '^emacssym' to force
       the emacssym option off, even in tests that specify it).
     </li>
</ul>
<hr>
<small>last updated January 8, 2004 </small> <!-- CVS variable -->

</body>
</html>