001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.junit;
043:
044: import java.util.ArrayList;
045: import java.util.EnumSet;
046: import java.util.HashSet;
047: import java.util.List;
048: import java.util.Map;
049: import java.util.Set;
050: import javax.lang.model.element.Element;
051: import javax.lang.model.element.ExecutableElement;
052: import javax.lang.model.element.Modifier;
053: import javax.lang.model.element.NestingKind;
054: import javax.lang.model.element.TypeElement;
055: import javax.lang.model.util.ElementFilter;
056: import org.netbeans.api.java.source.CompilationInfo;
057: import org.netbeans.modules.junit.plugin.JUnitPlugin.CreateTestParam;
058: import static javax.lang.model.element.Modifier.ABSTRACT;
059: import static javax.lang.model.element.Modifier.PRIVATE;
060: import static javax.lang.model.element.Modifier.PROTECTED;
061: import static javax.lang.model.element.Modifier.PUBLIC;
062: import static javax.lang.model.element.Modifier.STATIC;
063: import static org.openide.NotifyDescriptor.WARNING_MESSAGE;
064: import static org.netbeans.modules.junit.TestCreator.ACCESS_MODIFIERS;
065:
066: /**
067: *
068: * @author Marian Petras
069: */
070: public final class TestGeneratorSetup implements TestabilityJudge {
071: /* the class is final only for performance reasons */
072:
073: /* attributes - private */
074: static private final String JUNIT_SUPER_CLASS_NAME = "TestCase";
075: static private final String JUNIT_FRAMEWORK_PACKAGE_NAME = "junit.framework";
076:
077: static private final String METHOD_NAME_SETUP = "setUp"; //NOI18N
078: static private final String METHOD_NAME_TEARDOWN = "tearDown"; //NOI18N
079: static private final String CLASS_COMMENT_LINE1 = "TestCreator.javaClass.addTestsHereComment.l1";
080: static private final String CLASS_COMMENT_LINE2 = "TestCreator.javaClass.addTestsHereComment.l2";
081:
082: /** should test classes be skipped during generation of tests? */
083: private boolean skipTestClasses = true;
084: /** should package-private classes be skipped during generation of tests? */
085: private boolean skipPkgPrivateClasses = false;
086: /** should abstract classes be skipped during generation of tests? */
087: private boolean skipAbstractClasses = false;
088: /** should exception classes be skipped during generation of tests? */
089: private boolean skipExceptionClasses = false;
090: /**
091: * should test suite classes be generated when creating tests for folders
092: * and/or packages?
093: */
094: private boolean generateSuiteClasses = true;
095: /**
096: * bitmap defining whether public/protected methods should be tested
097: *
098: * @see #testPackagePrivateMethods
099: */
100: private Set<Modifier> methodAccessModifiers = AbstractTestGenerator
101: .createModifierSet(Modifier.PUBLIC, Modifier.PROTECTED);
102: /**
103: * should package-private methods be tested?
104: *
105: * @see #methodAccessModifiers
106: */
107: private boolean testPkgPrivateMethods = true;
108: /**
109: * should default method bodies be generated for newly created test methods?
110: *
111: * @see #generateMethodJavadoc
112: * @see #generateMethodBodyComments
113: */
114: private boolean generateDefMethodBody = true;
115: /**
116: * should Javadoc comment be generated for newly created test methods?
117: *
118: * @see #generateDefMethodBody
119: * @see #generateMethodBodyComments
120: */
121: private boolean generateMethodJavadoc = true;
122: /**
123: * should method body comment be generated for newly created test methods?
124: *
125: * @see #generateDefMethodBody
126: * @see #generateMethodJavadoc
127: */
128: private boolean generateSourceCodeHints = true;
129: /**
130: * should {@code setUp()} (or {@code @Before}) method be generated
131: * in test classes?
132: *
133: * @see #generateTestTearDown
134: * @see #generateClassSetUp
135: * @see #generateMainMethod
136: */
137: private boolean generateTestSetUp = true;
138: /**
139: * should {@code tearDown()} (or {@code @After}) method be generated
140: * in test classes?
141: *
142: * @see #generateTestSetUp
143: * @see #generateClassTearDown
144: * @see #generateMainMethod
145: */
146: private boolean generateTestTearDown = true;
147: /**
148: * should {@code @BeforeClass} method be generated in test classes?
149: *
150: * @see #generateClassTearDown
151: * @see #generateTestSetUp
152: * @see #generateMainMethod
153: */
154: private boolean generateClassSetUp = true;
155: /**
156: * should {@code @AfterClass} method be generated in test classes?
157: *
158: * @see #generateClassSetUp
159: * @see #generateTestTearDown
160: * @see #generateMainMethod
161: */
162: private boolean generateClassTearDown = true;
163: /**
164: * should static method <code>main(String args[])</code>
165: * be generated in test classes?
166: *
167: * @see #generateSetUp
168: * @see #generateTearDown
169: */
170: private boolean generateMainMethod = true;
171:
172: /* public methods */
173:
174: /**
175: * Creates a new <code>TestCreator</code>.
176: *
177: * @param loadDefaults <code>true</code> if defaults should be loaded
178: * from <code>JUnitSettings</code>;
179: * <code>false</code> otherwise
180: */
181: public TestGeneratorSetup(boolean loadDefaults) {
182: if (loadDefaults) {
183: loadDefaults();
184: }
185: }
186:
187: /**
188: *
189: */
190: public TestGeneratorSetup(Map<CreateTestParam, Object> params) {
191: final JUnitSettings settings = JUnitSettings.getDefault();
192:
193: skipTestClasses = !JUnitSettings.GENERATE_TESTS_FROM_TEST_CLASSES;
194:
195: skipPkgPrivateClasses = !Boolean.TRUE.equals(params
196: .get(CreateTestParam.INC_PKG_PRIVATE_CLASS));
197: skipAbstractClasses = !Boolean.TRUE.equals(params
198: .get(CreateTestParam.INC_ABSTRACT_CLASS));
199: skipExceptionClasses = !Boolean.TRUE.equals(params
200: .get(CreateTestParam.INC_EXCEPTION_CLASS));
201: generateSuiteClasses = Boolean.TRUE.equals(params
202: .get(CreateTestParam.INC_GENERATE_SUITE));
203:
204: methodAccessModifiers.clear();
205: if (Boolean.TRUE.equals(params.get(CreateTestParam.INC_PUBLIC))) {
206: methodAccessModifiers.add(Modifier.PUBLIC);
207: }
208: if (Boolean.TRUE.equals(params
209: .get(CreateTestParam.INC_PROTECTED))) {
210: methodAccessModifiers.add(Modifier.PROTECTED);
211: }
212: testPkgPrivateMethods = Boolean.TRUE.equals(params
213: .get(CreateTestParam.INC_PKG_PRIVATE));
214: generateDefMethodBody = Boolean.TRUE.equals(params
215: .get(CreateTestParam.INC_METHOD_BODIES));
216: generateMethodJavadoc = Boolean.TRUE.equals(params
217: .get(CreateTestParam.INC_JAVADOC));
218: generateSourceCodeHints = Boolean.TRUE.equals(params
219: .get(CreateTestParam.INC_CODE_HINT));
220: generateTestSetUp = Boolean.TRUE.equals(params
221: .get(CreateTestParam.INC_SETUP));
222: generateTestTearDown = Boolean.TRUE.equals(params
223: .get(CreateTestParam.INC_TEAR_DOWN));
224:
225: generateMainMethod = settings.isGenerateMainMethod();
226: }
227:
228: /**
229: * Loads default settings from <code>JUnitSettings</code>.
230: */
231: private void loadDefaults() {
232: final JUnitSettings settings = JUnitSettings.getDefault();
233:
234: skipTestClasses = JUnitSettings.GENERATE_TESTS_FROM_TEST_CLASSES;
235: skipPkgPrivateClasses = !settings
236: .isIncludePackagePrivateClasses();
237: skipAbstractClasses = !settings.isGenerateAbstractImpl();
238: skipExceptionClasses = !settings.isGenerateExceptionClasses();
239: generateSuiteClasses = settings.isGenerateSuiteClasses();
240:
241: methodAccessModifiers.clear();
242: if (settings.isMembersPublic()) {
243: methodAccessModifiers.add(Modifier.PUBLIC);
244: }
245: if (settings.isMembersProtected()) {
246: methodAccessModifiers.add(Modifier.PROTECTED);
247: }
248: testPkgPrivateMethods = settings.isMembersPackage();
249:
250: generateDefMethodBody = settings.isBodyContent();
251: generateMethodJavadoc = settings.isJavaDoc();
252: generateSourceCodeHints = settings.isBodyComments();
253: generateTestSetUp = settings.isGenerateSetUp();
254: generateTestTearDown = settings.isGenerateTearDown();
255: generateMainMethod = settings.isGenerateMainMethod();
256: }
257:
258: /**
259: * Sets whether tests for test classes should be generated
260: * The default is <code>true</code>.
261: *
262: * @param test <code>false</code> if test classes should be skipped
263: * during test creation;
264: * <code>true</code> otherwise
265: */
266: public void setSkipTestClasses(boolean skip) {
267: this .skipTestClasses = skip;
268: }
269:
270: /**
271: */
272: public boolean isSkipTestClasses() {
273: return skipTestClasses;
274: }
275:
276: /**
277: * Sets whether tests for package-private classes should be generated
278: * The default is <code>false</code>.
279: *
280: * @param test <code>false</code> if package-private classes should
281: * be skipped during test creation;
282: * <code>true</code> otherwise
283: */
284: public void setSkipPackagePrivateClasses(boolean skip) {
285: this .skipPkgPrivateClasses = skip;
286: }
287:
288: /**
289: */
290: public boolean isSkipPackagePrivateClasses() {
291: return skipPkgPrivateClasses;
292: }
293:
294: /**
295: * Sets whether tests for abstract classes should be generated
296: * The default is <code>false</code>.
297: *
298: * @param test <code>false</code> if abstract classes should be skipped
299: * during test creation;
300: * <code>true</code> otherwise
301: */
302: public void setSkipAbstractClasses(boolean skip) {
303: this .skipAbstractClasses = skip;
304: }
305:
306: /**
307: */
308: public boolean isSkipAbstractClasses() {
309: return skipAbstractClasses;
310: }
311:
312: /**
313: * Sets whether tests for exception classes should be generated
314: * The default is <code>false</code>.
315: *
316: * @param test <code>false</code> if exception classes should be skipped
317: * during test creation;
318: * <code>true</code> otherwise
319: */
320: public void setSkipExceptionClasses(boolean skip) {
321: this .skipExceptionClasses = skip;
322: }
323:
324: /**
325: */
326: public boolean isSkipExceptionClasses() {
327: return skipExceptionClasses;
328: }
329:
330: /**
331: * Sets whether test suite classes should be generated when creating tests
332: * for folders and/or packages.
333: *
334: * @param generate <code>true</code> if test suite classes should
335: * be generated; <code>false</code> otherwise
336: */
337: public void setGenerateSuiteClasses(boolean generate) {
338: this .generateSuiteClasses = generate;
339: }
340:
341: /**
342: */
343: public boolean isGenerateSuiteClasses() {
344: return generateSuiteClasses;
345: }
346:
347: /**
348: * Sets whether public methods should be tested or not.
349: * The default is <code>true</code>.
350: *
351: * @param test <code>true</code> if public methods should be tested;
352: * <code>false</code> if public methods should be skipped
353: */
354: public void setTestPublicMethods(boolean test) {
355: if (test) {
356: methodAccessModifiers.add(Modifier.PUBLIC);
357: } else {
358: methodAccessModifiers.remove(Modifier.PUBLIC);
359: }
360: }
361:
362: /**
363: */
364: public boolean isTestPublicMethods() {
365: return methodAccessModifiers.contains(Modifier.PUBLIC);
366: }
367:
368: /**
369: * Sets whether protected methods should be tested or not.
370: * The default is <code>true</code>.
371: *
372: * @param test <code>true</code> if protected methods should be tested;
373: * <code>false</code> if protected methods should be skipped
374: */
375: public void setTestProtectedMethods(boolean test) {
376: if (test) {
377: methodAccessModifiers.add(Modifier.PROTECTED);
378: } else {
379: methodAccessModifiers.remove(Modifier.PROTECTED);
380: }
381: }
382:
383: /**
384: */
385: public boolean isTestProtectedMethods() {
386: return methodAccessModifiers.contains(Modifier.PROTECTED);
387: }
388:
389: /**
390: * Tells which methods should be tested - public, protected, private
391: * or a combination of these.
392: *
393: * @return {@code EnumSet} of access modifiers that determine which methods
394: * should be tested (for which test skeletons should be created).
395: */
396: public EnumSet<Modifier> getMethodAccessModifiers() {
397: return EnumSet.copyOf(methodAccessModifiers);
398: }
399:
400: /**
401: * Sets whether package-private methods should be tested or not.
402: * The default is <code>true</code>.
403: *
404: * @param test <code>true</code> if package-private methods should be
405: * tested;
406: * <code>false</code> if package-private methods should be
407: * skipped
408: */
409: public void setTestPackagePrivateMethods(boolean test) {
410: this .testPkgPrivateMethods = test;
411: }
412:
413: /**
414: */
415: public boolean isTestPackagePrivateMethods() {
416: return testPkgPrivateMethods;
417: }
418:
419: /**
420: * Sets whether default method bodies should be generated for newly created
421: * test methods.
422: * The default is <code>true</code>.
423: *
424: * @param generate <code>true</code> if default method bodies should
425: * be generated; <code>false</code> otherwise
426: */
427: public void setGenerateDefMethodBody(boolean generate) {
428: this .generateDefMethodBody = generate;
429: }
430:
431: /**
432: */
433: public boolean isGenerateDefMethodBody() {
434: return generateDefMethodBody;
435: }
436:
437: /**
438: * Sets whether Javadoc comment should be generated for newly created
439: * test methods.
440: * The default is <code>true</code>.
441: *
442: * @param generate <code>true</code> if Javadoc comment should
443: * be generated; <code>false</code> otherwise
444: */
445: public void setGenerateMethodJavadoc(boolean generate) {
446: this .generateMethodJavadoc = generate;
447: }
448:
449: /**
450: */
451: public boolean isGenerateMethodJavadoc() {
452: return generateMethodJavadoc;
453: }
454:
455: /**
456: * Sets whether method body comment should be generated for newly created
457: * test methods.
458: * The default is <code>true</code>.
459: *
460: * @param generate <code>true</code> if method body comment should
461: * be generated; <code>false</code> otherwise
462: */
463: public void setGenerateMethodBodyComment(boolean generate) {
464: this .generateSourceCodeHints = generate;
465: }
466:
467: /**
468: */
469: public boolean isGenerateMethodBodyComment() {
470: return generateSourceCodeHints;
471: }
472:
473: /**
474: * Sets whether <code>setUp()</code> method should be generated
475: * in test classes being created/updated.
476: * The default is <code>true</code>.
477: *
478: * @param generate <code>true</code> if <code>setUp()</code> method
479: * should be generated; <code>false</code> otherwise
480: * @see #setGenerateTearDown
481: * @see #setGenerateMainMethod
482: */
483: public void setGenerateSetUp(boolean generate) {
484: this .generateTestSetUp = generate;
485: }
486:
487: /**
488: */
489: public boolean isGenerateSetUp() {
490: return generateTestSetUp;
491: }
492:
493: /**
494: * Sets whether <code>tearDown()</code> method should be generated
495: * in test classes being created/updated.
496: * The default is <code>true</code>.
497: *
498: * @param generate <code>true</code> if <code>tearDown()</code> method
499: * should be generated; <code>false</code> otherwise
500: * @see #setGenerateSetUp
501: * @see #setGenerateMainMethod
502: */
503: public void setGenerateTearDown(boolean generate) {
504: this .generateTestTearDown = generate;
505: }
506:
507: /**
508: */
509: public boolean isGenerateTearDown() {
510: return generateTestTearDown;
511: }
512:
513: /**
514: * Sets whether <code>setUp()</code> method should be generated
515: * in test classes being created/updated.
516: * The default is <code>true</code>.
517: *
518: * @param generate <code>true</code> if <code>setUp()</code> method
519: * should be generated; <code>false</code> otherwise
520: * @see #setGenerateTearDown
521: * @see #setGenerateMainMethod
522: */
523: public void setGenerateBefore(boolean generate) {
524: setGenerateSetUp(generate);
525: }
526:
527: /**
528: */
529: public boolean isGenerateBefore() {
530: return isGenerateSetUp();
531: }
532:
533: /**
534: * Sets whether <code>tearDown()</code> method should be generated
535: * in test classes being created/updated.
536: * The default is <code>true</code>.
537: *
538: * @param generate <code>true</code> if <code>tearDown()</code> method
539: * should be generated; <code>false</code> otherwise
540: * @see #setGenerateSetUp
541: * @see #setGenerateMainMethod
542: */
543: public void setGenerateAfter(boolean generate) {
544: setGenerateTearDown(generate);
545: }
546:
547: /**
548: */
549: public boolean isGenerateAfter() {
550: return isGenerateTearDown();
551: }
552:
553: /**
554: * Sets whether <code>setUp()</code> method should be generated
555: * in test classes being created/updated.
556: * The default is <code>true</code>.
557: *
558: * @param generate <code>true</code> if <code>setUp()</code> method
559: * should be generated; <code>false</code> otherwise
560: * @see #setGenerateTearDown
561: * @see #setGenerateMainMethod
562: */
563: public void setGenerateBeforeClass(boolean generate) {
564: this .generateClassSetUp = generate;
565: }
566:
567: /**
568: */
569: public boolean isGenerateBeforeClass() {
570: return generateClassSetUp;
571: }
572:
573: /**
574: * Sets whether <code>tearDown()</code> method should be generated
575: * in test classes being created/updated.
576: * The default is <code>true</code>.
577: *
578: * @param generate <code>true</code> if <code>tearDown()</code> method
579: * should be generated; <code>false</code> otherwise
580: * @see #setGenerateSetUp
581: * @see #setGenerateMainMethod
582: */
583: public void setGenerateAfterClass(boolean generate) {
584: this .generateClassTearDown = generate;
585: }
586:
587: /**
588: */
589: public boolean isGenerateAfterClass() {
590: return generateClassTearDown;
591: }
592:
593: /**
594: * Sets whether static method <code>main(String args[])</code> should
595: * be generated in test classes.
596: * The default is <code>true</code>.
597: *
598: * @param generate <code>true</code> if the method should be generated;
599: * <code>false</code> otherwise
600: * @see #setGenerateSetUp
601: * @see #setGenerateTearDown
602: */
603: public void setGenerateMainMethod(boolean generate) {
604: this .generateMainMethod = generate;
605: }
606:
607: /**
608: */
609: public boolean isGenerateMainMethod() {
610: return generateMainMethod;
611: }
612:
613: /**
614: * Checks whether the given class or at least one of its nested classes
615: * is testable.
616: *
617: * @param compInfo used for {@link CompilationInfo#getElements()}
618: * and {@link CompilationInfo#getTypes()}
619: * @param classElem class to be checked
620: * @return TestabilityResult that isOk, if the class is testable or carries
621: * the information why the class is not testable
622: */
623: public TestabilityResult isClassTestable(CompilationInfo compInfo,
624: TypeElement classElem) {
625: assert classElem != null;
626:
627: TestabilityResult result = isClassTestableSingle(compInfo,
628: classElem);
629:
630: if (result.isTestable()) {
631: return TestabilityResult.OK;
632: }
633:
634: List<? extends Element> enclosedElems = classElem
635: .getEnclosedElements();
636: if (enclosedElems.isEmpty()) {
637: /* Not testable, no contained types - no more chance: */
638: return result;
639: }
640:
641: List<TypeElement> enclosedTypes = ElementFilter
642: .typesIn(enclosedElems);
643: if (enclosedTypes.isEmpty()) {
644: /* Not testable, no contained types - no more chance: */
645: return result;
646: }
647:
648: /* Not testable but maybe one of its nested classes is testable: */
649: List<TypeElement> stack = new ArrayList<TypeElement>(Math.max(
650: 10, enclosedTypes.size()));
651: stack.addAll(enclosedTypes);
652: int stackSize = stack.size();
653:
654: Set<TypeElement> nonTestable = new HashSet<TypeElement>(64);
655: nonTestable.add(classElem);
656:
657: do {
658: TypeElement classToCheck = stack.remove(--stackSize);
659:
660: if (!TopClassFinder.isTestable(classElem)) { //it is an annotation
661: continue;
662: }
663:
664: if (!nonTestable.add(classToCheck)) {
665: continue; //we already know this single class is nontestable
666: }
667:
668: TestabilityResult resultSingle = isClassTestableSingle(
669: compInfo, classToCheck);
670: if (resultSingle.isTestable()) {
671: return TestabilityResult.OK;
672: } else {
673: result = TestabilityResult
674: .combine(result, resultSingle);
675: }
676:
677: enclosedTypes = ElementFilter.typesIn(classToCheck
678: .getEnclosedElements());
679: if (!enclosedTypes.isEmpty()) {
680: stack.addAll(enclosedTypes);
681: stackSize = stack.size();
682: }
683: } while (stackSize != 0);
684:
685: /* So not a single contained class is testable - no more chance: */
686: return result;
687: }
688:
689: /**
690: * Checks whether the given class is testable.
691: *
692: * @param jc class to be checked
693: * @return TestabilityResult that isOk, if the class is testable or carries
694: * the information why the class is not testable
695: */
696: private TestabilityResult isClassTestableSingle(
697: CompilationInfo compInfo, TypeElement classElem) {
698: assert classElem != null;
699:
700: TestabilityResult result = TestabilityResult.OK;
701:
702: /*
703: * If the class is a test class and test classes should be skipped,
704: * do not check nested classes (skip all):
705: */
706: /* Check if the class itself (w/o nested classes) is testable: */
707: Set<Modifier> modifiers = classElem.getModifiers();
708:
709: if (modifiers.contains(PRIVATE))
710: result = TestabilityResult.combine(result,
711: TestabilityResult.PRIVATE_CLASS);
712: if (isSkipTestClasses()
713: && TestUtil.isClassImplementingTestInterface(compInfo,
714: classElem))
715: result = TestabilityResult.combine(result,
716: TestabilityResult.TEST_CLASS);
717: if (isSkipPackagePrivateClasses()
718: && (modifiers.isEmpty() || !EnumSet.copyOf(modifiers)
719: .removeAll(ACCESS_MODIFIERS)))
720: result = TestabilityResult.combine(result,
721: TestabilityResult.PACKAGE_PRIVATE_CLASS);
722: if (isSkipAbstractClasses() && modifiers.contains(ABSTRACT))
723: result = TestabilityResult.combine(result,
724: TestabilityResult.ABSTRACT_CLASS);
725: if (!modifiers.contains(STATIC)
726: && (classElem.getNestingKind() != NestingKind.TOP_LEVEL))
727: result = TestabilityResult.combine(result,
728: TestabilityResult.NONSTATIC_INNER_CLASS);
729: if (!hasTestableMethods(classElem))
730: result = TestabilityResult.combine(result,
731: TestabilityResult.NO_TESTEABLE_METHODS);
732: if (isSkipExceptionClasses()
733: && TestUtil.isClassException(compInfo, classElem))
734: result = TestabilityResult.combine(result,
735: TestabilityResult.EXCEPTION_CLASS);
736:
737: return result;
738: }
739:
740: /**
741: */
742: private boolean hasTestableMethods(TypeElement classElem) {
743: List<? extends Element> enclosedElems = classElem
744: .getEnclosedElements();
745: if (enclosedElems.isEmpty()) {
746: return false;
747: }
748:
749: List<ExecutableElement> methods = ElementFilter
750: .methodsIn(enclosedElems);
751: if (methods.isEmpty()) {
752: return false;
753: }
754:
755: for (ExecutableElement method : methods) {
756: if (isMethodTestable(method)) {
757: return true;
758: }
759: }
760:
761: return false;
762: }
763:
764: /**
765: * Checks whether a test for the given method should be created.
766: * Access modifiers of the given method are compared to this creator's
767: * settings.
768: *
769: * @param m method to be checked
770: * @return <code>true</code> if this creator is configured to create tests
771: * for methods having the given method's access modifiers;
772: * <code>false</code> otherwise
773: */
774: public boolean isMethodTestable(ExecutableElement method) {
775: Set<Modifier> modifiers = method.getModifiers();
776:
777: if (modifiers.isEmpty()) {
778: /*
779: * EnumSet.copyOf(modifiers) may throw an exception if 'modifiers'
780: * is empty.
781: */
782: return isTestPackagePrivateMethods();
783: } else {
784: return (isTestPackagePrivateMethods() && !EnumSet.copyOf(
785: modifiers).removeAll(ACCESS_MODIFIERS))
786: || EnumSet.copyOf(modifiers).removeAll(
787: getMethodAccessModifiers());
788: }
789: }
790:
791: }
|