1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.0 (the "License"); you 5 * may not use this file except in compliance with the License. You may obtain a 6 * copy of the License at 7 * 8 * http://www.eclipse.org/org/documents/epl-v10.php 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.android.ide.eclipse.tests.functests.sampleProjects; 17 18 import com.android.ide.eclipse.adt.AdtUtils; 19 import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectCreator; 20 import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectWizardState; 21 import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectWizardState.Mode; 22 import com.android.ide.eclipse.tests.SdkTestCase; 23 import com.android.sdklib.IAndroidTarget; 24 import com.android.sdklib.SdkConstants; 25 26 import org.eclipse.core.resources.IMarker; 27 import org.eclipse.core.resources.IProject; 28 import org.eclipse.core.resources.IResource; 29 import org.eclipse.core.resources.IResourceChangeEvent; 30 import org.eclipse.core.resources.IResourceChangeListener; 31 import org.eclipse.core.resources.IResourceDelta; 32 import org.eclipse.core.resources.IResourceDeltaVisitor; 33 import org.eclipse.core.resources.ResourcesPlugin; 34 import org.eclipse.core.runtime.CoreException; 35 import org.eclipse.core.runtime.NullProgressMonitor; 36 import org.eclipse.jface.operation.IRunnableContext; 37 import org.eclipse.jface.operation.IRunnableWithProgress; 38 import org.eclipse.swt.widgets.Display; 39 40 import java.io.File; 41 import java.lang.reflect.InvocationTargetException; 42 import java.util.logging.Level; 43 import java.util.logging.Logger; 44 45 /** 46 * Test case that verifies all SDK sample projects can be imported, and built in 47 * Eclipse. 48 * <p/> 49 * TODO: add support for deploying apps onto emulator and verifying successful 50 * execution there 51 * 52 */ 53 public class SampleProjectTest extends SdkTestCase { 54 55 private static final Logger sLogger = Logger.getLogger(SampleProjectTest.class.getName()); 56 57 /** 58 * Finds all samples projects in set SDK and verify they can be built in Eclipse. 59 * <p/> 60 * TODO: add install and run on emulator test 61 * @throws CoreException 62 */ 63 public void testSamples() throws CoreException { 64 // TODO: For reporting purposes, it would be better if a separate test success or failure 65 // could be reported for each sample 66 IAndroidTarget[] targets = getSdk().getTargets(); 67 for (IAndroidTarget target : targets) { 68 doTestSamplesForTarget(target); 69 } 70 } 71 72 private void doTestSamplesForTarget(IAndroidTarget target) throws CoreException { 73 String path = target.getPath(IAndroidTarget.SAMPLES); 74 File samples = new File(path); 75 if (samples.isDirectory()) { 76 File[] files = samples.listFiles(); 77 for (File file : files) { 78 if (file.isDirectory()) { 79 doTestSampleProject(file.getName(), file.getAbsolutePath(), target); 80 } 81 } 82 } 83 } 84 85 /** 86 * Tests the sample project with the given name 87 * 88 * @param target - SDK target of project 89 * @param name - name of sample project to test 90 * @param path - absolute file system path 91 * @throws CoreException 92 */ 93 private void doTestSampleProject(String name, String path, IAndroidTarget target) 94 throws CoreException { 95 IProject iproject = null; 96 try { 97 sLogger.log(Level.INFO, String.format("Testing sample %s for target %s", name, 98 target.getName())); 99 100 prepareProject(path, target); 101 102 IRunnableContext context = new IRunnableContext() { 103 public void run(boolean fork, boolean cancelable, IRunnableWithProgress runnable) 104 throws InvocationTargetException, InterruptedException { 105 runnable.run(new NullProgressMonitor()); 106 } 107 }; 108 NewProjectWizardState state = new NewProjectWizardState(Mode.SAMPLE); 109 state.projectName = name; 110 state.target = target; 111 state.packageName = "com.android.samples"; 112 state.activityName = name; 113 state.applicationName = name; 114 state.chosenSample = new File(path); 115 state.useDefaultLocation = false; 116 state.createActivity = false; 117 118 NewProjectCreator creator = new NewProjectCreator(state, context); 119 creator.createAndroidProjects(); 120 iproject = validateProjectExists(name); 121 validateNoProblems(iproject); 122 } 123 catch (CoreException e) { 124 sLogger.log(Level.SEVERE, 125 String.format("Unexpected exception when creating sample project %s " + 126 "for target %s", name, target.getName())); 127 throw e; 128 } finally { 129 if (iproject != null) { 130 iproject.delete(false, true, new NullProgressMonitor()); 131 } 132 } 133 } 134 135 private void prepareProject(String path, IAndroidTarget target) { 136 if (target.getVersion().isPreview()) { 137 // need to explicitly set preview's version in manifest for project to compile 138 final String manifestPath = path + File.separatorChar + 139 SdkConstants.FN_ANDROID_MANIFEST_XML; 140 AndroidManifestWriter manifestWriter = 141 AndroidManifestWriter.parse(manifestPath); 142 assertNotNull(String.format("could not read manifest %s", manifestPath), 143 manifestWriter); 144 assertTrue(manifestWriter.setMinSdkVersion(target.getVersion().getApiString())); 145 } 146 } 147 148 private IProject validateProjectExists(String name) { 149 IProject iproject = getIProject(name); 150 assertTrue(String.format("%s project not created", name), iproject.exists()); 151 assertTrue(String.format("%s project not opened", name), iproject.isOpen()); 152 return iproject; 153 } 154 155 private IProject getIProject(String name) { 156 IProject iproject = ResourcesPlugin.getWorkspace().getRoot() 157 .getProject(name); 158 return iproject; 159 } 160 161 private void validateNoProblems(IProject iproject) throws CoreException { 162 waitForBuild(iproject); 163 164 boolean hasErrors = false; 165 StringBuilder failureBuilder = new StringBuilder(String.format("%s project has errors:", 166 iproject.getName())); 167 IMarker[] markers = iproject.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE); 168 if (markers != null && markers.length > 0) { 169 // the project has marker(s). even though they are "problem" we 170 // don't know their severity. so we loop on them and figure if they 171 // are warnings or errors 172 for (IMarker m : markers) { 173 int s = m.getAttribute(IMarker.SEVERITY, -1); 174 if (s == IMarker.SEVERITY_ERROR) { 175 hasErrors = true; 176 failureBuilder.append("\n"); 177 failureBuilder.append(m.getAttribute(IMarker.MESSAGE, "")); 178 } 179 } 180 } 181 failureBuilder.append("Project location: " + AdtUtils.getAbsolutePath(iproject)); 182 assertFalse(failureBuilder.toString(), hasErrors); 183 } 184 185 /** 186 * Waits for build to complete. 187 * 188 * @param iproject 189 */ 190 private void waitForBuild(final IProject iproject) { 191 192 final BuiltProjectDeltaVisitor deltaVisitor = new BuiltProjectDeltaVisitor(iproject); 193 IResourceChangeListener newBuildListener = new IResourceChangeListener() { 194 195 public void resourceChanged(IResourceChangeEvent event) { 196 try { 197 event.getDelta().accept(deltaVisitor); 198 } 199 catch (CoreException e) { 200 fail(); 201 } 202 } 203 204 }; 205 iproject.getWorkspace().addResourceChangeListener(newBuildListener, 206 IResourceChangeEvent.POST_BUILD); 207 208 // poll build listener to determine when build is done 209 // loop max of 1200 times * 50 ms = 60 seconds 210 final int maxWait = 1200; 211 for (int i=0; i < maxWait; i++) { 212 if (deltaVisitor.isProjectBuilt()) { 213 return; 214 } 215 try { 216 Thread.sleep(50); 217 } 218 catch (InterruptedException e) { 219 // ignore 220 } 221 if (Display.getCurrent() != null) { 222 Display.getCurrent().readAndDispatch(); 223 } 224 } 225 226 sLogger.log(Level.SEVERE, "expected build event never happened?"); 227 fail(String.format("Expected build event never happened for %s", iproject.getName())); 228 } 229 230 /** 231 * Scans a given IResourceDelta looking for a "build event" change for given IProject 232 * 233 */ 234 private class BuiltProjectDeltaVisitor implements IResourceDeltaVisitor { 235 236 private IProject mIProject; 237 private boolean mIsBuilt; 238 239 public BuiltProjectDeltaVisitor(IProject iproject) { 240 mIProject = iproject; 241 mIsBuilt = false; 242 } 243 244 public boolean visit(IResourceDelta delta) { 245 if (mIProject.equals(delta.getResource())) { 246 setBuilt(true); 247 return false; 248 } 249 return true; 250 } 251 252 private synchronized void setBuilt(boolean b) { 253 mIsBuilt = b; 254 } 255 256 public synchronized boolean isProjectBuilt() { 257 return mIsBuilt; 258 } 259 } 260 } 261