1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
59 static public final int AFFIRMATIVE = 1;
60 static public final int NEGATIVE = 0;
61 static public final int ERROR = -1;
62
63
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
72 throw new RuntimeException("Assertion failed.");
73 }
74
75 static final void affirm(Object obj)
76 {
77 if (debug && obj == null)
78
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
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
219 dis = new DataInputStream(openFileInputStream(fileName));
220 final boolean allowJDK12ClassFiles = true;
221 classFiles[i] = new ClassFile(dis, allowJDK12ClassFiles);
222
223
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
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
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
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
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
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 }