Fix tests when running from the command line

When loading classes from the jar file, we can't just use the
URLClassLoader since it can not enumerate files in a jar directory.
Restoring the ModuleClassLoader and making all paths relative to the
system class loader (as opposed to relative to the class location).

Change-Id: Ib3f5d12dd5c964d0ba9cc6c5ec9cb556c989e653
(cherry picked from commit 2a4a6c81f8a103be5c48d8a0605a3e4416e8f7f1)
diff --git a/bridge/tests/Android.mk b/bridge/tests/Android.mk
index 8a81d0b..565feb6 100644
--- a/bridge/tests/Android.mk
+++ b/bridge/tests/Android.mk
@@ -17,7 +17,9 @@
 include $(CLEAR_VARS)
 
 # Only compile source java files in this lib.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := \
+	$(call all-java-files-under, src) \
+	$(call all-java-files-under, res/testApp/MyApplication/src/main/myapplication.widgets)
 LOCAL_JAVA_RESOURCE_DIRS := res
 
 LOCAL_MODULE := layoutlib-tests
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/ImageUtils.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/ImageUtils.java
index 8f9fa8a..d81b4ba 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/ImageUtils.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/ImageUtils.java
@@ -64,7 +64,7 @@
         double scale = THUMBNAIL_SIZE / (double)maxDimension;
         BufferedImage thumbnail = scale(image, scale, scale);
 
-        InputStream is = ImageUtils.class.getResourceAsStream(relativePath);
+        InputStream is = ImageUtils.class.getClassLoader().getResourceAsStream(relativePath);
         if (is == null) {
             String message = "Unable to load golden thumbnail: " + relativePath + "\n";
             message = saveImageAndAppendMessage(thumbnail, message, relativePath);
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index 5423e87..24cbbca 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -121,7 +121,7 @@
     private static final String PLATFORM_DIR;
     private static final String TEST_RES_DIR;
     /** Location of the app to test inside {@link #TEST_RES_DIR}*/
-    private static final String APP_TEST_DIR = "/testApp/MyApplication";
+    private static final String APP_TEST_DIR = "testApp/MyApplication";
     /** Location of the app's res dir inside {@link #TEST_RES_DIR}*/
     private static final String APP_TEST_RES = APP_TEST_DIR + "/src/main/res";
     private static final String APP_CLASSES_LOCATION =
@@ -138,8 +138,7 @@
 
     // Default class loader with access to the app classes
     private ClassLoader mDefaultClassLoader =
-            new URLClassLoader(new URL[]{this.getClass().getResource(APP_CLASSES_LOCATION)},
-                    getClass().getClassLoader());
+            new ModuleClassLoader(APP_CLASSES_LOCATION, getClass().getClassLoader());
 
     @Rule
     public TestWatcher sRenderMessageWatcher = new TestWatcher() {
@@ -313,7 +312,8 @@
         sFrameworkRepo.loadPublicResources(getLogger());
 
         sProjectResources =
-                new ResourceRepository(new FolderWrapper(TEST_RES_DIR + APP_TEST_RES), false) {
+                new ResourceRepository(new FolderWrapper(TEST_RES_DIR + "/" + APP_TEST_RES),
+                        false) {
             @NonNull
             @Override
             protected ResourceItem createResourceItem(@NonNull String name) {
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/ModuleClassLoader.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/ModuleClassLoader.java
new file mode 100644
index 0000000..3fac778
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/ModuleClassLoader.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * 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.android.layoutlib.bridge.intensive;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import libcore.io.Streams;
+
+/**
+ * Module class loader that loads classes from the test project.
+ */
+public class ModuleClassLoader extends ClassLoader {
+    private final Map<String, Class<?>> mClasses = new HashMap<>();
+    private String myModuleRoot;
+
+    /**
+     * @param moduleRoot The path to the module root
+     * @param parent The parent class loader
+     */
+    public ModuleClassLoader(String moduleRoot, ClassLoader parent) {
+        super(parent);
+        myModuleRoot = moduleRoot + (moduleRoot.endsWith("/") ? "" : "/");
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+        try {
+            return super.findClass(name);
+        } catch (ClassNotFoundException ignored) {
+        }
+
+        Class<?> clazz = mClasses.get(name);
+        if (clazz == null) {
+            String path = name.replace('.', '/').concat(".class");
+            try {
+                byte[] b = Streams.readFully(getResourceAsStream(myModuleRoot + path));
+                clazz = defineClass(name, b, 0, b.length);
+                mClasses.put(name, clazz);
+            } catch (IOException ignore) {
+                throw new ClassNotFoundException(name + " not found");
+            }
+        }
+
+        return clazz;
+    }
+}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java
index 1110494..bc8083f 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java
@@ -42,9 +42,11 @@
      * @param layoutPath Must start with '/' and be relative to test resources.
      */
     public LayoutPullParser(String layoutPath) {
-        assert layoutPath.startsWith("/");
+        if (layoutPath.startsWith("/")) {
+            layoutPath = layoutPath.substring(1);
+        }
         try {
-            init(getClass().getResourceAsStream(layoutPath));
+            init(getClass().getClassLoader().getResourceAsStream(layoutPath));
         } catch (XmlPullParserException e) {
             throw new IOError(e);
         }