View Javadoc

1   /*
2    * Copyright 2005 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS, 
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License.
15   */
16  
17  package org.apache.jdo.impl.enhancer.util;
18  
19  import java.util.Iterator;
20  import java.util.Enumeration;
21  import java.util.List;
22  import java.util.ArrayList;
23  import java.util.Map;
24  import java.util.HashMap;
25  import java.util.Set;
26  import java.util.HashSet;
27  import java.util.Stack;
28  
29  import java.io.PrintWriter;
30  import java.io.StringWriter;
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.io.FileInputStream;
34  import java.io.BufferedInputStream;
35  import java.io.DataInputStream;
36  import java.io.FileNotFoundException;
37  import java.io.ByteArrayOutputStream;
38  import java.io.PrintStream;
39  
40  import org.apache.jdo.impl.enhancer.classfile.ClassFile;
41  import org.apache.jdo.impl.enhancer.classfile.ClassMethod;
42  import org.apache.jdo.impl.enhancer.classfile.Descriptor;
43  
44  
45  
46  /***
47   * Utility class for testing two class files for equal augmentation.
48   *
49   * @author Martin Zaun
50   */
51  public class AugmentationDiffTest
52  {
53      // return values of main()
54      static public final int OK = 0;
55      static public final int USAGE_ERROR = -1;
56      static public final int INTERNAL_ERROR = -3;
57  
58      // return values of internal test methods
59      static public final int AFFIRMATIVE = 1;
60      static public final int NEGATIVE = 0;
61      static public final int ERROR = -1;
62  
63      // output streams
64      static private boolean debug = false;
65      static private final PrintWriter out = new PrintWriter(System.out, true);
66      static private final PrintWriter err = new PrintWriter(System.err, true);
67  
68      static final void affirm(boolean cond)
69      {
70          if (debug && !cond)
71              //^olsen: throw AssertionException ?
72              throw new RuntimeException("Assertion failed.");
73      }
74  
75      static final void affirm(Object obj)
76      {
77          if (debug && obj == null)
78              //^olsen: throw AssertionException ?
79              throw new RuntimeException("Assertion failed: obj = null");
80      }
81  
82      static private InputStream openFileInputStream(String fileName)
83          throws FileNotFoundException
84      {
85       	return new BufferedInputStream(new FileInputStream(fileName));
86      }
87  
88      static private void closeInputStream(InputStream in)
89      {
90          if (in != null) {
91              try {
92                  in.close();
93              } catch (IOException ex) {
94                  err.println("Exception caught: " + ex);
95              }
96          }
97      }
98  
99      // ----------------------------------------------------------------------
100 
101     private boolean verbose;
102     private String[] classFileNames;
103     private String[] classNames;
104     private String[] userClassNames;
105     private ClassFile[] classFiles;
106 
107     public AugmentationDiffTest()
108     {}
109 
110     private int diffAugmentation(PrintWriter out)
111     {
112         affirm(ERROR < NEGATIVE && NEGATIVE < AFFIRMATIVE);
113         affirm(classFiles.length == 2);
114         affirm(classNames.length == 2);
115 
116         int res = NEGATIVE;
117 
118         final Map[] classMethods = { new HashMap(), new HashMap() };
119         for (int i = 0; i < 2; i++) {
120             for (Enumeration e = classFiles[i].methods().elements();
121                  e.hasMoreElements();) {
122                 final ClassMethod method = (ClassMethod)e.nextElement();
123                 final String methodSig = method.signature().asString();
124                 final String methodArgs = Descriptor.userMethodArgs(methodSig);
125                 final String methodName = method.name().asString();
126 
127                 if (methodName.startsWith("jdo")) {
128                 //if (methodName.equals("jdoReplaceField")) {
129                     final Object obj
130                         = classMethods[i].put(methodName + methodArgs, method);
131                     affirm(obj == null);
132                 }
133             }
134         }
135         
136         final Set keySet = new HashSet();
137         keySet.addAll(classMethods[0].keySet());
138         keySet.addAll(classMethods[1].keySet());
139         for (Iterator i = keySet.iterator(); i.hasNext();) {
140             final Object key = i.next();
141 
142             final ClassMethod method0
143                 = (ClassMethod)classMethods[0].remove(key);
144             final ClassMethod method1
145                 = (ClassMethod)classMethods[1].remove(key);
146             affirm(method0 != method1);
147             affirm(method0 != null || method1 != null);
148             
149             if (method0 == null || method1 == null) {
150                 out.println("    !!! ERROR: missing method: " + key);
151                 out.println("        <<< method: " + method0);
152                 out.println("        >>> method: " + method1);
153                 res = ERROR;
154                 continue;
155             }
156 
157             final Stack msg = new Stack();
158             if (method0.isEqual(msg, method1)) {
159                 if (verbose) {
160                     out.println("    +++ equal augmentation: " + key);
161                 }
162             } else {
163                 out.println("    !!! not equal augmentation: " + key);
164                 msg.push("method = " + method1);
165                 msg.push("method = " + method0);
166                 final StringWriter s0 = new StringWriter();
167                 final StringWriter s1 = new StringWriter();
168                 final PrintWriter p0 = new PrintWriter(s0);
169                 final PrintWriter p1 = new PrintWriter(s1);
170                 int j = 0;
171                 while (!msg.empty()) {
172                     p0.println("    <<< " + pad(j) + msg.pop());
173                     p1.println("    >>> " + pad(j) + msg.pop());
174                     j += 4;
175                 }
176                 out.println(s0.toString());
177                 out.println(s1.toString());
178 
179                 if (verbose) {
180                     ByteArrayOutputStream b0 = new ByteArrayOutputStream();
181                     ByteArrayOutputStream b1 = new ByteArrayOutputStream();
182                     method0.print(new PrintStream(b0), 4);
183                     method1.print(new PrintStream(b1), 4);
184                     out.println(b0.toString());
185                     out.println(b1.toString());
186                     if (res == NEGATIVE) {
187                         res = AFFIRMATIVE;
188                     }
189                 }
190                 break;
191             }
192         }
193 
194         return res;
195     }
196 
197     static private String pad(int n) 
198     {
199         final StringBuffer s = new StringBuffer();
200         for (int i = 0; i < n; i++) {
201             s.append(' ');
202         }
203         return s.toString();
204     }
205 
206     private int parseClass(PrintWriter out,
207                            int i)
208     {
209         affirm(0 <= i && i <= 1);
210         affirm(classFileNames.length == 2);
211         affirm(classFiles.length == 2);
212         affirm(classNames.length == 2);
213         affirm(userClassNames.length == 2);
214         final String fileName = classFileNames[i];
215         
216         DataInputStream dis = null;
217         try {
218             // create class file
219             dis = new DataInputStream(openFileInputStream(fileName));
220             final boolean allowJDK12ClassFiles = true;
221             classFiles[i] = new ClassFile(dis, allowJDK12ClassFiles);
222 
223             // get real class name
224             classNames[i] = classFiles[i].className().asString();
225             userClassNames[i] = classNames[i].replace('/', '.');
226             out.println("    +++ parsed classfile");
227         } catch (ClassFormatError ex) {
228             out.println("    !!! ERROR: format error when parsing classfile: "
229                         + fileName);
230             out.println("        error: " + err);
231             return ERROR;
232         } catch (IOException ex) {
233             out.println("    !!! ERROR: exception while reading classfile: "
234                         + fileName);
235             out.println("        exception: " + ex);
236             return ERROR;
237         } finally {
238             closeInputStream(dis);
239         }
240 
241         return AFFIRMATIVE;
242     }
243 
244     private int test(PrintWriter out,
245                      String[] classFileNames)
246     {
247         affirm(classFileNames.length == 2);
248         this.classFileNames = classFileNames;
249 
250         if (verbose) {
251             out.println("-------------------------------------------------------------------------------");
252             out.println();
253             out.println("Test classfiles for equal augmentation: ...");
254         }
255         
256         // check parsing class
257         classFiles = new ClassFile[2];
258         classNames = new String[2];
259         userClassNames = new String[2];
260         for (int i = 0; i < 2; i++) {
261             final StringWriter s = new StringWriter();
262             if (parseClass(new PrintWriter(s), i) <= NEGATIVE) {
263                 out.println();
264                 out.println("!!! ERROR: failed parsing classfile: "
265                             + classFileNames[i]);
266                 out.println(s.toString());
267                 return ERROR;
268             }
269 
270             if (verbose) {
271                 out.println();
272                 out.println("+++ parsed classfile: " + classFileNames[i]);
273                 out.println(s.toString());
274             }
275         }
276         
277         // check class names
278         {
279             final StringWriter s = new StringWriter();
280             if (!classNames[0].equals(classNames[1])) {
281                 out.println();
282                 out.println("!!! ERROR: different class names:");
283                 out.println("<<< class name = " + userClassNames[0]);
284                 out.println(">>> class name = " + userClassNames[1]);
285                 out.println(s.toString());
286                 return ERROR;
287             }
288         }
289         
290         // check for augmentation differences
291         final StringWriter s = new StringWriter();
292         final int r = diffAugmentation(new PrintWriter(s));
293         if (r < NEGATIVE) {
294             out.println();
295             out.println("!!! ERROR: incorrect augmentation: "
296                         + userClassNames[0]);
297             out.println(s.toString());
298             return ERROR;
299         }
300         
301         if (r == NEGATIVE) {
302             out.println();
303             out.println("+++ equal augmentation:"
304                         + userClassNames[0]);
305         } else {
306             out.println();
307             out.println("!!! not equal augmentation:"
308                         + userClassNames[0]);
309         }
310         if (verbose) {
311             out.println(s.toString());
312         }
313 
314         return r;
315     }
316 
317     public int test(PrintWriter out,
318                     boolean verbose,
319                     List classFileNames)
320     {
321         affirm(classFileNames.size() % 2 == 0);
322         this.verbose = verbose;
323 
324         out.println();
325         out.println("AugmentationDiffTest: Testing Classes for JDO Persistence-Capability Enhancement");
326 
327         final int all = classFileNames.size() / 2;
328         int nofFailed = 0;
329         for (int i = 0; i < all; i++) {
330             String name0 = (String)classFileNames.get(i);
331             String name1 = (String)classFileNames.get(all + i);
332             String[] pair = { name0, name1 };
333             if (test(out, pair) != NEGATIVE) {
334                 nofFailed++;
335             }
336         }
337         final int nofPassed = all - nofFailed;
338 
339         out.println();
340         out.println("AugmentationDiffTest: Summary:  TESTED: " + all
341                     + "  PASSED: " + nofPassed
342                     + "  FAILED: " + nofFailed);
343         return nofFailed;
344     }
345     
346     // ----------------------------------------------------------------------
347 
348     /***
349      * Prints usage message.
350      */
351     static private void usage()
352     {
353         err.println();
354         err.println("Usage: AugmentationDiffTest <options> <classfile1> ... <classfile2> ...");
355         err.println();
356         err.println("This class pairwise tests if two classes have structurally the same code");
357         err.println("enhancement for persistence-capability (\"augmentation\").");
358         err.println();
359         err.println("Options include:");
360         err.println("    -h, --help               print usage");
361         err.println("    -v, --verbose            enable verbose output");
362         err.println();
363         err.println("Return value:");
364         err.println("= 0   equally augmented classfiles");
365         err.println("> 0   not equally augmented classfiles");
366         err.println("< 0   severe errors preventing the test to complete");
367         err.println();
368     }
369 
370     static public void main(String[] argv)
371     {
372         // parse args
373         boolean verbose = false;
374         List classFileNames = new ArrayList();
375         for (int i = 0; i < argv.length; i++) {
376             String arg = argv[i];
377             if (arg.equals("-h") || arg.equals("--help")) {
378                 usage();
379                 System.exit(OK);
380             }
381             if (arg.equals("-v") || arg.equals("--verbose")) {
382                 verbose = true;
383                 continue;
384             }
385             if (arg.equals("-d") ||
386                 arg.equals("--debug")) {
387                 debug = true;
388                 continue;
389             }
390             if (arg.startsWith("-")) {
391                 err.println();
392                 err.println("Unrecognized option: " + arg);
393                 usage();
394                 System.exit(USAGE_ERROR);
395             }
396             classFileNames.add(arg);
397         }
398 
399         // check arguments
400         if (classFileNames.size() % 2 != 0) {
401             err.println();
402             err.println("Odd number of classfiles arguments.");
403             usage();
404             System.exit(USAGE_ERROR);
405             return;
406         }
407 
408         if (debug) {
409             out.println("AugmentationDiffTest: options:");
410             out.println("    verbose = " + verbose);
411             out.print("    classFileNames =");
412             for (int i = 0; i < classFileNames.size(); i++)
413                 out.print(" " + classFileNames.get(i));
414             out.println();
415         }
416 
417         try {
418             final AugmentationDiffTest test = new AugmentationDiffTest();
419             final int res = test.test(out, verbose, classFileNames);
420             System.exit(res);
421         } catch (RuntimeException ex) {
422             err.println("Internal error;");
423             err.println("Exception caught:" + ex);
424             ex.printStackTrace(err);
425             System.exit(INTERNAL_ERROR);
426         }
427     }
428 }