aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtTestData.java135
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AllTests.java40
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/EclipseTestCollector.java121
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/FuncTests.java48
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/SdkLoadingTestCase.java117
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/UnitTests.java61
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java328
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/sampleProjects/AndroidManifestWriter.java131
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/sampleProjects/SampleProjectTest.java263
9 files changed, 1244 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtTestData.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtTestData.java
new file mode 100644
index 000000000..c86f4cf5f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtTestData.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.android.ide.eclipse.tests;
+
+import com.android.SdkConstants;
+import com.android.ide.eclipse.adt.AdtConstants;
+import com.android.ide.eclipse.adt.AdtPlugin;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.Platform;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.logging.Logger;
+
+/**
+ * Helper class for retrieving test data
+ * <p/>
+ * All tests which need to retrieve paths to test data files should go through this class.
+ */
+public class AdtTestData {
+
+ /** singleton instance */
+ private static AdtTestData sInstance = null;
+ private static final Logger sLogger = Logger.getLogger(AdtTestData.class.getName());
+
+ /** The prefered directory separator to use. */
+ private static final String DIR_SEP_STR = "/";
+ private static final char DIR_SEP_CHAR = '/';
+
+ /** The absolute file path to the plugin's contents. */
+ private String mOsRootDataPath;
+
+ private AdtTestData() {
+ // can set test_data env variable to override default behavior of
+ // finding data using class loader
+ // useful when running in plugin environment, where test data is inside
+ // bundled jar, and must be extracted to temp filesystem location to be
+ // accessed normally
+ mOsRootDataPath = System.getProperty("test_data");
+ if (mOsRootDataPath == null) {
+ sLogger.info("Cannot find test_data environment variable, init to class loader");
+ URL url = this.getClass().getClassLoader().getResource("."); //$NON-NLS-1$
+
+ if (Platform.isRunning()) {
+ sLogger.info("Running as an Eclipse Plug-in JUnit test, using FileLocator");
+ try {
+ mOsRootDataPath = FileLocator.resolve(url).getFile();
+ if (SdkConstants.currentPlatform() == SdkConstants.PLATFORM_WINDOWS) {
+ // Fix the path returned by the URL resolver
+ // so that it actually works on Windows.
+
+ // First, Windows paths don't start with a / especially
+ // if they contain a drive spec such as C:/...
+ int pos = mOsRootDataPath.indexOf(':');
+ if (pos > 0 && mOsRootDataPath.charAt(0) == '/') {
+ mOsRootDataPath = mOsRootDataPath.substring(1);
+ }
+
+ // Looking for "." probably inserted a /./, so clean it up
+ mOsRootDataPath = mOsRootDataPath.replace("/./", "/");
+ }
+ } catch (IOException e) {
+ sLogger.warning("IOException while using FileLocator, reverting to url");
+ mOsRootDataPath = url.getFile();
+ }
+ } else {
+ sLogger.info("Running as an plain JUnit test, using url as-is");
+ mOsRootDataPath = url.getFile();
+ }
+ }
+
+ if (mOsRootDataPath.equals(AdtConstants.WS_SEP)) {
+ sLogger.warning("Resource data not found using class loader!, Defaulting to no path");
+ }
+
+ if (File.separatorChar == '\\') {
+ // On Windows, uniformize all separators to use the / convention
+ mOsRootDataPath.replace('\\', DIR_SEP_CHAR);
+ }
+
+ if (!mOsRootDataPath.endsWith(File.separator) && !mOsRootDataPath.endsWith(DIR_SEP_STR)) {
+ sLogger.info("Fixing test_data env variable (does not end with path separator)");
+ mOsRootDataPath += DIR_SEP_STR;
+ }
+ }
+
+ /** Get the singleton instance of AdtTestData */
+ public static AdtTestData getInstance() {
+ if (sInstance == null) {
+ sInstance = new AdtTestData();
+ }
+ return sInstance;
+ }
+
+ /**
+ * Returns the absolute file path to a file located in this plugin.
+ *
+ * @param osRelativePath {@link String} path to file contained in plugin. Must
+ * use path separators appropriate to host OS
+ *
+ * @return absolute OS path to test file
+ */
+ public String getTestFilePath(String osRelativePath) {
+ File path = new File(mOsRootDataPath, osRelativePath);
+
+ if (!path.exists()) {
+ // On Windows at least this ends up using the wrong plugin path.
+ String pkgAdt = AdtPlugin .class.getPackage().getName();
+ String pkgTests = AdtTestData.class.getPackage().getName();
+
+ if (mOsRootDataPath.contains(pkgAdt)) {
+ path = new File(mOsRootDataPath.replace(pkgAdt, pkgTests), osRelativePath);
+ }
+
+ assert path.exists();
+ }
+
+ return path.getAbsolutePath();
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AllTests.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AllTests.java
new file mode 100644
index 000000000..5386142a8
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AllTests.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.android.ide.eclipse.tests;
+
+import junit.framework.TestSuite;
+
+
+/**
+ * Container TestSuite for all eclipse tests, both functional and unit
+ */
+public class AllTests extends TestSuite {
+
+ public AllTests() {
+
+ }
+
+ /**
+ * Returns a suite of test cases to be run.
+ */
+ public static TestSuite suite() {
+ TestSuite suite = new TestSuite();
+ suite.addTest(FuncTests.suite());
+ suite.addTest(UnitTests.suite());
+ return suite;
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/EclipseTestCollector.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/EclipseTestCollector.java
new file mode 100644
index 000000000..6bbc955ee
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/EclipseTestCollector.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.android.ide.eclipse.tests;
+
+import org.eclipse.core.runtime.Plugin;
+
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.util.Enumeration;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Class for collecting all test cases in an eclipse plugin
+ *
+ */
+public class EclipseTestCollector {
+
+ /**
+ * Constructor
+ */
+ public EclipseTestCollector() {
+
+ }
+
+ /**
+ * Searches through given plugin, adding all TestCase classes to given suite
+ * @param suite - TestSuite to add to
+ * @param plugin - Plugin to search for tests
+ * @param expectedPackage - expected package for tests. Only test classes
+ * that start with this package name will be added to suite
+ */
+ @SuppressWarnings({"cast", "unchecked"})
+ public void addTestCases(TestSuite suite, Plugin plugin, String expectedPackage) {
+ if (plugin != null) {
+ Enumeration<?> entries = plugin.getBundle().findEntries("/", "*.class", true);
+
+ while (entries.hasMoreElements()) {
+ URL entry = (URL)entries.nextElement();
+ String filePath = entry.getPath().replace(".class", "");
+ try {
+ Class<?> testClass = getClass(filePath, expectedPackage);
+ if (isTestClass(testClass)) {
+ // In Eclipse 3.6 RCP Windows-x64, the signature has changed from
+ // addTestSuite(Class)
+ // to:
+ // addTestSuite(Class<? extends TestCase>)
+ // which is enough to create an error. To solve it, we cast into the
+ // generics expected by the JUnit framework used by 3.6 and suppress the
+ // warnings generated by the compiler under 3.5
+ suite.addTestSuite((Class<? extends TestCase>)testClass);
+ }
+ }
+ catch (ClassNotFoundException e) {
+ // ignore, this is not the class we're looking for
+ //sLogger.log(Level.INFO, "Could not load class " + filePath);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns true if given class should be added to suite
+ */
+ protected boolean isTestClass(Class<?> testClass) {
+ return TestCase.class.isAssignableFrom(testClass) &&
+ Modifier.isPublic(testClass.getModifiers()) &&
+ hasPublicConstructor(testClass);
+ }
+
+ /**
+ * Returns true if given class has a public constructor
+ */
+ @SuppressWarnings({"unchecked", "cast"})
+ protected boolean hasPublicConstructor(Class<?> testClass) {
+ try {
+ // In Eclipse 3.6 RCP Windows-x64, the signature has changed from
+ // getTestConstructor(Class)
+ // to:
+ // getTestConstructor(Class<? extends TestCase>)
+ // which is enough to create an error. To solve it, we cast into the
+ // generics expected by the JUnit framework used by 3.6 and suppress the
+ // warnings generated by the compiler under 3.5
+ TestSuite.getTestConstructor((Class<? extends TestCase>) testClass);
+ } catch(NoSuchMethodException e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Load the class given by the plugin aka bundle file path
+ * @param filePath - path of class in bundle
+ * @param expectedPackage - expected package of class
+ * @throws ClassNotFoundException
+ */
+ protected Class<?> getClass(String filePath, String expectedPackage) throws ClassNotFoundException {
+ String dotPath = filePath.replace('/', '.');
+ // remove the output folders, by finding where package name starts
+ int index = dotPath.indexOf(expectedPackage);
+ if (index == -1) {
+ throw new ClassNotFoundException();
+ }
+ String packagePath = dotPath.substring(index);
+ return Class.forName(packagePath);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/FuncTests.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/FuncTests.java
new file mode 100644
index 000000000..efa880126
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/FuncTests.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.android.ide.eclipse.tests;
+
+import com.android.ide.eclipse.tests.functests.layoutRendering.ApiDemosRenderingTest;
+
+import junit.framework.TestSuite;
+
+/**
+ * Container TestSuite for all eclipse tests to be run
+ */
+
+public class FuncTests extends TestSuite {
+
+ static final String FUNC_TEST_PACKAGE = "com.android.ide.eclipse.tests.functests";
+
+ public FuncTests() {
+
+ }
+
+ /**
+ * Returns a suite of test cases to be run.
+ * Needed for JUnit3 compliant command line test runner
+ */
+ public static TestSuite suite() {
+ TestSuite suite = new TestSuite();
+
+ // TODO: uncomment this when 'gen' folder error on create is fixed
+ // suite.addTestSuite(SampleProjectTest.class);
+ suite.addTestSuite(ApiDemosRenderingTest.class);
+
+ return suite;
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/SdkLoadingTestCase.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/SdkLoadingTestCase.java
new file mode 100644
index 000000000..65ce8e1d7
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/SdkLoadingTestCase.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.android.ide.eclipse.tests;
+
+import com.android.ide.common.sdk.LoadStatus;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetParser;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.sdklib.IAndroidTarget;
+import com.android.testutils.SdkTestCase;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+
+/**
+ * A test case which uses the SDK loaded by the ADT plugin.
+ */
+public abstract class SdkLoadingTestCase extends SdkTestCase {
+
+ private Sdk mSdk;
+
+ protected SdkLoadingTestCase() {
+ }
+
+ /**
+ * Retrieve the {@link Sdk} under test.
+ */
+ protected Sdk getSdk() {
+ if (mSdk == null) {
+ mSdk = loadSdk();
+ assertNotNull(mSdk);
+ validateSdk(mSdk);
+ }
+ return mSdk;
+ }
+
+ /**
+ * Gets the current SDK from ADT, waiting if necessary.
+ */
+ private Sdk loadSdk() {
+ AdtPlugin adt = AdtPlugin.getDefault();
+
+ // We'll never get an AdtPlugin object when running this with the
+ // non-Eclipse jUnit test runner.
+ if (adt == null) {
+ return null;
+ }
+
+ // We'll never break out of the SDK load-wait-loop if the AdtPlugin doesn't
+ // actually have a valid SDK location because it won't have started an async load:
+ String sdkLocation = AdtPrefs.getPrefs().getOsSdkFolder();
+ assertTrue("No valid SDK installation is set; for tests you typically need to set the"
+ + " environment variable ADT_TEST_SDK_PATH to point to an SDK folder",
+ sdkLocation != null && sdkLocation.length() > 0);
+
+ Object sdkLock = Sdk.getLock();
+ LoadStatus loadStatus = LoadStatus.LOADING;
+ // wait for ADT to load the SDK on a separate thread
+ // loop max of 600 times * 200 ms = 2 minutes
+ final int maxWait = 600;
+ for (int i=0; i < maxWait && loadStatus == LoadStatus.LOADING; i++) {
+ try {
+ Thread.sleep(200);
+ }
+ catch (InterruptedException e) {
+ // ignore
+ }
+ synchronized (sdkLock) {
+ loadStatus = adt.getSdkLoadStatus();
+ }
+ }
+ Sdk sdk = null;
+ synchronized (sdkLock) {
+ assertEquals(LoadStatus.LOADED, loadStatus);
+ sdk = Sdk.getCurrent();
+ }
+ assertNotNull(sdk);
+ return sdk;
+ }
+
+ protected boolean validateSdk(IAndroidTarget target) {
+ return true;
+ }
+
+ /**
+ * Checks that the provided sdk contains one or more valid targets.
+ * @param sdk the {@link Sdk} to validate.
+ */
+ private void validateSdk(Sdk sdk) {
+ assertTrue("sdk has no targets", sdk.getTargets().length > 0);
+ for (IAndroidTarget target : sdk.getTargets()) {
+ if (!validateSdk(target)) {
+ continue;
+ }
+ if (false) { // This takes forEVER
+ IStatus status = new AndroidTargetParser(target).run(new NullProgressMonitor());
+ if (status.getCode() != IStatus.OK) {
+ fail("Failed to parse targets data");
+ }
+ }
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/UnitTests.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/UnitTests.java
new file mode 100644
index 000000000..15d38715c
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/UnitTests.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.android.ide.eclipse.tests;
+
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Container TestSuite for all eclipse unit tests to be run
+ *
+ * Uses Eclipse OSGI to find and then run all junit.junit.framework.Tests in
+ * this plugin, excluding tests in the FuncTests.FUNC_TEST_PACKAGE package
+ *
+ * Since it uses Eclipse OSGI, it must be run in a Eclipse plugin environment
+ * i.e. from Eclipse workbench, this suite must be run using the
+ * "JUnit Plug-in Test" launch configuration as opposed to as a "JUnit Test"
+ *
+ */
+public class UnitTests {
+ private static final String TEST_PACKAGE = "com.android";
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+
+ UnitTestCollector collector = new UnitTestCollector();
+ // since this plugin is a fragment which runs insde adt, gather tests from AdtPlugin
+ collector.addTestCases(suite, AdtPlugin.getDefault(), TEST_PACKAGE);
+
+ return suite;
+ }
+
+ /**
+ * Specialized test collector which will skip adding functional tests
+ */
+ private static class UnitTestCollector extends EclipseTestCollector {
+ /**
+ * Override parent class to exclude functional tests
+ */
+ @Override
+ protected boolean isTestClass(Class<?> testClass) {
+ return super.isTestClass(testClass) &&
+ !testClass.getPackage().getName().startsWith(FuncTests.FUNC_TEST_PACKAGE);
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java
new file mode 100644
index 000000000..d6b401b62
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.android.ide.eclipse.tests.functests.layoutRendering;
+
+import com.android.SdkConstants;
+import com.android.ide.common.rendering.LayoutLibrary;
+import com.android.ide.common.rendering.api.ActionBarCallback;
+import com.android.ide.common.rendering.api.AdapterBinding;
+import com.android.ide.common.rendering.api.HardwareConfig;
+import com.android.ide.common.rendering.api.ILayoutPullParser;
+import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.ide.common.rendering.api.RenderSession;
+import com.android.ide.common.rendering.api.ResourceReference;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
+import com.android.ide.common.resources.ResourceItem;
+import com.android.ide.common.resources.ResourceRepository;
+import com.android.ide.common.resources.ResourceResolver;
+import com.android.ide.common.resources.configuration.DensityQualifier;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.KeyboardStateQualifier;
+import com.android.ide.common.resources.configuration.NavigationMethodQualifier;
+import com.android.ide.common.resources.configuration.NavigationStateQualifier;
+import com.android.ide.common.resources.configuration.ScreenDimensionQualifier;
+import com.android.ide.common.resources.configuration.ScreenHeightQualifier;
+import com.android.ide.common.resources.configuration.ScreenOrientationQualifier;
+import com.android.ide.common.resources.configuration.ScreenRatioQualifier;
+import com.android.ide.common.resources.configuration.ScreenSizeQualifier;
+import com.android.ide.common.resources.configuration.ScreenWidthQualifier;
+import com.android.ide.common.resources.configuration.SmallestScreenWidthQualifier;
+import com.android.ide.common.resources.configuration.TextInputMethodQualifier;
+import com.android.ide.common.resources.configuration.TouchScreenQualifier;
+import com.android.ide.common.sdk.LoadStatus;
+import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+import com.android.ide.eclipse.tests.SdkLoadingTestCase;
+import com.android.io.FolderWrapper;
+import com.android.resources.Density;
+import com.android.resources.Keyboard;
+import com.android.resources.KeyboardState;
+import com.android.resources.Navigation;
+import com.android.resources.NavigationState;
+import com.android.resources.ResourceType;
+import com.android.resources.ScreenOrientation;
+import com.android.resources.ScreenRatio;
+import com.android.resources.ScreenSize;
+import com.android.resources.TouchScreen;
+import com.android.sdklib.IAndroidTarget;
+import com.android.util.Pair;
+
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.imageio.ImageIO;
+
+public class ApiDemosRenderingTest extends SdkLoadingTestCase {
+
+ /**
+ * Custom parser that implements {@link ILayoutPullParser} (which itself extends
+ * {@link XmlPullParser}).
+ */
+ private final static class TestParser extends KXmlParser implements ILayoutPullParser {
+ /**
+ * Since we're not going to go through the result of the rendering/layout, we can return
+ * null for the View Key.
+ */
+ @Override
+ public Object getViewCookie() {
+ return null;
+ }
+
+ @Override
+ public ILayoutPullParser getParser(String layoutName) {
+ return null;
+ }
+ }
+
+ private final static class ProjectCallBack implements IProjectCallback {
+ // resource id counter.
+ // We start at 0x7f000000 to avoid colliding with the framework id
+ // since we have no access to the project R.java and we need to generate them automatically.
+ private int mIdCounter = 0x7f000000;
+
+ // in some cases, the id that getResourceValue(String type, String name) returns
+ // will be sent back to get the type/name. This map stores the id/type/name we generate
+ // to be able to do the reverse resolution.
+ private Map<Integer, Pair<ResourceType, String>> mResourceMap =
+ new HashMap<Integer, Pair<ResourceType, String>>();
+
+ private boolean mCustomViewAttempt = false;
+
+ @Override
+ public String getNamespace() {
+ // TODO: read from the ApiDemos manifest.
+ return "com.example.android.apis";
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
+ throws ClassNotFoundException, Exception {
+ mCustomViewAttempt = true;
+ return null;
+ }
+
+ @Override
+ public Integer getResourceId(ResourceType type, String name) {
+ Integer result = ++mIdCounter;
+ mResourceMap.put(result, Pair.of(type, name));
+ return result;
+ }
+
+ @Override
+ public Pair<ResourceType, String> resolveResourceId(int id) {
+ return mResourceMap.get(id);
+ }
+
+ @Override
+ public String resolveResourceId(int[] id) {
+ return null;
+ }
+
+ @Override
+ public ILayoutPullParser getParser(String layoutName) {
+ return null;
+ }
+
+ @Override
+ public Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie,
+ ResourceReference itemRef, int fullPosition, int typePosition,
+ int fullChildPosition, int typeChildPosition,
+ ResourceReference viewRef, ViewAttribute viewAttribute, Object defaultValue) {
+ return null;
+ }
+
+ @Override
+ public AdapterBinding getAdapterBinding(ResourceReference adapterView,
+ Object adapterCookie, Object viewObject) {
+ return null;
+ }
+
+ @Override
+ public ILayoutPullParser getParser(ResourceValue layoutResource) {
+ return null;
+ }
+
+ @Override
+ public ActionBarCallback getActionBarCallback() {
+ return new ActionBarCallback();
+ }
+ }
+
+ public void testApiDemos() throws IOException, XmlPullParserException {
+ findApiDemos();
+ }
+
+ private void findApiDemos() throws IOException, XmlPullParserException {
+ IAndroidTarget[] targets = getSdk().getTargets();
+
+ for (IAndroidTarget target : targets) {
+ String path = target.getPath(IAndroidTarget.SAMPLES);
+ File samples = new File(path);
+ if (samples.isDirectory()) {
+ File[] files = samples.listFiles();
+ for (File file : files) {
+ if ("apidemos".equalsIgnoreCase(file.getName())) {
+ testSample(target, file);
+ return;
+ }
+ }
+ }
+ }
+
+ fail("Failed to find ApiDemos!");
+ }
+
+ private void testSample(IAndroidTarget target, File sampleProject) throws IOException, XmlPullParserException {
+ AndroidTargetData data = getSdk().getTargetData(target);
+ if (data == null) {
+ fail("No AndroidData!");
+ }
+
+ LayoutLibrary layoutLib = data.getLayoutLibrary();
+ if (layoutLib.getStatus() != LoadStatus.LOADED) {
+ fail("Fail to load the bridge: " + layoutLib.getLoadMessage());
+ }
+
+ FolderWrapper resFolder = new FolderWrapper(sampleProject, SdkConstants.FD_RES);
+ if (resFolder.exists() == false) {
+ fail("Sample project has no res folder!");
+ }
+
+ // look for the layout folder
+ File layoutFolder = new File(resFolder, SdkConstants.FD_RES_LAYOUT);
+ if (layoutFolder.isDirectory() == false) {
+ fail("Sample project has no layout folder!");
+ }
+
+ // first load the project's target framework resource
+ ResourceRepository framework = ResourceManager.getInstance().loadFrameworkResources(target);
+
+ // now load the project resources
+ ResourceRepository project = new ResourceRepository(resFolder, false) {
+ @Override
+ protected ResourceItem createResourceItem(String name) {
+ return new ResourceItem(name);
+ }
+
+ };
+
+ // Create a folder configuration that will be used for the rendering:
+ FolderConfiguration config = getConfiguration();
+
+ // get the configured resources
+ Map<ResourceType, Map<String, ResourceValue>> configuredFramework =
+ framework.getConfiguredResources(config);
+ Map<ResourceType, Map<String, ResourceValue>> configuredProject =
+ project.getConfiguredResources(config);
+
+ boolean saveFiles = System.getenv("save_file") != null;
+
+ // loop on the layouts and render them
+ File[] layouts = layoutFolder.listFiles();
+ for (File layout : layouts) {
+ // create a parser for the layout file
+ TestParser parser = new TestParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ parser.setInput(new FileReader(layout));
+
+ System.out.println("Rendering " + layout.getName());
+
+ ProjectCallBack projectCallBack = new ProjectCallBack();
+
+ ResourceResolver resolver = ResourceResolver.create(
+ configuredProject, configuredFramework,
+ "Theme", false /*isProjectTheme*/);
+
+ HardwareConfig hardwareConfig = new HardwareConfig(
+ 320,
+ 480,
+ Density.MEDIUM,
+ 160, //xdpi
+ 160, // ydpi
+ ScreenSize.NORMAL,
+ ScreenOrientation.PORTRAIT,
+ false /*software buttons */);
+
+ RenderSession session = layoutLib.createSession(new SessionParams(
+ parser,
+ RenderingMode.NORMAL,
+ null /*projectKey*/,
+ hardwareConfig,
+ resolver,
+ projectCallBack,
+ 1, // minSdkVersion
+ 1, // targetSdkVersion
+ null //logger
+ ));
+
+ if (session.getResult().isSuccess() == false) {
+ if (projectCallBack.mCustomViewAttempt == false) {
+ System.out.println("FAILED");
+ fail(String.format("Rendering %1$s: %2$s", layout.getName(),
+ session.getResult().getErrorMessage()));
+ } else {
+ System.out.println("Ignore custom views for now");
+ }
+ } else {
+ if (saveFiles) {
+ File tmp = File.createTempFile(layout.getName(), ".png");
+ ImageIO.write(session.getImage(), "png", tmp);
+ }
+ System.out.println("Success!");
+ }
+ }
+ }
+
+ /**
+ * Returns a config. This must be a valid config like a device would return. This is to
+ * prevent issues where some resources don't exist in all cases and not in the default
+ * (for instance only available in hdpi and mdpi but not in default).
+ * @return
+ */
+ private FolderConfiguration getConfiguration() {
+ FolderConfiguration config = new FolderConfiguration();
+
+ // this matches an ADP1.
+ config.addQualifier(new SmallestScreenWidthQualifier(320));
+ config.addQualifier(new ScreenWidthQualifier(320));
+ config.addQualifier(new ScreenHeightQualifier(480));
+ config.addQualifier(new ScreenSizeQualifier(ScreenSize.NORMAL));
+ config.addQualifier(new ScreenRatioQualifier(ScreenRatio.NOTLONG));
+ config.addQualifier(new ScreenOrientationQualifier(ScreenOrientation.PORTRAIT));
+ config.addQualifier(new DensityQualifier(Density.MEDIUM));
+ config.addQualifier(new TouchScreenQualifier(TouchScreen.FINGER));
+ config.addQualifier(new KeyboardStateQualifier(KeyboardState.HIDDEN));
+ config.addQualifier(new TextInputMethodQualifier(Keyboard.QWERTY));
+ config.addQualifier(new NavigationStateQualifier(NavigationState.HIDDEN));
+ config.addQualifier(new NavigationMethodQualifier(Navigation.TRACKBALL));
+ config.addQualifier(new ScreenDimensionQualifier(480, 320));
+
+ config.updateScreenWidthAndHeight();
+
+ return config;
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/sampleProjects/AndroidManifestWriter.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/sampleProjects/AndroidManifestWriter.java
new file mode 100644
index 000000000..141e7e000
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/sampleProjects/AndroidManifestWriter.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.android.ide.eclipse.tests.functests.sampleProjects;
+
+import com.android.SdkConstants;
+import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
+import com.android.xml.AndroidManifest;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+/**
+ * Helper class for modifying an AndroidManifest.
+ * <p/>
+ * TODO: consider merging this with AndroidManifestParser.
+ */
+class AndroidManifestWriter {
+
+ private static final Logger sLogger = Logger.getLogger(AndroidManifestWriter.class.getName());
+
+ private final Document mDoc;
+ private final String mOsManifestFilePath;
+
+ private AndroidManifestWriter(Document doc, String osManifestFilePath) {
+ mDoc = doc;
+ mOsManifestFilePath = osManifestFilePath;
+ }
+
+ /**
+ * Sets the minimum SDK version for this manifest
+ * @param minSdkVersion - the minimim sdk version to use
+ * @returns <code>true</code> on success, false otherwise
+ */
+ public boolean setMinSdkVersion(String minSdkVersion) {
+ Element usesSdkElement = null;
+ NodeList nodeList = mDoc.getElementsByTagName(AndroidManifest.NODE_USES_SDK);
+ if (nodeList.getLength() > 0) {
+ usesSdkElement = (Element) nodeList.item(0);
+ } else {
+ usesSdkElement = mDoc.createElement(AndroidManifest.NODE_USES_SDK);
+ mDoc.getDocumentElement().appendChild(usesSdkElement);
+ }
+ Attr minSdkAttr = mDoc.createAttributeNS(SdkConstants.NS_RESOURCES,
+ AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION);
+ String prefix = mDoc.lookupPrefix(SdkConstants.NS_RESOURCES);
+ minSdkAttr.setPrefix(prefix);
+ minSdkAttr.setValue(minSdkVersion);
+ usesSdkElement.setAttributeNodeNS(minSdkAttr);
+ return saveXmlToFile();
+ }
+
+ private boolean saveXmlToFile() {
+ try {
+ // Prepare the DOM document for writing
+ Source source = new DOMSource(mDoc);
+
+ // Prepare the output file
+ File file = new File(mOsManifestFilePath);
+ Result result = new StreamResult(file);
+
+ // Write the DOM document to the file
+ Transformer xformer = TransformerFactory.newInstance().newTransformer();
+ xformer.transform(source, result);
+ } catch (TransformerConfigurationException e) {
+ sLogger.log(Level.SEVERE, "Failed to write xml file", e);
+ return false;
+ } catch (TransformerException e) {
+ sLogger.log(Level.SEVERE, "Failed to write xml file", e);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Parses the manifest file, and collects data.
+ *
+ * @param osManifestFilePath The OS path of the manifest file to parse.
+ * @return an {@link AndroidManifestHelper} or null if parsing failed
+ */
+ public static AndroidManifestWriter parse(String osManifestFilePath) {
+ try {
+ DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+ docFactory.setNamespaceAware(true);
+ DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
+ Document doc = docBuilder.parse(osManifestFilePath);
+ return new AndroidManifestWriter(doc, osManifestFilePath);
+ } catch (ParserConfigurationException e) {
+ sLogger.log(Level.SEVERE, "Error parsing file", e);
+ return null;
+ } catch (SAXException e) {
+ sLogger.log(Level.SEVERE, "Error parsing file", e);
+ return null;
+ } catch (IOException e) {
+ sLogger.log(Level.SEVERE, "Error parsing file", e);
+ return null;
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/sampleProjects/SampleProjectTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/sampleProjects/SampleProjectTest.java
new file mode 100644
index 000000000..3fb705dfb
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/sampleProjects/SampleProjectTest.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.android.ide.eclipse.tests.functests.sampleProjects;
+
+import com.android.SdkConstants;
+import com.android.ide.eclipse.adt.AdtUtils;
+import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectCreator;
+import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectWizardState;
+import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectWizardState.Mode;
+import com.android.ide.eclipse.tests.SdkLoadingTestCase;
+import com.android.sdklib.IAndroidTarget;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.operation.IRunnableContext;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.swt.widgets.Display;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Test case that verifies all SDK sample projects can be imported, and built in
+ * Eclipse.
+ * <p/>
+ * TODO: add support for deploying apps onto emulator and verifying successful
+ * execution there
+ *
+ */
+public class SampleProjectTest extends SdkLoadingTestCase {
+
+ private static final Logger sLogger = Logger.getLogger(SampleProjectTest.class.getName());
+
+ /**
+ * Finds all samples projects in set SDK and verify they can be built in Eclipse.
+ * <p/>
+ * TODO: add install and run on emulator test
+ * @throws CoreException
+ */
+ public void testSamples() throws CoreException {
+ // TODO: For reporting purposes, it would be better if a separate test success or failure
+ // could be reported for each sample
+ IAndroidTarget[] targets = getSdk().getTargets();
+ for (IAndroidTarget target : targets) {
+ doTestSamplesForTarget(target);
+ }
+ }
+
+ private void doTestSamplesForTarget(IAndroidTarget target) throws CoreException {
+ String path = target.getPath(IAndroidTarget.SAMPLES);
+ File samples = new File(path);
+ if (samples.isDirectory()) {
+ File[] files = samples.listFiles();
+ for (File file : files) {
+ if (file.isDirectory()) {
+ doTestSampleProject(file.getName(), file.getAbsolutePath(), target);
+ }
+ }
+ }
+ }
+
+ /**
+ * Tests the sample project with the given name
+ *
+ * @param target - SDK target of project
+ * @param name - name of sample project to test
+ * @param path - absolute file system path
+ * @throws CoreException
+ */
+ private void doTestSampleProject(String name, String path, IAndroidTarget target)
+ throws CoreException {
+ IProject iproject = null;
+ try {
+ sLogger.log(Level.INFO, String.format("Testing sample %s for target %s", name,
+ target.getName()));
+
+ prepareProject(path, target);
+
+ IRunnableContext context = new IRunnableContext() {
+ @Override
+ public void run(boolean fork, boolean cancelable, IRunnableWithProgress runnable)
+ throws InvocationTargetException, InterruptedException {
+ runnable.run(new NullProgressMonitor());
+ }
+ };
+ NewProjectWizardState state = new NewProjectWizardState(Mode.SAMPLE);
+ state.projectName = name;
+ state.target = target;
+ state.packageName = "com.android.samples";
+ state.activityName = name;
+ state.applicationName = name;
+ state.chosenSample = new File(path);
+ state.useDefaultLocation = false;
+ state.createActivity = false;
+
+ NewProjectCreator creator = new NewProjectCreator(state, context);
+ creator.createAndroidProjects();
+ iproject = validateProjectExists(name);
+ validateNoProblems(iproject);
+ }
+ catch (CoreException e) {
+ sLogger.log(Level.SEVERE,
+ String.format("Unexpected exception when creating sample project %s " +
+ "for target %s", name, target.getName()));
+ throw e;
+ } finally {
+ if (iproject != null) {
+ iproject.delete(false, true, new NullProgressMonitor());
+ }
+ }
+ }
+
+ private void prepareProject(String path, IAndroidTarget target) {
+ if (target.getVersion().isPreview()) {
+ // need to explicitly set preview's version in manifest for project to compile
+ final String manifestPath = path + File.separatorChar +
+ SdkConstants.FN_ANDROID_MANIFEST_XML;
+ AndroidManifestWriter manifestWriter =
+ AndroidManifestWriter.parse(manifestPath);
+ assertNotNull(String.format("could not read manifest %s", manifestPath),
+ manifestWriter);
+ assertTrue(manifestWriter.setMinSdkVersion(target.getVersion().getApiString()));
+ }
+ }
+
+ private IProject validateProjectExists(String name) {
+ IProject iproject = getIProject(name);
+ assertTrue(String.format("%s project not created", name), iproject.exists());
+ assertTrue(String.format("%s project not opened", name), iproject.isOpen());
+ return iproject;
+ }
+
+ private IProject getIProject(String name) {
+ IProject iproject = ResourcesPlugin.getWorkspace().getRoot()
+ .getProject(name);
+ return iproject;
+ }
+
+ private void validateNoProblems(IProject iproject) throws CoreException {
+ waitForBuild(iproject);
+
+ boolean hasErrors = false;
+ StringBuilder failureBuilder = new StringBuilder(String.format("%s project has errors:",
+ iproject.getName()));
+ IMarker[] markers = iproject.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE);
+ if (markers != null && markers.length > 0) {
+ // the project has marker(s). even though they are "problem" we
+ // don't know their severity. so we loop on them and figure if they
+ // are warnings or errors
+ for (IMarker m : markers) {
+ int s = m.getAttribute(IMarker.SEVERITY, -1);
+ if (s == IMarker.SEVERITY_ERROR) {
+ hasErrors = true;
+ failureBuilder.append("\n");
+ failureBuilder.append(m.getAttribute(IMarker.MESSAGE, ""));
+ }
+ }
+ }
+ failureBuilder.append("Project location: " + AdtUtils.getAbsolutePath(iproject));
+ assertFalse(failureBuilder.toString(), hasErrors);
+ }
+
+ /**
+ * Waits for build to complete.
+ *
+ * @param iproject
+ */
+ private void waitForBuild(final IProject iproject) {
+
+ final BuiltProjectDeltaVisitor deltaVisitor = new BuiltProjectDeltaVisitor(iproject);
+ IResourceChangeListener newBuildListener = new IResourceChangeListener() {
+
+ @Override
+ public void resourceChanged(IResourceChangeEvent event) {
+ try {
+ event.getDelta().accept(deltaVisitor);
+ }
+ catch (CoreException e) {
+ fail();
+ }
+ }
+
+ };
+ iproject.getWorkspace().addResourceChangeListener(newBuildListener,
+ IResourceChangeEvent.POST_BUILD);
+
+ // poll build listener to determine when build is done
+ // loop max of 1200 times * 50 ms = 60 seconds
+ final int maxWait = 1200;
+ for (int i=0; i < maxWait; i++) {
+ if (deltaVisitor.isProjectBuilt()) {
+ return;
+ }
+ try {
+ Thread.sleep(50);
+ }
+ catch (InterruptedException e) {
+ // ignore
+ }
+ if (Display.getCurrent() != null) {
+ Display.getCurrent().readAndDispatch();
+ }
+ }
+
+ sLogger.log(Level.SEVERE, "expected build event never happened?");
+ fail(String.format("Expected build event never happened for %s", iproject.getName()));
+ }
+
+ /**
+ * Scans a given IResourceDelta looking for a "build event" change for given IProject
+ *
+ */
+ private class BuiltProjectDeltaVisitor implements IResourceDeltaVisitor {
+
+ private IProject mIProject;
+ private boolean mIsBuilt;
+
+ public BuiltProjectDeltaVisitor(IProject iproject) {
+ mIProject = iproject;
+ mIsBuilt = false;
+ }
+
+ @Override
+ public boolean visit(IResourceDelta delta) {
+ if (mIProject.equals(delta.getResource())) {
+ setBuilt(true);
+ return false;
+ }
+ return true;
+ }
+
+ private synchronized void setBuilt(boolean b) {
+ mIsBuilt = b;
+ }
+
+ public synchronized boolean isProjectBuilt() {
+ return mIsBuilt;
+ }
+ }
+}