/* * Copyright 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.google.android.testing.mocking; import javassist.CannotCompileException; import javassist.CtClass; import javassist.NotFoundException; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; /** * Mock Generator for Android Framework classes. * *

* This class will pull pre-defined mocks for Android framework classes. This is * needed because a project might build against version X and then run against * version Y, where the specification of the class being mocked will have * changed, rendering the mock invalid. * * @author swoodward@google.com (Stephen Woodward) */ public class AndroidFrameworkMockGenerator { private static final String LIB_FOLDER = "lib"; private static final String JAR_NAME = "android_"; /** * Returns a set of mock support classes for the specified Class for all versions of * the Android SDK. If the requested class is not part of the Android framework, then the class * will not be found and an exception will be thrown. * * @param clazz the class to mock. * @return All available mock support classes (for all known Android SDKs) for * the requested Class. */ public List getMocksForClass(Class clazz) throws ClassNotFoundException, IOException { List> prebuiltClasses = getPrebuiltClassesFor(clazz); List classList = new ArrayList(); for (Class prebuiltClass : prebuiltClasses) { try { CtClass ctClass = AndroidMockGenerator.getClassPool().get(prebuiltClass.getName()); classList.add(new GeneratedClassFile(ctClass.getName(), ctClass.toBytecode())); } catch (NotFoundException e) { throw new ClassNotFoundException("Missing class while fetching prebuilt mocks: " + prebuiltClass.getName(), e); } catch (CannotCompileException e) { throw new RuntimeException("Internal Error copying a prebuilt mock: " + prebuiltClass.getName(), e); } } return classList; } private List> getPrebuiltClassesFor(Class clazz) throws ClassNotFoundException { List> classes = new ArrayList>(); SdkVersion[] versions = SdkVersion.getAllVersions(); for (SdkVersion sdkVersion : versions) { classes.add(Class.forName(FileUtils.getSubclassNameFor(clazz, sdkVersion))); classes.add(Class.forName(FileUtils.getInterfaceNameFor(clazz, sdkVersion))); } return classes; } /** * @return a List of {@link GeneratedClassFile} objects representing the mocks for the specified * class for a single version of the Android SDK. */ public List createMocksForClass(Class clazz, SdkVersion version) throws ClassNotFoundException, IOException, CannotCompileException { AndroidMockGenerator mockGenerator = new AndroidMockGenerator(); List mocks = new ArrayList(); mocks.addAll(mockGenerator.createMocksForClass(clazz, version)); return mocks; } /** * @return A list of all class files inside the provided jar file. */ List> getClassList(JarFile jar) throws ClassNotFoundException { return getClassList(Collections.list(jar.entries())); } List> getClassList(Collection jarEntries) throws ClassNotFoundException { List> classList = new ArrayList>(); for (JarEntry jarEntry : jarEntries) { if (jarEntryIsClassFile(jarEntry)) { classList.add(Class.forName(FileUtils.getClassNameFor(jarEntry.getName()))); } } return classList; } /** * @return true if the provided JarEntry represents a class file. */ boolean jarEntryIsClassFile(JarEntry jarEntry) { return jarEntry.getName().endsWith(".class"); } /** * @return the Android framework jar file for the specified {@link SdkVersion}. */ static String getJarFileNameForVersion(SdkVersion version) { return new StringBuilder() .append(LIB_FOLDER) .append(File.separator) .append("android") .append(File.separator) .append(JAR_NAME) .append(version.getVersionName()) .append(".jar").toString(); } private static Set generateMocks( SdkVersion version, JarFile jar) throws ClassNotFoundException, IOException, CannotCompileException { AndroidFrameworkMockGenerator mockGenerator = new AndroidFrameworkMockGenerator(); List> classList = mockGenerator.getClassList(jar); Set classes = new HashSet(); for (Class clazz : classList) { classes.addAll(mockGenerator.createMocksForClass(clazz, version)); } return classes; } private static JarFile getJarFile(SdkVersion version) throws IOException { File jarFile = new File(getJarFileNameForVersion(version)).getAbsoluteFile(); System.out.println("Using Jar File: " + jarFile.getAbsolutePath()); return new JarFile(jarFile); } public static void main(String[] args) { try { String outputFolderName = args[0]; SdkVersion version = SdkVersion.getVersionFor(Integer.parseInt(args[1])); System.out.println("Generating files for " + version.getPackagePrefix()); JarFile jar = getJarFile(version); Set classes = generateMocks(version, jar); for (GeneratedClassFile clazz : classes) { FileUtils.saveClassToFolder(clazz, outputFolderName); } } catch (Exception e) { throw new RuntimeException("Internal error generating framework mocks", e); } } }