• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2010 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 package com.google.android.testing.mocking;
17 
18 import javassist.CannotCompileException;
19 import javassist.CtClass;
20 import javassist.NotFoundException;
21 
22 import java.io.File;
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Set;
30 import java.util.jar.JarEntry;
31 import java.util.jar.JarFile;
32 
33 /**
34  * Mock Generator for Android Framework classes.
35  *
36  * <p>
37  * This class will pull pre-defined mocks for Android framework classes. This is
38  * needed because a project might build against version X and then run against
39  * version Y, where the specification of the class being mocked will have
40  * changed, rendering the mock invalid.
41  *
42  * @author swoodward@google.com (Stephen Woodward)
43  */
44 public class AndroidFrameworkMockGenerator {
45   private static final String LIB_FOLDER = "lib";
46   private static final String JAR_NAME = "android_";
47 
48   /**
49    * Returns a set of mock support classes for the specified Class for all versions of
50    * the Android SDK. If the requested class is not part of the Android framework, then the class
51    * will not be found and an exception will be thrown.
52    *
53    * @param clazz the class to mock.
54    * @return All available mock support classes (for all known Android SDKs) for
55    *         the requested Class.
56    */
getMocksForClass(Class<?> clazz)57   public List<GeneratedClassFile> getMocksForClass(Class<?> clazz) throws ClassNotFoundException,
58       IOException {
59     List<Class<?>> prebuiltClasses = getPrebuiltClassesFor(clazz);
60     List<GeneratedClassFile> classList = new ArrayList<GeneratedClassFile>();
61     for (Class<?> prebuiltClass : prebuiltClasses) {
62       try {
63         CtClass ctClass = AndroidMockGenerator.getClassPool().get(prebuiltClass.getName());
64         classList.add(new GeneratedClassFile(ctClass.getName(), ctClass.toBytecode()));
65       } catch (NotFoundException e) {
66         throw new ClassNotFoundException("Missing class while fetching prebuilt mocks: "
67             + prebuiltClass.getName(), e);
68       } catch (CannotCompileException e) {
69         throw new RuntimeException("Internal Error copying a prebuilt mock: "
70             + prebuiltClass.getName(), e);
71       }
72     }
73     return classList;
74   }
75 
getPrebuiltClassesFor(Class<?> clazz)76   private List<Class<?>> getPrebuiltClassesFor(Class<?> clazz) throws ClassNotFoundException {
77     List<Class<?>> classes = new ArrayList<Class<?>>();
78     SdkVersion[] versions = SdkVersion.getAllVersions();
79     for (SdkVersion sdkVersion : versions) {
80       classes.add(Class.forName(FileUtils.getSubclassNameFor(clazz, sdkVersion)));
81       classes.add(Class.forName(FileUtils.getInterfaceNameFor(clazz, sdkVersion)));
82     }
83     return classes;
84   }
85 
86   /**
87    * @return a List of {@link GeneratedClassFile} objects representing the mocks for the specified
88    *         class for a single version of the Android SDK.
89    */
createMocksForClass(Class<?> clazz, SdkVersion version)90   public List<GeneratedClassFile> createMocksForClass(Class<?> clazz, SdkVersion version)
91       throws ClassNotFoundException, IOException, CannotCompileException {
92     AndroidMockGenerator mockGenerator = new AndroidMockGenerator();
93     List<GeneratedClassFile> mocks = new ArrayList<GeneratedClassFile>();
94     mocks.addAll(mockGenerator.createMocksForClass(clazz, version));
95     return mocks;
96   }
97 
98   /**
99    * @return A list of all class files inside the provided jar file.
100    */
getClassList(JarFile jar)101   List<Class<?>> getClassList(JarFile jar) throws ClassNotFoundException {
102     return getClassList(Collections.list(jar.entries()));
103   }
104 
getClassList(Collection<JarEntry> jarEntries)105   List<Class<?>> getClassList(Collection<JarEntry> jarEntries) throws ClassNotFoundException {
106     List<Class<?>> classList = new ArrayList<Class<?>>();
107     for (JarEntry jarEntry : jarEntries) {
108       if (jarEntryIsClassFile(jarEntry)) {
109         classList.add(Class.forName(FileUtils.getClassNameFor(jarEntry.getName())));
110       }
111     }
112     return classList;
113   }
114 
115   /**
116    * @return true if the provided JarEntry represents a class file.
117    */
jarEntryIsClassFile(JarEntry jarEntry)118   boolean jarEntryIsClassFile(JarEntry jarEntry) {
119     return jarEntry.getName().endsWith(".class");
120   }
121 
122   /**
123    * @return the Android framework jar file for the specified {@link SdkVersion}.
124    */
getJarFileNameForVersion(SdkVersion version)125   static String getJarFileNameForVersion(SdkVersion version) {
126     return new StringBuilder()
127         .append(LIB_FOLDER)
128         .append(File.separator)
129         .append("android")
130         .append(File.separator)
131         .append(JAR_NAME)
132         .append(version.getVersionName())
133         .append(".jar").toString();
134   }
135 
generateMocks( SdkVersion version, JarFile jar)136   private static Set<GeneratedClassFile> generateMocks(
137       SdkVersion version, JarFile jar)
138       throws ClassNotFoundException, IOException, CannotCompileException {
139     AndroidFrameworkMockGenerator mockGenerator = new AndroidFrameworkMockGenerator();
140     List<Class<?>> classList = mockGenerator.getClassList(jar);
141     Set<GeneratedClassFile> classes = new HashSet<GeneratedClassFile>();
142     for (Class<?> clazz : classList) {
143       classes.addAll(mockGenerator.createMocksForClass(clazz, version));
144     }
145     return classes;
146   }
147 
getJarFile(SdkVersion version)148   private static JarFile getJarFile(SdkVersion version) throws IOException {
149     File jarFile = new File(getJarFileNameForVersion(version)).getAbsoluteFile();
150     System.out.println("Using Jar File: " + jarFile.getAbsolutePath());
151     return new JarFile(jarFile);
152   }
153 
main(String[] args)154   public static void main(String[] args) {
155     try {
156       String outputFolderName = args[0];
157       SdkVersion version = SdkVersion.getVersionFor(Integer.parseInt(args[1]));
158       System.out.println("Generating files for " + version.getPackagePrefix());
159 
160       JarFile jar = getJarFile(version);
161 
162       Set<GeneratedClassFile> classes = generateMocks(version, jar);
163       for (GeneratedClassFile clazz : classes) {
164         FileUtils.saveClassToFolder(clazz, outputFolderName);
165       }
166     } catch (Exception e) {
167       throw new RuntimeException("Internal error generating framework mocks", e);
168     }
169   }
170 }
171