001: /*
002: * @(#)BaseTestCase.java 1.13 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package gunit.framework;
028:
029: import java.awt.*;
030: import java.awt.image.*;
031: import java.io.*;
032: import java.net.*;
033: import java.util.*;
034: import java.lang.reflect.*;
035:
036: import junit.framework.AssertionFailedError;
037: import gunit.image.RefImageNotFoundException;
038:
039: /**
040: * <code>BaseTestCase</code> is an abstract class that extends JUnit's
041: * <code>TestCase</code> and contains methods that should run on PBP/PP.
042: * All profile and optional package specific functionality are defined
043: * as abstract methods.
044: * <p>
045: * <code>BaseTestCase</code> overrides setUp(), tearDown() and runTest()
046: * methods from the base class to provide gunit's Testcase behavioir.
047: */
048: public abstract class BaseTestCase extends junit.framework.TestCase {
049: private Container container;
050:
051: private String[] args;
052:
053: // Graphics object for the testcase.
054: private Graphics gc;
055:
056: // created only for Graphics testcases
057: private BufferedImage backBuffer;
058:
059: /**
060: * Indicates if the testcase asserted using any of the assert
061: * functions provided by gunit.
062: */
063: protected boolean isAsserted = false;
064:
065: private boolean isGraphicsTestcase = false;
066:
067: // A non-null value indicates that the testcase attempted to assert
068: // and the reference image was not found.
069: private String referenceImageFile = null;
070:
071: static TestContainer defaultTestContainer = null;
072: static String[] refImageDirs = null;
073: static TestArguments testArgs = null;
074: static TestFilter testFilter = null;
075: static TestResultVerifier resultVerifier = null;
076:
077: static {
078: TestContext context = TestContext.getInstance();
079: defaultTestContainer = (TestContainer) context
080: .getObjectValue(TestContext.TEST_CONTAINER_CLASS);
081: refImageDirs = getRefImageDirs(context
082: .getStringValue(TestContext.REF_IMAGE_PATH));
083: resultVerifier = (TestResultVerifier) context
084: .getObjectValue(TestContext.TEST_RESULT_VERIFIER_CLASS);
085: testArgs = new TestArguments();
086: testFilter = (TestFilter) context
087: .getValue(TestContext.TEST_FILTER);
088: if (testFilter == null) {
089: testFilter = new TestFilter() {
090: public boolean allowExecution(Class c, String method) {
091: return true;
092: }
093: };
094: }
095: }
096:
097: static String[] getRefImageDirs(String path) {
098: StringTokenizer st = new StringTokenizer(path, ":");
099: String[] dirs = new String[st.countTokens()];
100: for (int i = 0; st.hasMoreTokens(); i++) {
101: dirs[i] = st.nextToken();
102: }
103: return dirs;
104: }
105:
106: // junit.framework.TestCase
107: protected void setUp() {
108: if (!testFilter.allowExecution(getClass(), getName()))
109: return;
110:
111: this .isAsserted = false;
112: this .isGraphicsTestcase = false;
113: this .referenceImageFile = null;
114: this .args = testArgs.getArgs(getClass(), getName());
115: this .container = createContainer();
116: this .container.removeAll();
117: this .gc = createGraphics();
118: }
119:
120: // junit.framework.TestCase
121: protected void tearDown() {
122: if (!testFilter.allowExecution(getClass(), getName()))
123: return;
124:
125: disposeGraphics();
126: if (this .referenceImageFile != null) {
127: System.out.println(this .referenceImageFile
128: + " does not exist, in the following directories");
129: for (int i = 0; i < this .refImageDirs.length; i++) {
130: System.out.println(" " + this .refImageDirs[i]);
131: }
132: System.out.print("Create image[(y)es/(n)o][default=y]:");
133: BufferedReader br = new BufferedReader(
134: new InputStreamReader(System.in));
135: try {
136: String result_code = br.readLine().trim();
137: if (result_code.length() <= 0
138: || "y".equals(result_code)) {
139: File f = new File(this .referenceImageFile);
140: String dir = f.getParent();
141: if (dir == null)
142: dir = this .refImageDirs[this .refImageDirs.length - 1];
143: String file = f.getName();
144: System.out.print("Enter directory [default=" + dir
145: + "]:");
146: result_code = br.readLine().trim();
147:
148: if (result_code.length() > 0) {
149: dir = result_code;
150: }
151: file = dir + File.separator + file;
152: encodeImage(this .backBuffer, file);
153: }
154: } catch (Exception ex) {
155: }
156: }
157: this .container = null;
158: this .backBuffer = null;
159: }
160:
161: // junit.framework.TestCase
162: protected void runTest() throws Throwable {
163: Class cls = getClass();
164: String method = getName();
165: //System.out.println(cls.getName()+"."+method);
166: try {
167: if (testFilter.allowExecution(cls, method)) {
168: super .runTest();
169: } else {
170: return;
171: }
172: if (!this .isGraphicsTestcase && this .container != null) {
173: this .container.validate();
174: }
175: // if ( testcase has not asserted using image compare ) {
176: // chances are it had used Assert.assert() methods or
177: // it did not do anything w.r.t the verification
178: //
179: // if there is a description information for the testcase
180: // then we display the description and ask the user to
181: // manually verify the result
182: // }
183: if (this .isAsserted) {
184: return;
185: }
186:
187: TestResultDescription desc = TestResultDescription
188: .getInstance(cls, method);
189: resultVerifier.verify(cls, method, desc);
190: } catch (RefImageNotFoundException refimg_nf_ex) {
191: this .referenceImageFile = refimg_nf_ex.getImage();
192: throw refimg_nf_ex;
193: } catch (Throwable t) {
194: throw t;
195: }
196: }
197:
198: /**
199: * Returns the container for the testcase to create GUI artifacts
200: */
201: protected final Container getContainer() {
202: return this .container;
203: }
204:
205: /**
206: * Returns the arguments for the testcase. It returns a zero length
207: * string if there are no arguments for the testcase
208: */
209: protected final String[] getArguments() {
210: return this .args;
211: }
212:
213: /**
214: * Returns a <code>Graphics</code> for the graphics testcase to
215: * render.
216: */
217: protected final Graphics getGraphics() {
218: this .isGraphicsTestcase = true;
219: return this .gc;
220: }
221:
222: protected Container createContainer() {
223: return defaultTestContainer.getContainer();
224: }
225:
226: /**
227: * Returns the image that contains the testcase rendition
228: */
229: protected final Object getTestImage() {
230: return this .backBuffer;
231: }
232:
233: /**
234: * Returns the reference image for the testcase.
235: */
236: protected final Object getReferenceImage() {
237: return getReferenceImage(getReferenceImageFilename());
238: }
239:
240: /**
241: * Returns the image file name for the testcase
242: */
243: protected final String getReferenceImageFilename() {
244: if (!this .isGraphicsTestcase)
245: throw new IllegalArgumentException(
246: "Not a graphics testcase");
247:
248: return getClass().getName().replace('.', '_') + "_" + getName()
249: + "_" + this .backBuffer.getWidth() + "x"
250: + this .backBuffer.getHeight() + ".png";
251: }
252:
253: /**
254: * Returns the expected image for the fileName relative to the
255: * image directory
256: *
257: * @param fileName file name relative to the image directory
258: */
259: protected final Object getReferenceImage(String fileName) {
260: for (int i = 0; i < this .refImageDirs.length; i++) {
261: // make the relative fileName absolute with the ref image
262: // directory
263: String file = this .refImageDirs[i] + File.separator
264: + fileName;
265: if (new File(file).exists())
266: return file;
267: }
268:
269: throw new RefImageNotFoundException("", fileName);
270: }
271:
272: /**
273: * A convenience method that does
274: * <code>assertImageEquals(getReferenceImage(),getTestImage())</code>
275: */
276: protected final void assertImageEquals() {
277: assertImageEquals(getReferenceImage(), getTestImage());
278: }
279:
280: /**
281: * Assertion method for image equal operation.
282: *
283: * @param expected the value returned from getReferenceImage()
284: * @param actual the value returned from getTestImage()
285: */
286: protected final void assertImageEquals(Object expected,
287: Object actual) {
288: assertImageEquals("", expected, actual);
289: }
290:
291: /**
292: * An utility method that loads an image from a file
293: */
294: protected final Image loadImage(String imageFile) {
295: Image img = Toolkit.getDefaultToolkit().getImage(imageFile);
296: return trackImage(img, this .container);
297: }
298:
299: /**
300: * An utility method that loads an image from a URL
301: */
302: protected final Image loadImage(URL url) {
303: Image img = Toolkit.getDefaultToolkit().getImage(url);
304: return trackImage(img, this .container);
305: }
306:
307: /**
308: * Indicates if a back buffer should be used to render the testcase
309: * graphics operations. The default value is <code>true</code>, but
310: * the testcase can override to return <code>false</code>,so that
311: * screen graphics is used instead of the image buffer
312: */
313: protected boolean useBackBuffer() {
314: return true;
315: }
316:
317: /**
318: * Creates a <Code>BufferedImage</code> for the specified width
319: * and height with INT_ARGB format
320: */
321: protected abstract BufferedImage createBufferedImage(int width,
322: int height);
323:
324: /**
325: * Encode the contents of the <code>BufferedImage</code> with an
326: * image compression format (preferably PNG) onto the file specified
327: */
328: protected abstract void encodeImage(BufferedImage image,
329: String filename);
330:
331: /**
332: * Ensures that the assertion is valid
333: *
334: * @param message message for the failure case.
335: * @param expected the value returned from getReferenceImage()
336: * @param actual the value returned from getTestImage()
337: */
338: protected abstract void assertImageEquals(String message,
339: Object expected, Object actual);
340:
341: /**
342: * Set the background color for the graphics
343: */
344: protected abstract void clearToBackgroundColor(Graphics g, Color c,
345: int x, int y, int w, int h);
346:
347: private Graphics createGraphics() {
348: if (useBackBuffer()) {
349: return createImageGraphics();
350: } else {
351: return createScreenGraphics();
352: }
353: }
354:
355: private void disposeGraphics() {
356: if (this .backBuffer != null) {
357: disposeImageGraphics();
358: } else {
359: disposeScreenGraphics();
360: }
361: }
362:
363: private BufferedImage createBackBuffer() {
364: Dimension size = this .container.getSize();
365: return createBufferedImage(size.width, size.height);
366: }
367:
368: private Graphics createScreenGraphics() {
369: Dimension size = this .container.getSize();
370: Graphics g = container.getGraphics();
371: g.clearRect(0, 0, size.width, size.height);
372: Insets insets = this .container.getInsets();
373: if ((insets.top | insets.left) != 0)
374: g.translate(insets.left, insets.top);
375: return g;
376: }
377:
378: private void disposeScreenGraphics() {
379: this .gc.dispose();
380: this .gc = null;
381: }
382:
383: private Graphics createImageGraphics() {
384: this .backBuffer = createBackBuffer();
385: Graphics2D g = (Graphics2D) this .backBuffer.getGraphics();
386: Color bg = this .container.getBackground();
387: clearToBackgroundColor(g, bg, 0, 0, this .backBuffer
388: .getWidth(null), this .backBuffer.getHeight(null));
389: /*
390: g.setBackground(bg) ;
391: g.clearRect(0,
392: 0,
393: this.backBuffer.getWidth(null),
394: this.backBuffer.getHeight(null)) ;
395: */
396: return g;
397: }
398:
399: private void disposeImageGraphics() {
400: this .gc.dispose();
401: this .gc = null;
402: if (this .isGraphicsTestcase) {
403: Graphics g = createScreenGraphics();
404: try {
405: g.drawImage(this .backBuffer, 0, 0, null);
406: } finally {
407: g.dispose();
408: }
409: }
410: }
411:
412: private BufferedImage toBufferedImage(Image image) {
413: if (image instanceof BufferedImage)
414: return ((BufferedImage) image);
415: BufferedImage bi = createBufferedImage(image.getWidth(null),
416: image.getHeight(null));
417: Graphics big = bi.getGraphics();
418: big.drawImage(image, 0, 0, null);
419: big.dispose();
420:
421: return bi;
422: }
423:
424: static Image trackImage(Image img) {
425: return trackImage(img, new Container());
426: }
427:
428: static Image trackImage(Image img, Component comp) {
429: MediaTracker tracker = new MediaTracker(comp);
430: if (img != null) {
431: // Track an image to make sure it loads completely.
432: tracker.addImage(img, 0);
433: try {
434: tracker.waitForID(0);
435: } catch (InterruptedException e) {
436: e.printStackTrace();
437: }
438: }
439: return img;
440: }
441: }
442:
443: /**
444: * <code>TestArguments</code> abstracts the arguments for all the testcases
445: */
446: class TestArguments {
447: static final String DEFAULT_TAG = "default";
448: static final String[] STRING_ARRAY = new String[0];
449:
450: String[] defaultArgs = STRING_ARRAY;
451: // Map<String, String[]>
452: // Map<classname.methodname, args>
453: Map argsMap = new HashMap();
454:
455: TestArguments() {
456: this (TestContext.getInstance().getStringValue(
457: TestContext.TEST_ARGS_FILENAME));
458: }
459:
460: TestArguments(String file) {
461: if (file == null)
462: return;
463: try {
464: init(new FileInputStream(file));
465: } catch (Exception ex) {
466: System.err.println("Unable to load args file " + file);
467: }
468: }
469:
470: void init(InputStream stream) {
471: BufferedReader reader = null;
472: String line = null;
473: String start_tag = null;
474: String class_name = null;
475: String method_name = null;
476: java.util.List args = new ArrayList();
477: try {
478: reader = new BufferedReader(new InputStreamReader(stream));
479: while ((line = reader.readLine()) != null) {
480: line = line.trim(); // trim white spaces
481: if (line.startsWith("</")) { // check if end tag
482: // process an end tag only if
483: // we had processed a start tag AND
484: // the end tag markup is the same as the start tag
485: if (start_tag != null
486: && line.substring(2).startsWith(start_tag)) {
487: if (DEFAULT_TAG.equals(start_tag)) {
488: this .defaultArgs = (String[]) args
489: .toArray(STRING_ARRAY);
490: } else {
491: argsMap.put(class_name + "." + method_name,
492: args.toArray(STRING_ARRAY));
493: }
494: args.clear();
495: }
496: start_tag = null;
497: } else if (line.startsWith("<args>")) { // check for <args>
498: int end_index = line.indexOf("</args>");
499: if (start_tag == null || end_index == -1) {
500: continue; // we have not seen a start tag or
501: // the end markup for args is missing
502: }
503: String arg = line.substring("<args>".length(),
504: end_index).trim();
505: if (arg != null && arg.length() > 0) {
506: args.add(arg);
507: }
508: } else if (line.startsWith("<")) { // check for start tag
509: int end_index = line.indexOf(">");
510: if (end_index < 0)
511: continue; // no markup termination
512: line = line.substring(1, end_index).trim();
513: if (line.length() < 0)
514: continue; // no content inside markup
515: end_index = line.lastIndexOf('-');
516: start_tag = line;
517: // start_tag is either a valid testcase or "default"
518: // tag
519: if (end_index > 0) {
520: class_name = line.substring(0, end_index)
521: .replace('-', '.');
522: method_name = line.substring(end_index + 1);
523: } else if (!DEFAULT_TAG.equals(line)) {
524: start_tag = null;
525: }
526: }
527: }
528: } catch (Exception ex) {
529: } finally {
530: try {
531: reader.close();
532: } catch (Exception ex) {
533: }
534: }
535: }
536:
537: String[] getArgs(Class testClass, String methodName) {
538: String[] args = null;
539: args = (String[]) this .argsMap.get(testClass.getName() + "."
540: + methodName);
541: if (args != null)
542: return args;
543: return defaultArgs;
544: }
545: }
|