Expanded SCons.Scanner.LaTeX.comment_re to not break on \%
[scons.git] / src / engine / SCons / Tool / JavaCommonTests.py
1 #
2 # __COPYRIGHT__
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #
23
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
25
26 import os.path
27 import sys
28 import unittest
29
30 import SCons.Tool.JavaCommon
31
32
33 # Adding trace=trace to any of the parse_jave() calls below will cause
34 # the parser to spit out trace messages of the tokens it sees and the
35 # attendant transitions.
36
37 def trace(token, newstate):
38     from SCons.Debug import Trace
39     statename = newstate.__class__.__name__
40     Trace('token = %s, state = %s\n' % (repr(token), statename))
41
42 class parse_javaTestCase(unittest.TestCase):
43
44     def test_bare_bones(self):
45         """Test a bare-bones class"""
46
47         input = """\
48 package com.sub.bar;
49
50 public class Foo
51 {
52
53      public static void main(String[] args)
54      {
55
56         /* This tests a former bug where strings would eat later code. */
57         String hello1 = new String("Hello, world!");
58
59      }
60
61 }
62 """
63         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
64         assert pkg_dir == os.path.join('com', 'sub', 'bar'), pkg_dir
65         assert classes == ['Foo'], classes
66
67
68
69     def test_dollar_sign(self):
70         """Test class names with $ in them"""
71
72         input = """\
73 public class BadDep { 
74   public void new$rand () {}
75 }
76 """
77         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
78         assert pkg_dir is None, pkg_dir
79         assert classes == ['BadDep'], classes
80
81
82
83     def test_inner_classes(self):
84         """Test parsing various forms of inner classes"""
85
86         input = """\
87 class Empty {
88 }
89
90 interface Listener {
91   public void execute();
92 }
93
94 public
95 class
96 Test implements Listener {
97   class Inner {
98     void go() {
99       use(new Listener() {
100         public void execute() {
101           System.out.println("In Inner");
102         }
103       });
104     }
105     String s1 = "class A";
106     String s2 = "new Listener() { }";
107     /* class B */
108     /* new Listener() { } */
109   }
110
111   class Inner2 {
112      Inner2() { Listener l = new Listener(); }
113   }
114
115   /* Make sure this class doesn't get interpreted as an inner class of the previous one, when "new" is used in the previous class. */
116   class Inner3 {
117
118   }
119
120   public static void main(String[] args) {
121     new Test().run();
122   }
123
124   void run() {
125     use(new Listener() {
126       public void execute() {
127         use(new Listener( ) {
128           public void execute() {
129             System.out.println("Inside execute()");
130           }
131         });
132       }
133     });
134
135     new Inner().go();
136   }
137
138   void use(Listener l) {
139     l.execute();
140   }
141 }
142
143 class Private {
144   void run() {
145     new Listener() {
146       public void execute() {
147       }
148     };
149   }
150 }
151 """
152
153         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.4')
154         assert pkg_dir is None, pkg_dir
155         expect = [
156                    'Empty',
157                    'Listener',
158                    'Test$1',
159                    'Test$Inner',
160                    'Test$Inner2',
161                    'Test$Inner3',
162                    'Test$2',
163                    'Test$3',
164                    'Test',
165                    'Private$1',
166                    'Private',
167                  ]
168         assert classes == expect, classes
169
170         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.5')
171         assert pkg_dir is None, pkg_dir
172         expect = [
173                    'Empty',
174                    'Listener',
175                    'Test$Inner$1',
176                    'Test$Inner',
177                    'Test$Inner2',
178                    'Test$Inner3',
179                    'Test$1',
180                    'Test$1$1',
181                    'Test',
182                    'Private$1',
183                    'Private',
184                  ]
185         assert classes == expect, (expect, classes)
186
187         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '5')
188         assert pkg_dir is None, pkg_dir
189         expect = [
190                    'Empty',
191                    'Listener',
192                    'Test$Inner$1',
193                    'Test$Inner',
194                    'Test$Inner2',
195                    'Test$Inner3',
196                    'Test$1',
197                    'Test$1$1',
198                    'Test',
199                    'Private$1',
200                    'Private',
201                  ]
202         assert classes == expect, (expect, classes)
203
204
205
206     def test_comments(self):
207         """Test a class with comments"""
208
209         input = """\
210 package com.sub.foo;
211
212 import java.rmi.Naming;
213 import java.rmi.RemoteException;
214 import java.rmi.RMISecurityManager;
215 import java.rmi.server.UnicastRemoteObject;
216
217 public class Example1 extends UnicastRemoteObject implements Hello {
218
219     public Example1() throws RemoteException {
220         super();
221     }
222
223     public String sayHello() {
224         return "Hello World!";
225     }
226
227     public static void main(String args[]) {
228         if (System.getSecurityManager() == null) {
229             System.setSecurityManager(new RMISecurityManager());
230         }
231         // a comment
232         try {
233             Example1 obj = new Example1();
234
235             Naming.rebind("//myhost/HelloServer", obj);
236
237             System.out.println("HelloServer bound in registry");
238         } catch (Exception e) {
239             System.out.println("Example1 err: " + e.getMessage());
240             e.printStackTrace();
241         }
242     }
243 }
244 """
245
246         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
247         assert pkg_dir == os.path.join('com', 'sub', 'foo'), pkg_dir
248         assert classes == ['Example1'], classes
249
250
251     def test_arrays(self):
252         """Test arrays of class instances"""
253
254         input = """\
255 public class Test {
256     MyClass abc = new MyClass();
257     MyClass xyz = new MyClass();
258     MyClass _array[] = new MyClass[] {
259         abc,
260         xyz
261     }
262 }
263 """
264
265         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
266         assert pkg_dir is None, pkg_dir
267         assert classes == ['Test'], classes
268
269
270
271     def test_backslash(self):
272         """Test backslash handling"""
273
274         input = """\
275 public class MyTabs
276 {
277         private class MyInternal
278         {
279         }
280         private final static String PATH = "images\\\\";
281 }
282 """
283
284         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
285         assert pkg_dir is None, pkg_dir
286         assert classes == ['MyTabs$MyInternal', 'MyTabs'], classes
287
288
289     def test_enum(self):
290         """Test the Java 1.5 enum keyword"""
291
292         input = """\
293 package p;
294 public enum a {}
295 """
296
297         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
298         assert pkg_dir == 'p', pkg_dir
299         assert classes == ['a'], classes
300
301
302     def test_anon_classes(self):
303         """Test anonymous classes"""
304
305         input = """\
306 public abstract class TestClass
307 {
308     public void completed()
309     {
310         new Thread()
311         {
312         }.start();
313
314         new Thread()
315         {
316         }.start();
317     }
318 }
319 """
320
321         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
322         assert pkg_dir is None, pkg_dir
323         assert classes == ['TestClass$1', 'TestClass$2', 'TestClass'], classes
324
325
326     def test_closing_bracket(self):
327         """Test finding a closing bracket instead of an anonymous class"""
328
329         input = """\
330 class TestSCons {
331     public static void main(String[] args) {
332         Foo[] fooArray = new Foo[] { new Foo() };
333     }
334 }
335
336 class Foo { }
337 """
338
339         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
340         assert pkg_dir is None, pkg_dir
341         assert classes == ['TestSCons', 'Foo'], classes
342
343
344     def test_dot_class_attributes(self):
345         """Test handling ".class" attributes"""
346
347         input = """\
348 public class Test extends Object
349 {
350     static {
351         Class c = Object[].class;
352         Object[] s = new Object[] {};
353     }
354 }
355 """
356
357         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
358         assert classes == ['Test'], classes
359
360         input = """\
361 public class A {
362     public class B {
363         public void F(Object[] o) {
364             F(new Object[] {Object[].class});
365         }
366         public void G(Object[] o) {
367             F(new Object[] {});
368         }
369     }
370 }
371 """
372
373         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input)
374         assert pkg_dir is None, pkg_dir
375         assert classes == ['A$B', 'A'], classes
376
377     def test_anonymous_classes_with_parentheses(self):
378         """Test finding anonymous classes marked by parentheses"""
379
380         input = """\
381 import java.io.File;
382
383 public class Foo {
384     public static void main(String[] args) {
385         File f = new File(
386             new File("a") {
387                 public String toString() {
388                     return "b";
389                 }
390             } to String()
391         ) {
392             public String toString() {
393                 return "c";
394             }
395         };
396     }
397 }
398 """
399
400         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.4')
401         assert classes == ['Foo$1', 'Foo$2', 'Foo'], classes
402
403         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.5')
404         assert classes == ['Foo$1', 'Foo$1$1', 'Foo'], classes
405
406         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '6')
407         assert classes == ['Foo$1', 'Foo$1$1', 'Foo'], classes
408
409
410
411     def test_nested_anonymous_inner_classes(self):
412         """Test finding nested anonymous inner classes"""
413
414         input = """\
415 // import java.util.*;
416
417 public class NestedExample
418 {
419         public NestedExample()
420         {
421                 Thread t = new Thread() {
422                         public void start()
423                         {
424                                 Thread t = new Thread() {
425                                         public void start()
426                                         {
427                                                 try {Thread.sleep(200);}
428                                                 catch (Exception e) {}
429                                         }
430                                 };
431                                 while (true)
432                                 {
433                                         try {Thread.sleep(200);}
434                                         catch (Exception e) {}
435                                 }
436                         }
437                 };
438         }
439
440
441         public static void main(String argv[])
442         {
443                 NestedExample e = new NestedExample();
444         }
445 }
446 """
447
448         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.4')
449         expect = [ 'NestedExample$1', 'NestedExample$2', 'NestedExample' ]
450         assert expect == classes, (expect, classes)
451
452         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.5')
453         expect = [ 'NestedExample$1', 'NestedExample$1$1', 'NestedExample' ]
454         assert expect == classes, (expect, classes)
455
456         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '6')
457         expect = [ 'NestedExample$1', 'NestedExample$1$1', 'NestedExample' ]
458         assert expect == classes, (expect, classes)
459
460     def test_private_inner_class_instantiation(self):
461         """Test anonymous inner class generated by private instantiation"""
462
463         input = """\
464 class test
465 {
466     test()
467     {
468         super();
469         new inner();
470     }
471
472     static class inner
473     {
474         private inner() {}
475     }
476 }
477 """
478
479         # This is what we *should* generate, apparently due to the
480         # private instantiation of the inner class, but don't today.
481         #expect = [ 'test$1', 'test$inner', 'test' ]
482
483         # What our parser currently generates, which doesn't match
484         # what the Java compiler actually generates.
485         expect = [ 'test$inner', 'test' ]
486
487         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.4')
488         assert expect == classes, (expect, classes)
489
490         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.5')
491         assert expect == classes, (expect, classes)
492
493     def test_floating_point_numbers(self):
494         """Test floating-point numbers in the input stream"""
495         input = """
496 // Broken.java
497 class Broken
498 {
499   /**
500    * Detected.
501    */
502   Object anonymousInnerOK = new Runnable() { public void run () {} };
503
504   /**
505    * Detected.
506    */
507   class InnerOK { InnerOK () { } }
508   
509   {
510     System.out.println("a number: " + 1000.0 + "");
511   }
512
513   /**
514    * Not detected.
515    */
516   Object anonymousInnerBAD = new Runnable() { public void run () {} };
517
518   /**
519    * Not detected.
520    */
521   class InnerBAD { InnerBAD () { } }
522 }
523 """
524
525         expect = ['Broken$1', 'Broken$InnerOK', 'Broken$2', 'Broken$InnerBAD', 'Broken']
526
527         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.4')
528         assert expect == classes, (expect, classes)
529
530         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.5')
531         assert expect == classes, (expect, classes)
532
533
534     def test_genercis(self):
535         """Test that generics don't interfere with detecting anonymous classes"""
536
537         input = """\
538 import java.util.Date;
539 import java.util.Comparator;
540
541 public class Foo
542 {
543   public void foo()
544   {
545     Comparator<Date> comp = new Comparator<Date>()
546       {
547         static final long serialVersionUID = 1L;
548         public int compare(Date lhs, Date rhs)
549         {
550           return 0;
551         }
552       };
553   }
554 }
555 """
556
557         expect = [ 'Foo$1', 'Foo' ]
558
559         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '1.6')
560         assert expect == classes, (expect, classes)
561
562         pkg_dir, classes = SCons.Tool.JavaCommon.parse_java(input, '6')
563         assert expect == classes, (expect, classes)
564
565
566
567 if __name__ == "__main__":
568     suite = unittest.TestSuite()
569     tclasses = [ parse_javaTestCase ]
570     for tclass in tclasses:
571         names = unittest.getTestCaseNames(tclass, 'test_')
572         suite.addTests(list(map(tclass, names)))
573     if not unittest.TextTestRunner().run(suite).wasSuccessful():
574         sys.exit(1)
575
576 # Local Variables:
577 # tab-width:4
578 # indent-tabs-mode:nil
579 # End:
580 # vim: set expandtab tabstop=4 shiftwidth=4: