source: release-kits/lirk3/bin/ant-installer/web/manual1.7.0/manual/tutorial-writing-tasks.html@ 14982

Last change on this file since 14982 was 14982, checked in by oranfry, 16 years ago

initial import of LiRK3

File size: 26.1 KB
Line 
1<!--
2 Licensed to the Apache Software Foundation (ASF) under one or more
3 contributor license agreements. See the NOTICE file distributed with
4 this work for additional information regarding copyright ownership.
5 The ASF licenses this file to You under the Apache License, Version 2.0
6 (the "License"); you may not use this file except in compliance with
7 the License. You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16-->
17<html>
18<head>
19 <title>Tutorial: Writing Tasks</title>
20 <meta name="author" content="Jan Matï¿œne">
21 <link rel="stylesheet" type="text/css" href="stylesheets/style.css">
22 <style type="text/css">
23 <!--
24 .code { background: #EFEFEF; margin-top: }
25 .output { color: #FFFFFF; background: #837A67; }
26 -->
27 </style>
28</head>
29<body>
30<h1>Tutorial: Writing Tasks</h1>
31
32<p>This document provides a step by step tutorial for writing
33tasks.</p>
34<h2>Content</h2>
35<p><ul>
36<li><a href="#buildenvironment">Set up the build environment</a></li>
37<li><a href="#write1">Write the Task</a></li>
38<li><a href="#use1">Use the Task</a></li>
39<li><a href="#TaskAdapter">Integration with TaskAdapter</a></li>
40<li><a href="#derivingFromTask">Deriving from Ant's Task</a></li>
41<li><a href="#attributes">Attributes</a></li>
42<li><a href="#NestedText">Nested Text</a></li>
43<li><a href="#NestedElements">Nested Elements</a></li>
44<li><a href="#complex">Our task in a little more complex version</a></li>
45<li><a href="#TestingTasks">Test the Task</a></li>
46<li><a href="#resources">Resources</a></li>
47</ul></p>
48
49<a name="buildenvironment"></a>
50<h2>Set up the build environment</h2>
51<p>Ant builds itself, we are using Ant too (why we would write
52a task if not? :-) therefore we should use Ant for our build.<p>
53<p>We choose a directory as root directory. All things will be done
54here if I say nothing different. I will reference this directory
55as <i>root-directory</i> of our project. In this root-directory we
56create a text file names <i>build.xml</i>. What should Ant do for us?
57<ul>
58<li>compiles my stuff</li>
59<li>make the jar, so that I can deploy it</li>
60<li>clean up everything</li>
61</ul>
62So the buildfile contains three targets.
63<pre class="code">
64&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;
65&lt;project name="MyTask" basedir="." default="jar"&gt;
66
67 &lt;target name="clean" description="Delete all generated files"&gt;
68 &lt;delete dir="classes"/&gt;
69 &lt;delete file="MyTasks.jar"/&gt;
70 &lt;/target&gt;
71
72 &lt;target name="compile" description="Compiles the Task"&gt;
73 &lt;javac srcdir="src" destdir="classes"/&gt;
74 &lt;/target&gt;
75
76 &lt;target name="jar" description="JARs the Task"&gt;
77 &lt;jar destfile="MyTask.jar" basedir="classes"/&gt;
78 &lt;/target&gt;
79
80&lt;/project&gt;
81</pre>
82
83This buildfile uses often the same value (src, classes, MyTask.jar), so we should rewrite that
84using <code>&lt;property&gt;</code>s. On second there are some handicaps: <code>&lt;javac&gt;</code> requires that the destination
85directory exists; a call of "clean" with a non existing classes directory will fail; "jar" requires
86the execution of some steps bofore. So the refactored code is:
87
88<pre class="code">
89&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;
90&lt;project name="MyTask" basedir="." default="jar"&gt;
91
92 <b>&lt;property name="src.dir" value="src"/&gt;</b>
93 <b>&lt;property name="classes.dir" value="classes"/&gt;</b>
94
95 &lt;target name="clean" description="Delete all generated files"&gt;
96 &lt;delete dir="<b>${classes.dir}</b>" <b>failonerror="false"</b>/&gt;
97 &lt;delete file="<b>${ant.project.name}.jar</b>"/&gt;
98 &lt;/target&gt;
99
100 &lt;target name="compile" description="Compiles the Task"&gt;
101 <b>&lt;mkdir dir="${classes.dir}"/&gt;</b>
102 &lt;javac srcdir="<b>${src.dir}</b>" destdir="${classes.dir}"/&gt;
103 &lt;/target&gt;
104
105 &lt;target name="jar" description="JARs the Task" <b>depends="compile"</b>&gt;
106 &lt;jar destfile="${ant.project.name}.jar" basedir="${classes.dir}"/&gt;
107 &lt;/target&gt;
108
109&lt;/project&gt;
110</pre>
111<i>ant.project.name</i> is one of the
112<a href="http://ant.apache.org/manual/using.html#built-in-props" target="_blank">
113build-in properties [1]</a> of Ant.
114
115
116<a name="write1"></a>
117<h2>Write the Task</h2>
118
119Now we write the simplest Task - a HelloWorld-Task (what else?). Create a text file
120<i>HelloWorld.java</i> in the src-directory with:
121<pre class="code">
122public class HelloWorld {
123 public void execute() {
124 System.out.println("Hello World");
125 }
126}
127</pre>
128and we can compile and jar it with <tt>ant</tt> (default target is "jar" and via
129its <i>depends</i>-clause the "compile" is executed before).
130
131
132
133<a name="use1"></a>
134<h2>Use the Task</h2>
135<p>But after creating the jar we want to use our new Task. Therefore we need a
136new target "use". Before we can use our new task we have to declare it with
137<a href="http://ant.apache.org/manual/CoreTasks/taskdef.html" target="_blank">
138<code>&lt;taskdef&gt;</code> [2]</a>. And for easier process we change the default clause:
139<pre class="code">
140&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;
141&lt;project name="MyTask" basedir="." default="<b>use</b>"&gt;
142
143 ...
144
145 <b>&lt;target name="use" description="Use the Task" depends="jar"&gt;
146 &lt;taskdef name="helloworld" classname="HelloWorld" classpath="${ant.project.name}.jar"/&gt;
147 &lt;helloworld/&gt;
148 &lt;/target&gt;</b>
149
150&lt;/project&gt;
151</pre>
152
153Important is the <i>classpath</i>-attribute. Ant searches in its /lib directory for
154tasks and our task isn't there. So we have to provide the right location. </p>
155
156<p>Now we can type in <tt>ant</tt> and all should work ...
157<pre class="output">
158Buildfile: build.xml
159
160compile:
161 [mkdir] Created dir: C:\tmp\anttests\MyFirstTask\classes
162 [javac] Compiling 1 source file to C:\tmp\anttests\MyFirstTask\classes
163
164jar:
165 [jar] Building jar: C:\tmp\anttests\MyFirstTask\MyTask.jar
166
167use:
168[helloworld] Hello World
169
170BUILD SUCCESSFUL
171Total time: 3 seconds
172</pre>
173
174
175
176<a name="TaskAdapter"></a>
177<h2>Integration with TaskAdapter</h2>
178<p>Our class has nothing to do with Ant. It extends no superclass and implements
179no interface. How does Ant know to integrate? Via name convention: our class provides
180a method with signature <tt>public void execute()</tt>. This class is wrapped by Ant's
181<tt>org.apache.tools.ant.TaskAdapter</tt> which is a task and uses reflection for
182setting a reference to the project and calling the <i>execute()</i> method.</p>
183
184<p><i>Setting a reference to the project</i>? Could be interesting. The Project class
185gives us some nice abilities: access to Ant's logging facilities getting and setting
186properties and much more. So we try to use that class:
187<pre class="code">
188import org.apache.tools.ant.Project;
189
190public class HelloWorld {
191
192 private Project project;
193
194 public void setProject(Project proj) {
195 project = proj;
196 }
197
198 public void execute() {
199 String message = project.getProperty("ant.project.name");
200 project.log("Here is project '" + message + "'.", Project.MSG_INFO);
201 }
202}
203</pre>
204and the execution with <tt>ant</tt> will show us the expected
205<pre class="output">
206use:
207Here is project 'MyTask'.
208</pre></p>
209
210
211<a name="derivingFromTask"></a>
212<h2>Deriving from Ant's Task</h2>
213<p>Ok, that works ... But usually you will extend <tt>org.apache.tools.ant.Task</tt>.
214That class is integrated in Ant, get's the project-reference, provides documentation
215fiels, provides easier access to the logging facility and (very useful) gives you
216the exact location where <i>in the buildfile</i> this task instance is used.</p>
217
218<p>Oki-doki - let's us use some of these:
219<pre class="code">
220import org.apache.tools.ant.Task;
221
222public class HelloWorld extends Task {
223 public void execute() {
224 // use of the reference to Project-instance
225 String message = getProject().getProperty("ant.project.name");
226
227 // Task's log method
228 log("Here is project '" + message + "'.");
229
230 // where this task is used?
231 log("I am used in: " + getLocation() );
232 }
233}
234</pre>
235which gives us when running
236<pre class="output">
237use:
238[helloworld] Here is project 'MyTask'.
239[helloworld] I am used in: C:\tmp\anttests\MyFirstTask\build.xml:23:
240</pre>
241
242
243<a name="attributes"></a>
244<h2>Attributes</h2>
245<p>Now we want to specify the text of our message (it seems that we are
246rewriting the <code>&lt;echo/&gt;</code> task :-). First we well do that with an attribute.
247It is very easy - for each attribute provide a <tt>public void set<code>&lt;attributename&gt;</code>(<code>&lt;type&gt;</code>
248newValue)</tt> method and Ant will do the rest via reflection.</p>
249<pre class="code">
250import org.apache.tools.ant.Task;
251import org.apache.tools.ant.BuildException;
252
253public class HelloWorld extends Task {
254
255 String message;
256 public void setMessage(String msg) {
257 message = msg;
258 }
259
260 public void execute() {
261 if (message==null) {
262 throw new BuildException("No message set.");
263 }
264 log(message);
265 }
266
267}
268</pre>
269<p>Oh, what's that in execute()? Throw a <i>BuildException</i>? Yes, that's the usual
270way to show Ant that something important is missed and complete build should fail. The
271string provided there is written as build-failes-message. Here it's necessary because
272the log() method can't handle a <i>null</i> value as parameter and throws a NullPointerException.
273(Of course you can initialize the <i>message</i> with a default string.)</p>
274
275<p>After that we have to modify our buildfile:
276<pre class="code">
277 &lt;target name="use" description="Use the Task" depends="jar"&gt;
278 &lt;taskdef name="helloworld"
279 classname="HelloWorld"
280 classpath="${ant.project.name}.jar"/&gt;
281 &lt;helloworld <b>message="Hello World"</b>/&gt;
282 &lt;/target&gt;
283</pre>
284That's all.</p>
285
286<p>Some background for working with attributes: Ant supports any of these datatypes as
287arguments of the set-method:<ul>
288<li>elementary data type like <i>int</i>, <i>long</i>, ...</li>
289<li>its wrapper classes like <i>java.lang.Integer</i>, <i>java.lang.Long</i>, ...</li>
290<li><i>java.lang.String</i></li>
291<li>some more classes (e.g. <i>java.io.File</i>; see
292 <a href="http://ant.apache.org/manual/develop.html#set-magic">Manual
293 'Writing Your Own Task' [3]</a>)</li>
294</ul>
295Before calling the set-method all properties are resolved. So a <tt>&lt;helloworld message="${msg}"/&gt;</tt>
296would not set the message string to "${msg}" if there is a property "msg" with a set value.
297
298
299<a name="NestedText"></a>
300<h2>Nested Text</h2>
301<p>Maybe you have used the <code>&lt;echo&gt;</code> task in a way like <tt>&lt;echo&gt;Hello World&lt;/echo&gt;</tt>.
302For that you have to provide a <tt>public void addText(String text)</tt> method.
303<pre class="code">
304...
305public class HelloWorld extends Task {
306 ...
307 public void addText(String text) {
308 message = text;
309 }
310 ...
311}
312</pre>
313But here properties are <b>not</b> resolved! For resolving properties we have to use
314Project's <tt>replaceProperties(String propname) : String</tt> method which takes the
315property name as argument and returns its value (or ${propname} if not set).</p>
316
317
318<a name="NestedElements"></a>
319<h2>Nested Elements</h2>
320<p>There are several ways for inserting the ability of handling nested elements. See
321the <a href="http://ant.apache.org/manual/develop.html#nested-elements">Manual [4]</a> for other.
322We use the first way of the three described ways. There are several steps for that:<ol>
323<li>We create a class for collecting all the infos the nested element should contain.
324 This class is created by the same rules for attributes and nested elements
325 as for the task (<code>set&lt;attributename&gt;</code>() methods). </li>
326<li>The task holds multiple instances of this class in a list.</li>
327<li>A factory method instantiates an object, saves the reference in the list
328 and returns it to Ant Core.</li>
329<li>The execute() method iterates over the list and evaluates its values.</li>
330</li></ol></p>
331<pre class="code">
332import java.util.Vector;
333import java.util.Iterator;
334...
335 public void execute() {
336 if (message!=null) log(message);
337 for (Iterator it=messages.iterator(); it.hasNext(); ) { <b>// 4</b>
338 Message msg = (Message)it.next();
339 log(msg.getMsg());
340 }
341 }
342
343
344 Vector messages = new Vector(); <b>// 2</b>
345
346 public Message createMessage() { <b>// 3</b>
347 Message msg = new Message();
348 messages.add(msg);
349 return msg;
350 }
351
352 public class Message { <b>// 1</b>
353 public Message() {}
354
355 String msg;
356 public void setMsg(String msg) { this.msg = msg; }
357 public String getMsg() { return msg; }
358 }
359...
360</pre>
361<p>Then we can use the new nested element. But where is xml-name for that defined?
362The mapping XML-name : classname is defined in the factory method:
363<tt>public <i>classname</i> create<i>XML-name</i>()</tt>. Therefore we write in
364the buildfile
365<pre class="code">
366 &lt;helloworld&gt;
367 &lt;message msg="Nested Element 1"/&gt;
368 &lt;message msg="Nested Element 2"/&gt;
369 &lt;/helloworld&gt;
370</pre>
371
372
373<a name="complex"></a>
374<h2>Our task in a little more complex version</h2>
375<p>For recapitulation now a little refactored buildfile:
376<pre class="code">
377&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;
378&lt;project name="MyTask" basedir="." default="use"&gt;
379
380 &lt;property name="src.dir" value="src"/&gt;
381 &lt;property name="classes.dir" value="classes"/&gt;
382
383 &lt;target name="clean" description="Delete all generated files"&gt;
384 &lt;delete dir="${classes.dir}" failonerror="false"/&gt;
385 &lt;delete file="${ant.project.name}.jar"/&gt;
386 &lt;/target&gt;
387
388 &lt;target name="compile" description="Compiles the Task"&gt;
389 &lt;mkdir dir="${classes.dir}"/&gt;
390 &lt;javac srcdir="${src.dir}" destdir="${classes.dir}"/&gt;
391 &lt;/target&gt;
392
393 &lt;target name="jar" description="JARs the Task" depends="compile"&gt;
394 &lt;jar destfile="${ant.project.name}.jar" basedir="${classes.dir}"/&gt;
395 &lt;/target&gt;
396
397
398 &lt;target name="use.init"
399 description="Taskdef the HelloWorld-Task"
400 depends="jar"&gt;
401 &lt;taskdef name="helloworld"
402 classname="HelloWorld"
403 classpath="${ant.project.name}.jar"/&gt;
404 &lt;/target&gt;
405
406
407 &lt;target name="use.without"
408 description="Use without any"
409 depends="use.init"&gt;
410 &lt;helloworld/&gt;
411 &lt;/target&gt;
412
413 &lt;target name="use.message"
414 description="Use with attribute 'message'"
415 depends="use.init"&gt;
416 &lt;helloworld message="attribute-text"/&gt;
417 &lt;/target&gt;
418
419 &lt;target name="use.fail"
420 description="Use with attribute 'fail'"
421 depends="use.init"&gt;
422 &lt;helloworld fail="true"/&gt;
423 &lt;/target&gt;
424
425 &lt;target name="use.nestedText"
426 description="Use with nested text"
427 depends="use.init"&gt;
428 &lt;helloworld&gt;nested-text&lt;/helloworld&gt;
429 &lt;/target&gt;
430
431 &lt;target name="use.nestedElement"
432 description="Use with nested 'message'"
433 depends="use.init"&gt;
434 &lt;helloworld&gt;
435 &lt;message msg="Nested Element 1"/&gt;
436 &lt;message msg="Nested Element 2"/&gt;
437 &lt;/helloworld&gt;
438 &lt;/target&gt;
439
440
441 &lt;target name="use"
442 description="Try all (w/out use.fail)"
443 depends="use.without,use.message,use.nestedText,use.nestedElement"
444 /&gt;
445
446&lt;/project&gt;
447</pre>
448
449And the code of the task:
450<pre class="code">
451import org.apache.tools.ant.Task;
452import org.apache.tools.ant.BuildException;
453import java.util.Vector;
454import java.util.Iterator;
455
456/**
457 * The task of the tutorial.
458 * Print a message or let the build fail.
459 * @author Jan Matï¿œne
460 * @since 2003-08-19
461 */
462public class HelloWorld extends Task {
463
464
465 /** The message to print. As attribute. */
466 String message;
467 public void setMessage(String msg) {
468 message = msg;
469 }
470
471 /** Should the build fail? Defaults to <i>false</i>. As attribute. */
472 boolean fail = false;
473 public void setFail(boolean b) {
474 fail = b;
475 }
476
477 /** Support for nested text. */
478 public void addText(String text) {
479 message = text;
480 }
481
482
483 /** Do the work. */
484 public void execute() {
485 // handle attribute 'fail'
486 if (fail) throw new BuildException("Fail requested.");
487
488 // handle attribute 'message' and nested text
489 if (message!=null) log(message);
490
491 // handle nested elements
492 for (Iterator it=messages.iterator(); it.hasNext(); ) {
493 Message msg = (Message)it.next();
494 log(msg.getMsg());
495 }
496 }
497
498
499 /** Store nested 'message's. */
500 Vector messages = new Vector();
501
502 /** Factory method for creating nested 'message's. */
503 public Message createMessage() {
504 Message msg = new Message();
505 messages.add(msg);
506 return msg;
507 }
508
509 /** A nested 'message'. */
510 public class Message {
511 // Bean constructor
512 public Message() {}
513
514 /** Message to print. */
515 String msg;
516 public void setMsg(String msg) { this.msg = msg; }
517 public String getMsg() { return msg; }
518 }
519
520}
521</pre>
522
523And it works:
524<pre class="output">
525C:\tmp\anttests\MyFirstTask&gt;ant
526Buildfile: build.xml
527
528compile:
529 [mkdir] Created dir: C:\tmp\anttests\MyFirstTask\classes
530 [javac] Compiling 1 source file to C:\tmp\anttests\MyFirstTask\classes
531
532jar:
533 [jar] Building jar: C:\tmp\anttests\MyFirstTask\MyTask.jar
534
535use.init:
536
537use.without:
538
539use.message:
540[helloworld] attribute-text
541
542use.nestedText:
543[helloworld] nested-text
544
545use.nestedElement:
546[helloworld]
547[helloworld]
548[helloworld]
549[helloworld]
550[helloworld] Nested Element 1
551[helloworld] Nested Element 2
552
553use:
554
555BUILD SUCCESSFUL
556Total time: 3 seconds
557C:\tmp\anttests\MyFirstTask&gt;ant use.fail
558Buildfile: build.xml
559
560compile:
561
562jar:
563
564use.init:
565
566use.fail:
567
568BUILD FAILED
569C:\tmp\anttests\MyFirstTask\build.xml:36: Fail requested.
570
571Total time: 1 second
572C:\tmp\anttests\MyFirstTask&gt;
573</pre>
574Next step: test ...
575
576
577
578<a name="TestingTasks"></a>
579<h2>Test the Task</h2>
580<p>We have written a test already: the use.* tasks in the buildfile. But its
581difficult to test that automatically. Common (and in Ant) used is JUnit for
582that. For testing tasks Ant provides a baseclass <tt>org.apache.tools.ant.BuildFileTest</tt>.
583This class extends <tt>junit.framework.TestCase</tt> and can therefore be integrated
584into the unit tests. But this class provides some for testing tasks useful methods:
585initialize Ant, load a buildfile, execute targets,
586expecting BuildExceptions with a specified text, expect a special text
587in the output log ... </p>
588
589<p>In Ant it is usual that the testcase has the same name as the task with a prepending
590<i>Test</i>, therefore we will create a file <i>HelloWorldTest.java</i>. Because we
591have a very small project we can put this file into <i>src</i> directory (Ant's own
592testclasses are in /src/testcases/...). Because we have already written our tests
593for "hand-test" we can use that for automatic tests, too. But there is one little
594problem we have to solve: all test supporting classes are not part of the binary
595distribution of Ant. So you can build the special jar file from source distro with
596target "test-jar" or you can download a nightly build from
597<a href="http://gump.covalent.net/jars/latest/ant/ant-testutil.jar">
598http://gump.covalent.net/jars/latest/ant/ant-testutil.jar [5]</a>.</p>
599
600<p>For executing the test and creating a report we need the optional tasks <code>&lt;junit&gt;</code>
601and <code>&lt;junitreport&gt;</code>. So we add to the buildfile:
602<pre class="code">
603...
604<font color="#9F9F9F">&lt;project name="MyTask" basedir="." </font>default="test"<font color="#9F9F9F">&gt;</font>
605...
606 &lt;property name="ant.test.lib" value="ant-testutil.jar"/&gt;
607 &lt;property name="report.dir" value="report"/&gt;
608 &lt;property name="junit.out.dir.xml" value="${report.dir}/junit/xml"/&gt;
609 &lt;property name="junit.out.dir.html" value="${report.dir}/junit/html"/&gt;
610
611 &lt;path id="classpath.run"&gt;
612 &lt;path path="${java.class.path}"/&gt;
613 &lt;path location="${ant.project.name}.jar"/&gt;
614 &lt;/path&gt;
615
616 &lt;path id="classpath.test"&gt;
617 &lt;path refid="classpath.run"/&gt;
618 &lt;path location="${ant.test.lib}"/&gt;
619 &lt;/path&gt;
620
621 &lt;target name="clean" description="Delete all generated files"&gt;
622 &lt;delete failonerror="false" includeEmptyDirs="true"&gt;
623 &lt;fileset dir="." includes="${ant.project.name}.jar"/&gt;
624 &lt;fileset dir="${classes.dir}"/&gt;
625 &lt;fileset dir="${report.dir}"/&gt;
626 &lt;/delete&gt;
627 &lt;/target&gt;
628
629 <font color="#9F9F9F">&lt;target name="compile" description="Compiles the Task"&gt;
630 &lt;mkdir dir="${classes.dir}"/&gt;
631 &lt;javac srcdir="${src.dir}" destdir="${classes.dir}" </font>classpath="${ant.test.lib}"<font color="#9F9F9F">/&gt;
632 &lt;/target&gt;</font>
633...
634 &lt;target name="junit" description="Runs the unit tests" depends="jar"&gt;
635 &lt;delete dir="${junit.out.dir.xml}"/&gt;
636 &lt;mkdir dir="${junit.out.dir.xml}"/&gt;
637 &lt;junit printsummary="yes" haltonfailure="no"&gt;
638 &lt;classpath refid="classpath.test"/&gt;
639 &lt;formatter type="xml"/&gt;
640 &lt;batchtest fork="yes" todir="${junit.out.dir.xml}"&gt;
641 &lt;fileset dir="${src.dir}" includes="**/*Test.java"/&gt;
642 &lt;/batchtest&gt;
643 &lt;/junit&gt;
644 &lt;/target&gt;
645
646 &lt;target name="junitreport" description="Create a report for the rest result"&gt;
647 &lt;mkdir dir="${junit.out.dir.html}"/&gt;
648 &lt;junitreport todir="${junit.out.dir.html}"&gt;
649 &lt;fileset dir="${junit.out.dir.xml}"&gt;
650 &lt;include name="*.xml"/&gt;
651 &lt;/fileset&gt;
652 &lt;report format="frames" todir="${junit.out.dir.html}"/&gt;
653 &lt;/junitreport&gt;
654 &lt;/target&gt;
655
656 &lt;target name="test"
657 depends="junit,junitreport"
658 description="Runs unit tests and creates a report"
659 /&gt;
660...
661</pre></p>
662
663<p>Back to the <i>src/HelloWorldTest.java</i>. We create a class extending
664<i>BuildFileTest</i> with String-constructor (JUnit-standard), a <i>setUp()</i>
665method initializing Ant and for each testcase (targets use.*) a <i>testXX()</i>
666method invoking that target.
667<pre class="code">
668import org.apache.tools.ant.BuildFileTest;
669
670public class HelloWorldTest extends BuildFileTest {
671
672 public HelloWorldTest(String s) {
673 super(s);
674 }
675
676 public void setUp() {
677 // initialize Ant
678 configureProject("build.xml");
679 }
680
681 public void testWithout() {
682 executeTarget("use.without");
683 assertEquals("Message was logged but should not.", getLog(), "");
684 }
685
686 public void testMessage() {
687 // execute target 'use.nestedText' and expect a message
688 // 'attribute-text' in the log
689 expectLog("use.message", "attribute-text");
690 }
691
692 public void testFail() {
693 // execute target 'use.fail' and expect a BuildException
694 // with text 'Fail requested.'
695 expectBuildException("use.fail", "Fail requested.");
696 }
697
698 public void testNestedText() {
699 expectLog("use.nestedText", "nested-text");
700 }
701
702 public void testNestedElement() {
703 executeTarget("use.nestedElement");
704 assertLogContaining("Nested Element 1");
705 assertLogContaining("Nested Element 2");
706 }
707}
708</pre></p>
709
710<p>When starting <tt>ant</tt> we'll get a short message to STDOUT and
711a nice HTML-report.
712<pre class="output">
713C:\tmp\anttests\MyFirstTask&gt;ant
714Buildfile: build.xml
715
716compile:
717 [mkdir] Created dir: C:\tmp\anttests\MyFirstTask\classes
718 [javac] Compiling 2 source files to C:\tmp\anttests\MyFirstTask\classes
719
720jar:
721 [jar] Building jar: C:\tmp\anttests\MyFirstTask\MyTask.jar
722
723junit:
724 [mkdir] Created dir: C:\tmp\anttests\MyFirstTask\report\junit\xml
725 [junit] Running HelloWorldTest
726 [junit] Tests run: 5, Failures: 0, Errors: 0, Time elapsed: 2,334 sec
727
728
729
730junitreport:
731 [mkdir] Created dir: C:\tmp\anttests\MyFirstTask\report\junit\html
732[junitreport] Using Xalan version: Xalan Java 2.4.1
733[junitreport] Transform time: 661ms
734
735test:
736
737BUILD SUCCESSFUL
738Total time: 7 seconds
739C:\tmp\anttests\MyFirstTask&gt;
740</pre></p>
741
742
743<a name="resources"></a>
744<h2>Resources</h2>
745<p>This tutorial and its resources are available via
746<a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=22570">BugZilla [6]</a>.
747The ZIP provided there contains<ul>
748<li>this tutorial</li>
749<li>the buildfile (last version)</li>
750<li>the source of the task (last version)</li>
751<li>the source of the unit test (last version)</li>
752<li>the ant-testutil.jar (nightly build of 2003-08-18)</li>
753<li>generated classes</li>
754<li>generated jar</li>
755<li>generated reports</li>
756</ul>
757The last sources and the buildfile are also available
758<a href="tutorial-writing-tasks-src.zip">here [7]</a> inside the manual.
759</p>
760
761
762Used Links:<br>
763&nbsp;&nbsp;[1] <a href="http://ant.apache.org/manual/using.html#built-in-props">http://ant.apache.org/manual/using.html#built-in-props</a><br>
764&nbsp;&nbsp;[2] <a href="http://ant.apache.org/manual/CoreTasks/taskdef.html">http://ant.apache.org/manual/CoreTasks/taskdef.html</a><br>
765&nbsp;&nbsp;[3] <a href="http://ant.apache.org/manual/develop.html#set-magic">http://ant.apache.org/manual/develop.html#set-magic</a><br>
766&nbsp;&nbsp;[4] <a href="http://ant.apache.org/manual/develop.html#nested-elements">http://ant.apache.org/manual/develop.html#nested-elements</a><br>
767&nbsp;&nbsp;[5] <a href="http://gump.covalent.net/jars/latest/ant/ant-testutil.jar">http://gump.covalent.net/jars/latest/ant/ant-testutil.jar</a><br>
768&nbsp;&nbsp;[6] <a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=22570">http://issues.apache.org/bugzilla/show_bug.cgi?id=22570</a><br>
769&nbsp;&nbsp;[7] <a href="tutorial-writing-tasks-src.zip">tutorial-writing-tasks-src.zip</a><br>
770
771
772
773</body>
774</html>
Note: See TracBrowser for help on using the repository browser.