001 // Copyright 2005-2006 Ferdinand Prantl <prantl@users.sourceforge.net>
002 // Copyright 2001-2004 The Apache Software Foundation
003 // All rights reserved.
004 //
005 // Licensed under the Apache License, Version 2.0 (the "License");
006 // you may not use this file except in compliance with the License.
007 // You may obtain a copy of the License at
008 //
009 // http://www.apache.org/licenses/LICENSE-2.0
010 //
011 // Unless required by applicable law or agreed to in writing, software
012 // distributed under the License is distributed on an "AS IS" BASIS,
013 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 // See the License for the specific language governing permissions and
015 // limitations under the License.
016 //
017 // See http://ant-eclipse.sourceforge.net for the most recent version
018 // and more information.
019
020 package prantl.ant.eclipse;
021
022 import java.io.ByteArrayInputStream;
023 import java.io.ByteArrayOutputStream;
024 import java.io.IOException;
025 import java.io.InputStream;
026 import java.io.InputStreamReader;
027 import java.io.OutputStream;
028 import java.io.PrintStream;
029 import java.util.Arrays;
030 import java.util.HashSet;
031 import java.util.Hashtable;
032 import java.util.StringTokenizer;
033
034 import junit.framework.TestCase;
035
036 import org.apache.tools.ant.BuildEvent;
037 import org.apache.tools.ant.BuildListener;
038 import org.apache.tools.ant.Project;
039
040 /**
041 * Test fixture with unit test cases for the class <tt>EclipseTask</tt>.
042 *
043 * @see EclipseTask
044 * @since Ant-Eclipse 1.0
045 * @author Ferdinand Prantl <prantl@users.sourceforge.net>
046 */
047 public class EclipseTaskTest extends TestCase {
048
049 /**
050 * Creates a new instance of the test fixture. Default constructor.
051 *
052 * @since Ant-Eclipse 1.0
053 */
054 public EclipseTaskTest() {
055 }
056
057 /**
058 * Helper class constructing the task object for the class <tt>EclipseTask</tt> with
059 * the minimum environment simulating an ant-build file with a single target with a
060 * single task.
061 *
062 * @see EclipseTask
063 * @since Ant-Eclipse 1.0
064 * @author Ferdinand Prantl <prantl@users.sourceforge.net>
065 */
066 static final class EclipseTaskTester extends EclipseTask {
067
068 /**
069 * Creates a new instance of the task with the minimum ant-like environment.
070 *
071 * @param object
072 * An object that the output is delegated into.
073 * @throws NullPointerException
074 * If the parameter <tt>object</tt> is null.
075 * @since Ant-Eclipse 1.0
076 */
077 EclipseTaskTester(EclipseOutput object) {
078 super(object);
079 setProject(new Project());
080 getProject().init();
081 getProject().setUserProperty("ant.project.name", "eclipse");
082 // setTaskType("eclipse");
083 // setTaskName("eclipse");
084 // setOwningTarget(new Target());
085 }
086
087 }
088
089 /**
090 * Testing output class performing output into in-memory streams.
091 *
092 * @see EclipseOutput
093 * @since Ant-Eclipse 1.0
094 * @author Ferdinand Prantl <prantl@users.sourceforge.net>
095 */
096 static final class MemoryEclipseOutput extends EclipseOutput {
097
098 private Hashtable settings = new Hashtable();
099
100 private ByteArrayOutputStream project = null;
101
102 private ByteArrayOutputStream classPath = null;
103
104 /**
105 * Creates a new instance of the output object.
106 *
107 * @param element
108 * An object containing the configuration.
109 * @see EclipseOutput#EclipseOutput(EclipseElement)
110 * @since Ant-Eclipse 1.0
111 */
112 MemoryEclipseOutput(EclipseElement element) {
113 super(element);
114 }
115
116 /**
117 * @see EclipseOutput#isPreferencesUpToDate(String)
118 * @since Ant-Eclipse 1.0
119 */
120 boolean isPreferencesUpToDate(String name) {
121 return false;
122 }
123
124 /**
125 * @see EclipseOutput#isProjectUpToDate()
126 * @since Ant-Eclipse 1.0
127 */
128 boolean isProjectUpToDate() {
129 return false;
130 }
131
132 /**
133 * @see EclipseOutput#isClassPathUpToDate()
134 * @since Ant-Eclipse 1.0
135 */
136 boolean isClassPathUpToDate() {
137 return false;
138 }
139
140 /**
141 * @see EclipseOutput#openPreferences(String)
142 * @since Ant-Eclipse 1.0
143 */
144 InputStream openPreferences(String name) {
145 ByteArrayOutputStream preferences = (ByteArrayOutputStream) settings
146 .get(name);
147 return preferences == null ? null : new ByteArrayInputStream(preferences
148 .toByteArray());
149 }
150
151 /**
152 * @see EclipseOutput#openProject()
153 * @since Ant-Eclipse 1.0
154 */
155 InputStream openProject() {
156 return project == null ? null : new ByteArrayInputStream(project
157 .toByteArray());
158 }
159
160 /**
161 * @see EclipseOutput#openClassPath()
162 * @since Ant-Eclipse 1.0
163 */
164 InputStream openClassPath() {
165 return classPath == null ? null : new ByteArrayInputStream(classPath
166 .toByteArray());
167 }
168
169 /**
170 * @see EclipseOutput#createPreferences(String)
171 * @since Ant-Eclipse 1.0
172 */
173 OutputStream createPreferences(String name) {
174 ByteArrayOutputStream preferences = new ByteArrayOutputStream();
175 settings.put(name, preferences);
176 return preferences;
177 }
178
179 /**
180 * @see EclipseOutput#createProject()
181 * @since Ant-Eclipse 1.0
182 */
183 OutputStream createProject() {
184 return project = new ByteArrayOutputStream();
185 }
186
187 /**
188 * @see EclipseOutput#createClassPath()
189 * @since Ant-Eclipse 1.0
190 */
191 OutputStream createClassPath() {
192 return classPath = new ByteArrayOutputStream();
193 }
194
195 }
196
197 /**
198 * Testing build listener collecting the log messages up to the specified level.
199 *
200 * @since Ant-Eclipse 1.0
201 * @author Ferdinand Prantl <prantl@users.sourceforge.net>
202 */
203 static final class MemoryLogListener implements BuildListener {
204
205 private StringBuffer log = new StringBuffer();
206
207 private int logLevel;
208
209 /**
210 * Constructs a test listener which will store the log events below or equal the
211 * given level.
212 *
213 * @param logLevel
214 * Maximum logLevel to watch.
215 */
216 public MemoryLogListener(int logLevel) {
217 this.logLevel = logLevel;
218 }
219
220 /**
221 * @return Collected log messages.
222 */
223 String getLog() {
224 return log.toString();
225 }
226
227 /**
228 * Fired before any targets are started.
229 *
230 * @param event
231 * Context information about the event.
232 */
233 public void buildStarted(BuildEvent event) {
234 }
235
236 /**
237 * Fired after the last target has finished. This event will still be thrown if an
238 * error occured during the build.
239 *
240 * @param event
241 * Context information about the event.
242 */
243 public void buildFinished(BuildEvent event) {
244 }
245
246 /**
247 * Fired when a target is started.
248 *
249 * @param event
250 * Context information about the event.
251 */
252 public void targetStarted(BuildEvent event) {
253 }
254
255 /**
256 * Fired when a target has finished. This event will still be thrown if an error
257 * occured during the build.
258 *
259 * @param event
260 * Context information about the event.
261 */
262 public void targetFinished(BuildEvent event) {
263 }
264
265 /**
266 * Fired when a task is started.
267 *
268 * @param event
269 * Context information about the event.
270 */
271 public void taskStarted(BuildEvent event) {
272 }
273
274 /**
275 * Fired when a task has finished. This event will still be thrown if an error
276 * occured during the build.
277 *
278 * @param event
279 * Context information about the event.
280 */
281 public void taskFinished(BuildEvent event) {
282 }
283
284 /**
285 * Fired whenever a message is logged.
286 *
287 * @param event
288 * Context information about the event.
289 */
290 public void messageLogged(BuildEvent event) {
291 if (event.getPriority() <= logLevel) {
292 log.append(levels[event.getPriority()]);
293 log.append(' ');
294 log.append(event.getMessage());
295 log.append('\n');
296 }
297 }
298
299 private static final String[] levels = new String[] { "ERROR ", "WARNING",
300 "INFO ", "VERBOSE", "DEBUG " };
301
302 }
303
304 /**
305 * Tests executing the task with the following configuration:
306 *
307 * <pre>
308 * <eclipse />
309 * </pre>
310 *
311 * @throws Exception
312 * If the task execution fails.
313 */
314 public void testExecuteWithEmptyEclipseElement() throws Exception {
315 MemoryEclipseOutput output = new MemoryEclipseOutput(new EclipseElement());
316 EclipseTaskTester task = new EclipseTaskTester(output);
317 MemoryLogListener logListener = new MemoryLogListener(Project.MSG_VERBOSE);
318 task.getProject().addBuildListener(logListener);
319
320 ByteArrayOutputStream out = new ByteArrayOutputStream();
321 ByteArrayOutputStream err = new ByteArrayOutputStream();
322 PrintStream systemOut = System.out;
323 PrintStream systemErr = System.err;
324 try {
325 systemOut.flush();
326 systemErr.flush();
327 System.setOut(new PrintStream(out));
328 System.setErr(new PrintStream(err));
329 task.execute();
330 } finally {
331 System.setOut(systemOut);
332 System.setErr(systemErr);
333 }
334
335 assertEquals(0, out.size());
336 assertEquals(0, err.size());
337 assertNull(output.openPreferences(OrgEclipseCoreResourcesPreferencesElement
338 .getPackageName()));
339 assertNull(output.openPreferences(OrgEclipseCoreRuntimePreferencesElement
340 .getPackageName()));
341 assertNull(output.openPreferences(OrgEclipseJdtCorePreferencesElement
342 .getPackageName()));
343 assertNull(output.openPreferences(OrgEclipseJdtUiPreferencesElement
344 .getPackageName()));
345 assertNull(output.openProject());
346 assertNull(output.openClassPath());
347 assertEquals("WARNING There were no settings found.\n"
348 + "WARNING There was no description of a project found.\n"
349 + "WARNING There was no description of a classpath found.\n", logListener
350 .getLog());
351 }
352
353 /**
354 * Tests executing the task with the following configuration:
355 *
356 * <pre>
357 * <eclipse>
358 * <settings>
359 * <resources version="2" encoding="UTF-8" />
360 * </settings>
361 * </eclipse>
362 * </pre>
363 *
364 * @throws Exception
365 * If the task execution fails.
366 */
367 public void testExecuteWithSettingsElementWithVersionAndEncoding() throws Exception {
368 EclipseElement eclipse = new EclipseElement();
369 SettingsElement settings = new SettingsElement();
370 OrgEclipseCoreResourcesPreferencesElement resources = settings.createResources();
371 resources.setVersion("2");
372 resources.setEncoding("UTF-8");
373 eclipse.setSettings(settings);
374
375 MemoryEclipseOutput output = new MemoryEclipseOutput(eclipse);
376 EclipseTaskTester task = new EclipseTaskTester(output);
377 MemoryLogListener logListener = new MemoryLogListener(Project.MSG_VERBOSE);
378 task.getProject().addBuildListener(logListener);
379 task.execute();
380
381 String settingsOutput = streamToString(output
382 .openPreferences(OrgEclipseCoreResourcesPreferencesElement
383 .getPackageName()));
384 // "#Fri Aug 19 07:58:50 CEST 2005\n"
385 assertTrue(settingsOutput.startsWith("#"));
386 assertTrue(settingsOutput.endsWith("\n"));
387 assertEqualAllLines("encoding/<project>=UTF-8\n"
388 + "eclipse.preferences.version=2\n", skipLine(settingsOutput));
389 assertNull(output.openPreferences(OrgEclipseCoreRuntimePreferencesElement
390 .getPackageName()));
391 assertNull(output.openPreferences(OrgEclipseJdtCorePreferencesElement
392 .getPackageName()));
393 assertNull(output.openPreferences(OrgEclipseJdtUiPreferencesElement
394 .getPackageName()));
395 assertNull(output.openProject());
396 assertNull(output.openClassPath());
397 assertEquals(
398 "INFO Writing the preferences for \"org.eclipse.core.resources\".\n"
399 + "WARNING There was no description of a project found.\n"
400 + "WARNING There was no description of a classpath found.\n",
401 logListener.getLog());
402 }
403
404 /**
405 * Tests executing the task with the following configuration:
406 *
407 * <pre>
408 * <eclipse>
409 * <project />
410 * </eclipse>
411 * </pre>
412 *
413 * @throws Exception
414 * If the task execution fails.
415 */
416 public void testExecuteWithEmptyProjectElement() throws Exception {
417 EclipseElement eclipse = new EclipseElement();
418 eclipse.setProject(new ProjectElement());
419
420 MemoryEclipseOutput output = new MemoryEclipseOutput(eclipse);
421 EclipseTaskTester task = new EclipseTaskTester(output);
422 MemoryLogListener logListener = new MemoryLogListener(Project.MSG_VERBOSE);
423 task.getProject().addBuildListener(logListener);
424 task.execute();
425
426 assertNull(output.openPreferences(OrgEclipseCoreResourcesPreferencesElement
427 .getPackageName()));
428 assertNull(output.openPreferences(OrgEclipseCoreRuntimePreferencesElement
429 .getPackageName()));
430 assertNull(output.openPreferences(OrgEclipseJdtCorePreferencesElement
431 .getPackageName()));
432 assertNull(output.openPreferences(OrgEclipseJdtUiPreferencesElement
433 .getPackageName()));
434 String projectOutput = streamToString(output.openProject());
435 assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
436 + "<projectDescription>\n" + " <name>eclipse</name>\n" + " <comment>\n"
437 + " </comment>\n" + " <projects>\n" + " </projects>\n"
438 + " <buildSpec>\n" + " <buildCommand>\n"
439 + " <name>org.eclipse.jdt.core.javabuilder</name>\n"
440 + " <arguments>\n" + " </arguments>\n"
441 + " </buildCommand>\n" + " </buildSpec>\n" + " <natures>\n"
442 + " <nature>org.eclipse.jdt.core.javanature</nature>\n"
443 + " </natures>\n" + "</projectDescription>", projectOutput);
444 assertNull(output.openClassPath());
445 assertEquals("WARNING There were no settings found.\n"
446 + "INFO Writing the project definition in the mode \"java\".\n"
447 + "VERBOSE Project name is \"eclipse\".\n"
448 + "WARNING There was no description of a classpath found.\n", logListener
449 .getLog());
450 }
451
452 /**
453 * Tests executing the task with the following configuration:
454 *
455 * <pre>
456 * <eclipse>
457 * <project />
458 * </eclipse>
459 * </pre>
460 *
461 * @throws Exception
462 * If the task execution fails.
463 */
464 public void testExecuteWithProjectElementWithName() throws Exception {
465 EclipseElement eclipse = new EclipseElement();
466 ProjectElement project = new ProjectElement();
467 project.setName("test");
468 eclipse.setProject(project);
469
470 MemoryEclipseOutput output = new MemoryEclipseOutput(eclipse);
471 EclipseTaskTester task = new EclipseTaskTester(output);
472 MemoryLogListener logListener = new MemoryLogListener(Project.MSG_VERBOSE);
473 task.getProject().addBuildListener(logListener);
474 task.execute();
475
476 assertNull(output.openPreferences(OrgEclipseCoreResourcesPreferencesElement
477 .getPackageName()));
478 assertNull(output.openPreferences(OrgEclipseCoreRuntimePreferencesElement
479 .getPackageName()));
480 assertNull(output.openPreferences(OrgEclipseJdtCorePreferencesElement
481 .getPackageName()));
482 assertNull(output.openPreferences(OrgEclipseJdtUiPreferencesElement
483 .getPackageName()));
484 String projectOutput = streamToString(output.openProject());
485 assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
486 + "<projectDescription>\n" + " <name>test</name>\n" + " <comment>\n"
487 + " </comment>\n" + " <projects>\n" + " </projects>\n"
488 + " <buildSpec>\n" + " <buildCommand>\n"
489 + " <name>org.eclipse.jdt.core.javabuilder</name>\n"
490 + " <arguments>\n" + " </arguments>\n"
491 + " </buildCommand>\n" + " </buildSpec>\n" + " <natures>\n"
492 + " <nature>org.eclipse.jdt.core.javanature</nature>\n"
493 + " </natures>\n" + "</projectDescription>", projectOutput);
494 assertNull(output.openClassPath());
495 assertEquals("WARNING There were no settings found.\n"
496 + "INFO Writing the project definition in the mode \"java\".\n"
497 + "VERBOSE Project name is \"test\".\n"
498 + "WARNING There was no description of a classpath found.\n", logListener
499 .getLog());
500 }
501
502 private String streamToString(InputStream input) throws IOException {
503 InputStreamReader reader = new InputStreamReader(input, "UTF-8");
504 StringBuffer content = new StringBuffer();
505 for (int ch; (ch = reader.read()) != -1;)
506 content.append((char) ch);
507 return content.toString();
508 }
509
510 private String skipLine(String input) {
511 int next = input.indexOf('\n');
512 return next >= 0 ? input.substring(next + 1) : "";
513 }
514
515 private void assertEqualAllLines(String expected, String actual) {
516 HashSet expectedLines = new HashSet(Arrays.asList(split(expected, "\n")));
517 String[] actualLines = split(actual, "\n");
518 for (int i = 0; i != actualLines.length; ++i)
519 if (!expectedLines.remove(actualLines[i]))
520 fail("The line " + (i + 1) + " (\"" + actualLines[i]
521 + ") from the actual content \"" + actual
522 + "\" does not match any in the expected content \"" + expected
523 + "\".");
524 int size = expectedLines.size();
525 if (size != 0)
526 fail(size + " line(s) ("
527 + join((String[]) expectedLines.toArray(new String[] {}), "\n")
528 + ") from the expected content \"" + expected
529 + "\" were missing in the actual content \"" + actual + "\".");
530 }
531
532 /**
533 * Splits the input string into an array of strings using the specified delimiter.
534 *
535 * @param input
536 * The input string to be splitted.
537 * @param delimiter
538 * The delimiting token.
539 * @throws NullPointerException
540 * If some of the input parameters are null.
541 * @return An array with strings as delimited parts of the input.
542 */
543 public static String[] split(String input, String delimiter) {
544 StringTokenizer tokenizer = new StringTokenizer(input, delimiter);
545 String[] result = new String[tokenizer.countTokens()];
546 for (int i = 0; tokenizer.hasMoreTokens(); ++i)
547 result[i] = tokenizer.nextToken();
548 return result;
549 }
550
551 /**
552 * Splits the input array into a string using the specified delimiter.
553 *
554 * @param input
555 * The input array to be joined.
556 * @param delimiter
557 * The delimiting token.
558 * @throws NullPointerException
559 * If some of the input parameters are null.
560 * @return An string joined with the specified delimiter from he input array.
561 */
562 public static String join(String[] input, String delimiter) {
563 StringBuffer result = new StringBuffer();
564 for (int i = 0; i != input.length; ++i) {
565 if (i != 0)
566 result.append(delimiter);
567 result.append(input[i]);
568 }
569 return result.toString();
570 }
571
572 }