1 /* 2 * Copyright (C) 2016 The Android Open Source Project 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 package com.android.icu4j.srcgen; 17 18 import com.google.currysrc.api.process.Context; 19 import com.google.currysrc.api.process.Processor; 20 import java.lang.reflect.Modifier; 21 import java.util.List; 22 import java.util.Map; 23 import java.util.TreeMap; 24 import org.eclipse.jdt.core.dom.AST; 25 import org.eclipse.jdt.core.dom.CompilationUnit; 26 import org.eclipse.jdt.core.dom.ImportDeclaration; 27 import org.eclipse.jdt.core.dom.Name; 28 import org.eclipse.jdt.core.dom.QualifiedName; 29 import org.eclipse.jdt.core.dom.SingleMemberAnnotation; 30 import org.eclipse.jdt.core.dom.Type; 31 import org.eclipse.jdt.core.dom.TypeDeclaration; 32 import org.eclipse.jdt.core.dom.TypeLiteral; 33 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; 34 import org.eclipse.jdt.core.dom.rewrite.ListRewrite; 35 36 /** 37 * Adds @RunWith annotations to test classes. 38 * 39 * <p>A class that extends {@code TestFmwk.TestGroup} will have an annotation 40 * {@code @RunWith(IcuTestGroupRunner.class)} added and a class that extends {@code TestFmwk} will 41 * have an annotation {@code @RunWith(IcuTestFmwkRunner.class)} added. 42 * 43 * <p>Ideally, this would operate on an AST that has resolved type information so that it was 44 * possible to traverse the class hierarchy to identify classes that are derived both directly and 45 * indirectly from the test classes. Unfortunately, that approach is very, very time consuming, an 46 * order of magnitude (if not two) slower than not resolving the types. Therefore, it was quicker 47 * to simply iteratively annotate and run the tests to find the set of direct dependencies and hard 48 * code them in here. That could be an issue if this had to deal with many changes in the test 49 * classes but this code should only have a very short lifespan given that the ICU4J team is 50 * already well on their way to porting the tests over to JUnit. 51 */ 52 class RunWithAnnotator implements Processor { 53 54 private static final String RUN_WITH_CLASS_NAME = "org.junit.runner.RunWith"; 55 56 private static final String[] TEST_FMWK_BASE_CLASSES = { 57 "BidiTest", 58 "CalendarTest", 59 "android.icu.dev.test.TestFmwk", 60 "CompatibilityTest", 61 "CoverageTest", 62 "LanguageTestRoot", 63 "ModuleTest", 64 "TestFmwk", 65 "TestFmwk.TestGroup", 66 "TestGroup", 67 "TransliteratorTest", 68 }; 69 70 private static final String[] TEST_GROUP_BASE_CLASSES = { 71 "TestFmwk.TestGroup", 72 "TestGroup", 73 }; 74 75 private static Map<String, String> BASE_CLASS_2_RUNNER_CLASS = new TreeMap<>(); 76 static { 77 for (String name : TEST_FMWK_BASE_CLASSES) { 78 BASE_CLASS_2_RUNNER_CLASS.put(name, "android.icu.junit.IcuTestFmwkRunner"); 79 } 80 for (String name : TEST_GROUP_BASE_CLASSES) { 81 BASE_CLASS_2_RUNNER_CLASS.put(name, "android.icu.junit.IcuTestGroupRunner"); 82 } 83 } 84 85 @Override 86 public void process(Context context, CompilationUnit cu) { 87 List types = cu.types(); 88 ASTRewrite rewrite = context.rewrite(); 89 boolean imported = false; 90 for (Object type : types) { 91 if (type instanceof TypeDeclaration) { 92 TypeDeclaration typeDeclaration = (TypeDeclaration) type; 93 imported = annotateTypeDeclaration(cu, rewrite, typeDeclaration, true, imported); 94 } 95 } 96 } 97 98 private boolean annotateTypeDeclaration(CompilationUnit cu, 99 ASTRewrite rewrite, TypeDeclaration typeDeclaration, boolean topLevelClass, 100 boolean imported) { 101 102 int modifiers = typeDeclaration.getModifiers(); 103 if ((topLevelClass || Modifier.isStatic(modifiers)) && Modifier.isPublic(modifiers) 104 && !Modifier.isAbstract(modifiers)) { 105 Type superClassType = typeDeclaration.getSuperclassType(); 106 if (superClassType != null) { 107 String name = superClassType.toString(); 108 String runnerClass = BASE_CLASS_2_RUNNER_CLASS.get(name); 109 if (runnerClass != null) { 110 addRunWithAnnotation(cu, rewrite, typeDeclaration, runnerClass, imported); 111 imported = true; 112 } 113 } 114 } 115 116 for (TypeDeclaration innerClass : typeDeclaration.getTypes()) { 117 imported = annotateTypeDeclaration(cu, rewrite, innerClass, false, imported); 118 } 119 120 return imported; 121 } 122 123 private boolean addRunWithAnnotation( 124 CompilationUnit cu, ASTRewrite rewrite, TypeDeclaration type, String runnerClass, 125 boolean imported) { 126 127 AST ast = cu.getAST(); 128 129 QualifiedName qRunWith = (QualifiedName) ast.newName(RUN_WITH_CLASS_NAME); 130 QualifiedName qRunner = (QualifiedName) ast.newName(runnerClass); 131 if (!imported) { 132 appendImport(cu, rewrite, qRunWith); 133 appendImport(cu, rewrite, qRunner); 134 } 135 String runWithName = qRunWith.getName().getIdentifier(); 136 String runnerName = qRunner.getName().getIdentifier(); 137 138 SingleMemberAnnotation annotation = ast.newSingleMemberAnnotation(); 139 annotation.setTypeName(ast.newSimpleName(runWithName)); 140 141 TypeLiteral junit4Literal = ast.newTypeLiteral(); 142 junit4Literal.setType(ast.newSimpleType(ast.newSimpleName(runnerName))); 143 annotation.setValue(junit4Literal); 144 145 ListRewrite lrw = rewrite.getListRewrite(type, type.getModifiersProperty()); 146 lrw.insertFirst(annotation, null); 147 148 return imported; 149 } 150 151 private void appendImport(CompilationUnit cu, ASTRewrite rewriter, Name name) { 152 ListRewrite lrw = rewriter.getListRewrite(cu, CompilationUnit.IMPORTS_PROPERTY); 153 AST ast = cu.getAST(); 154 ImportDeclaration importDeclaration = ast.newImportDeclaration(); 155 importDeclaration.setName(name); 156 lrw.insertLast(importDeclaration, null); 157 } 158 159 } 160