From 640cd329f9743c8ff7c23388c78db10f5bda22d2 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Fri, 7 Jan 2022 09:26:54 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1160164 - Run "all the tests" when invoking xpcshell's mach command with no arguments instead of passing "all" to the test resolver.;r=ahal DONTBUILD (5583e3bce) - Bug 1171602 - Run mochitest using mach from a tests.zip, r=chmanchester (083d01f4c) - Bug 1178850 - Generate naive method bindings in annotation processor; r=snorp (8f8a14e3d) - Bug 1192079 - Support inner classes in generated JNI wrapper; r=snorp (1eefd6a4f) --- .../AnnotationProcessor.java | 138 ++++++++++-------- build/annotationProcessors/CodeGenerator.java | 106 ++++++++++++-- .../classloader/AnnotatableEntity.java | 11 +- .../classloader/JarClassIterator.java | 11 +- .../utils/GeneratableElementIterator.java | 58 +++++++- build/annotationProcessors/utils/Utils.java | 4 + .../mochitest/mach_test_package_commands.py | 41 ++++++ testing/mochitest/mochitest_options.py | 9 +- testing/mochitest/moz.build | 1 + testing/tools/mach_test_package_bootstrap.py | 21 +++ testing/xpcshell/mach_commands.py | 2 +- 11 files changed, 321 insertions(+), 81 deletions(-) create mode 100644 testing/mochitest/mach_test_package_commands.py diff --git a/build/annotationProcessors/AnnotationProcessor.java b/build/annotationProcessors/AnnotationProcessor.java index 1408891a1d..ff6fb9b449 100644 --- a/build/annotationProcessors/AnnotationProcessor.java +++ b/build/annotationProcessors/AnnotationProcessor.java @@ -15,17 +15,22 @@ import java.util.Arrays; import java.util.Iterator; public class AnnotationProcessor { - public static final String OUTFILE = "GeneratedJNIWrappers.cpp"; - public static final String HEADERFILE = "GeneratedJNIWrappers.h"; + public static final String SOURCE_FILE = "GeneratedJNIWrappers.cpp"; + public static final String HEADER_FILE = "GeneratedJNIWrappers.h"; + public static final String NATIVES_FILE = "GeneratedJNINatives.h"; public static final String GENERATED_COMMENT = "// GENERATED CODE\n" + "// Generated by the Java program at /build/annotationProcessors at compile time\n" + "// from annotations on Java methods. To update, change the annotations on the\n" + - "// corresponding Javamethods and rerun the build. Manually updating this file\n" + + "// corresponding Java methods and rerun the build. Manually updating this file\n" + "// will cause your build to fail.\n" + "\n"; + private static final StringBuilder headerFile = new StringBuilder(GENERATED_COMMENT); + private static final StringBuilder implementationFile = new StringBuilder(GENERATED_COMMENT); + private static final StringBuilder nativesFile = new StringBuilder(GENERATED_COMMENT); + public static void main(String[] args) { // We expect a list of jars on the commandline. If missing, whinge about it. if (args.length <= 1) { @@ -45,10 +50,9 @@ public class AnnotationProcessor { // Get an iterator over the classes in the jar files given... Iterator jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(args); - StringBuilder headerFile = new StringBuilder(GENERATED_COMMENT); headerFile.append( - "#ifndef GeneratedJNIWrappers_h__\n" + - "#define GeneratedJNIWrappers_h__\n" + + "#ifndef " + getHeaderGuardName(HEADER_FILE) + "\n" + + "#define " + getHeaderGuardName(HEADER_FILE) + "\n" + "\n" + "#include \"mozilla/jni/Refs.h\"\n" + "\n" + @@ -56,7 +60,6 @@ public class AnnotationProcessor { "namespace widget {\n" + "\n"); - StringBuilder implementationFile = new StringBuilder(GENERATED_COMMENT); implementationFile.append( "#include \"GeneratedJNIWrappers.h\"\n" + "#include \"mozilla/jni/Accessors.h\"\n" + @@ -65,37 +68,19 @@ public class AnnotationProcessor { "namespace widget {\n" + "\n"); + nativesFile.append( + "#ifndef " + getHeaderGuardName(NATIVES_FILE) + "\n" + + "#define " + getHeaderGuardName(NATIVES_FILE) + "\n" + + "\n" + + "#include \"GeneratedJNIWrappers.h\"\n" + + "#include \"mozilla/jni/Natives.h\"\n" + + "\n" + + "namespace mozilla {\n" + + "namespace widget {\n" + + "\n"); + while (jarClassIterator.hasNext()) { - ClassWithOptions aClassTuple = jarClassIterator.next(); - - CodeGenerator generatorInstance; - - // Get an iterator over the appropriately generated methods of this class - Iterator methodIterator = new GeneratableElementIterator(aClassTuple.wrappedClass); - - if (!methodIterator.hasNext()) { - continue; - } - generatorInstance = new CodeGenerator(aClassTuple); - - // Iterate all annotated members in this class.. - while (methodIterator.hasNext()) { - AnnotatableEntity aElementTuple = methodIterator.next(); - switch (aElementTuple.mEntityType) { - case METHOD: - generatorInstance.generateMethod(aElementTuple); - break; - case FIELD: - generatorInstance.generateField(aElementTuple); - break; - case CONSTRUCTOR: - generatorInstance.generateConstructor(aElementTuple); - break; - } - } - - headerFile.append(generatorInstance.getHeaderFileContents()); - implementationFile.append(generatorInstance.getWrapperFileContents()); + generateClass(jarClassIterator.next()); } implementationFile.append( @@ -107,38 +92,75 @@ public class AnnotationProcessor { "\n" + "} /* widget */\n" + "} /* mozilla */\n" + - "#endif // GeneratedJNIWrappers_h__\n"); + "#endif // " + getHeaderGuardName(HEADER_FILE) + "\n"); + + nativesFile.append( + "\n" + + "} /* widget */\n" + + "} /* mozilla */\n" + + "#endif // " + getHeaderGuardName(NATIVES_FILE) + "\n"); + + writeOutputFile(SOURCE_FILE, implementationFile); + writeOutputFile(HEADER_FILE, headerFile); + writeOutputFile(NATIVES_FILE, nativesFile); - writeOutputFiles(headerFile, implementationFile); long e = System.currentTimeMillis(); System.out.println("Annotation processing complete in " + (e - s) + "ms"); } - private static void writeOutputFiles(StringBuilder aHeaderFile, StringBuilder aImplementationFile) { - FileOutputStream headerStream = null; - try { - headerStream = new FileOutputStream(OUTFILE); - headerStream.write(aImplementationFile.toString().getBytes()); - } catch (IOException e) { - System.err.println("Unable to write " + OUTFILE + ". Perhaps a permissions issue?"); - e.printStackTrace(System.err); - } finally { - if (headerStream != null) { - try { - headerStream.close(); - } catch (IOException e) { - System.err.println("Unable to close headerStream due to "+e); - e.printStackTrace(System.err); - } + private static void generateClass(final ClassWithOptions annotatedClass) { + // Get an iterator over the appropriately generated methods of this class + final GeneratableElementIterator methodIterator + = new GeneratableElementIterator(annotatedClass); + final ClassWithOptions[] innerClasses = methodIterator.getInnerClasses(); + + if (!methodIterator.hasNext() && innerClasses.length == 0) { + return; + } + + final CodeGenerator generatorInstance = new CodeGenerator(annotatedClass); + generatorInstance.generateClasses(innerClasses); + + // Iterate all annotated members in this class.. + while (methodIterator.hasNext()) { + AnnotatableEntity aElementTuple = methodIterator.next(); + switch (aElementTuple.mEntityType) { + case METHOD: + generatorInstance.generateMethod(aElementTuple); + break; + case NATIVE: + generatorInstance.generateNative(aElementTuple); + break; + case FIELD: + generatorInstance.generateField(aElementTuple); + break; + case CONSTRUCTOR: + generatorInstance.generateConstructor(aElementTuple); + break; } } + headerFile.append(generatorInstance.getHeaderFileContents()); + implementationFile.append(generatorInstance.getWrapperFileContents()); + nativesFile.append(generatorInstance.getNativesFileContents()); + + for (ClassWithOptions innerClass : innerClasses) { + generateClass(innerClass); + } + } + + private static String getHeaderGuardName(final String name) { + return name.replaceAll("\\W", "_"); + } + + private static void writeOutputFile(final String name, + final StringBuilder content) { FileOutputStream outStream = null; try { - outStream = new FileOutputStream(HEADERFILE); - outStream.write(aHeaderFile.toString().getBytes()); + outStream = new FileOutputStream(name); + outStream.write(content.toString().getBytes()); } catch (IOException e) { - System.err.println("Unable to write " + HEADERFILE + ". Perhaps a permissions issue?"); + System.err.println("Unable to write " + name + ". Perhaps a permissions issue?"); e.printStackTrace(System.err); } finally { if (outStream != null) { diff --git a/build/annotationProcessors/CodeGenerator.java b/build/annotationProcessors/CodeGenerator.java index 9b965f1bc3..d5f1adf462 100644 --- a/build/annotationProcessors/CodeGenerator.java +++ b/build/annotationProcessors/CodeGenerator.java @@ -22,6 +22,8 @@ public class CodeGenerator { // Buffers holding the strings to ultimately be written to the output files. private final StringBuilder cpp = new StringBuilder(); private final StringBuilder header = new StringBuilder(); + private final StringBuilder natives = new StringBuilder(); + private final StringBuilder nativesInits = new StringBuilder(); private final Class cls; private final String clsName; @@ -33,8 +35,8 @@ public class CodeGenerator { this.clsName = annotatedClass.generatedName; header.append( - "class " + clsName + " : public mozilla::jni::Class<" + clsName + "> {\n" + - "\n" + + "class " + clsName + " : public mozilla::jni::Class<" + clsName + ">\n" + + "{\n" + "public:\n" + " typedef mozilla::jni::Ref<" + clsName + "> Ref;\n" + " typedef mozilla::jni::LocalRef<" + clsName + "> LocalRef;\n" + @@ -51,6 +53,12 @@ public class CodeGenerator { cpp.append( "constexpr char " + clsName + "::name[];\n" + "\n"); + + natives.append( + "template\n" + + "class " + clsName + "::Natives : " + + "public mozilla::jni::NativeImpl<" + clsName + ", Impl>\n" + + "{\n"); } private String getTraitsName(String uniqueName, boolean includeScope) { @@ -72,20 +80,30 @@ public class CodeGenerator { } private void generateMember(AnnotationInfo info, Member member, - String uniqueName, Class type) { + String uniqueName, Class type, Class[] argTypes) { + final StringBuilder args = new StringBuilder(); + for (Class argType : argTypes) { + args.append("\n " + getNativeParameterType(argType, info) + ","); + } + if (args.length() > 0) { + args.setLength(args.length() - 1); + } + header.append( "public:\n" + " struct " + getTraitsName(uniqueName, /* includeScope */ false) + " {\n" + " typedef " + clsName + " Owner;\n" + " typedef " + getNativeReturnType(type, info) + " ReturnType;\n" + " typedef " + getNativeParameterType(type, info) + " SetterType;\n" + + " typedef mozilla::jni::Args<" + args + "> Args;\n" + " static constexpr char name[] = \"" + Utils.getMemberName(member) + "\";\n" + " static constexpr char signature[] =\n" + " \"" + Utils.getSignature(member) + "\";\n" + " static const bool isStatic = " + Utils.isStatic(member) + ";\n" + " static const bool isMultithreaded = " + info.isMultithreaded + ";\n" + - " static const mozilla::jni::ExceptionMode exceptionMode = " + ( + " static const mozilla::jni::ExceptionMode exceptionMode =\n" + + " " + ( info.catchException ? "mozilla::jni::ExceptionMode::NSRESULT" : info.noThrow ? "mozilla::jni::ExceptionMode::IGNORE" : "mozilla::jni::ExceptionMode::ABORT") + ";\n" + @@ -248,15 +266,15 @@ public class CodeGenerator { final Method method = annotatedMethod.getMethod(); final AnnotationInfo info = annotatedMethod.mAnnotationInfo; final String uniqueName = getUniqueMethodName(info.wrapperName); + final Class[] argTypes = method.getParameterTypes(); final Class returnType = method.getReturnType(); if (method.isSynthetic()) { return; } - generateMember(info, method, uniqueName, returnType); + generateMember(info, method, uniqueName, returnType, argTypes); - final Class[] argTypes = method.getParameterTypes(); final boolean isStatic = Utils.isStatic(method); header.append( @@ -272,6 +290,35 @@ public class CodeGenerator { "\n"); } + /** + * Append the appropriate generated code to the buffers for the native method provided. + * + * @param annotatedMethod The Java native method, plus annotation data. + */ + public void generateNative(AnnotatableEntity annotatedMethod) { + // Unpack the tuple and extract some useful fields from the Method.. + final Method method = annotatedMethod.getMethod(); + final AnnotationInfo info = annotatedMethod.mAnnotationInfo; + final String uniqueName = getUniqueMethodName(info.wrapperName); + final Class[] argTypes = method.getParameterTypes(); + final Class returnType = method.getReturnType(); + + generateMember(info, method, uniqueName, returnType, argTypes); + + final String traits = getTraitsName(uniqueName, /* includeScope */ true); + + if (nativesInits.length() > 0) { + nativesInits.append(','); + } + + nativesInits.append( + "\n" + + "\n" + + " mozilla::jni::MakeNativeMethod<" + traits + ">(\n" + + " mozilla::jni::NativeStub<" + traits + ", Impl>\n" + + " ::template Wrap<&Impl::" + info.wrapperName + ">)"); + } + private String getLiteral(Object val, AnnotationInfo info) { final Class type = val.getClass(); @@ -348,7 +395,7 @@ public class CodeGenerator { // Fall back to using accessors if we encounter an exception. } - generateMember(info, field, uniqueName, type); + generateMember(info, field, uniqueName, type, EMPTY_CLASS_ARRAY); final Class[] getterArgs = EMPTY_CLASS_ARRAY; @@ -389,15 +436,14 @@ public class CodeGenerator { final AnnotationInfo info = annotatedConstructor.mAnnotationInfo; final String wrapperName = "New"; final String uniqueName = getUniqueMethodName(wrapperName); + final Class[] argTypes = method.getParameterTypes(); final Class returnType = cls; if (method.isSynthetic()) { return; } - generateMember(info, method, uniqueName, returnType); - - final Class[] argTypes = method.getParameterTypes(); + generateMember(info, method, uniqueName, returnType, argTypes); header.append( " " + generateDeclaration(wrapperName, argTypes, @@ -439,6 +485,21 @@ public class CodeGenerator { } } + public void generateClasses(final ClassWithOptions[] classes) { + if (classes.length == 0) { + return; + } + + header.append( + "public:\n"); + for (final ClassWithOptions cls : classes) { + // Extract "Inner" from "Outer::Inner". + header.append( + " class " + Utils.getUnqualifiedName(cls.generatedName) + ";\n"); + } + header.append('\n'); + } + /** * Get the finalised bytes to go into the generated wrappers file. * @@ -454,9 +515,34 @@ public class CodeGenerator { * @return The bytes to be written to the header file. */ public String getHeaderFileContents() { + if (nativesInits.length() > 0) { + header.append( + "public:\n" + + " template class Natives;\n"); + } header.append( "};\n" + "\n"); return header.toString(); } + + /** + * Get the finalised bytes to go into the generated natives header file. + * + * @return The bytes to be written to the header file. + */ + public String getNativesFileContents() { + if (nativesInits.length() == 0) { + return ""; + } + natives.append( + "public:\n" + + " static constexpr JNINativeMethod methods[] = {" + nativesInits + '\n' + + " };\n" + + "};\n" + + "\n" + + "template\n" + + "constexpr JNINativeMethod " + clsName + "::Natives::methods[];\n"); + return natives.toString(); + } } diff --git a/build/annotationProcessors/classloader/AnnotatableEntity.java b/build/annotationProcessors/classloader/AnnotatableEntity.java index 9dffe6033b..b11a6c49a6 100644 --- a/build/annotationProcessors/classloader/AnnotatableEntity.java +++ b/build/annotationProcessors/classloader/AnnotatableEntity.java @@ -10,13 +10,14 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; /** * Union type to hold either a method, field, or ctor. Allows us to iterate "The generatable stuff", despite * the fact that such things can be of either flavour. */ public class AnnotatableEntity { - public enum ENTITY_TYPE {METHOD, FIELD, CONSTRUCTOR} + public enum ENTITY_TYPE {METHOD, NATIVE, FIELD, CONSTRUCTOR} private final Member mMember; public final ENTITY_TYPE mEntityType; @@ -28,7 +29,11 @@ public class AnnotatableEntity { mAnnotationInfo = aAnnotationInfo; if (aObject instanceof Method) { - mEntityType = ENTITY_TYPE.METHOD; + if (Modifier.isNative(aObject.getModifiers())) { + mEntityType = ENTITY_TYPE.NATIVE; + } else { + mEntityType = ENTITY_TYPE.METHOD; + } } else if (aObject instanceof Field) { mEntityType = ENTITY_TYPE.FIELD; } else { @@ -37,7 +42,7 @@ public class AnnotatableEntity { } public Method getMethod() { - if (mEntityType != ENTITY_TYPE.METHOD) { + if (mEntityType != ENTITY_TYPE.METHOD && mEntityType != ENTITY_TYPE.NATIVE) { throw new UnsupportedOperationException("Attempt to cast to unsupported member type."); } return (Method) mMember; diff --git a/build/annotationProcessors/classloader/JarClassIterator.java b/build/annotationProcessors/classloader/JarClassIterator.java index 003856a1c3..c5b0fd31c3 100644 --- a/build/annotationProcessors/classloader/JarClassIterator.java +++ b/build/annotationProcessors/classloader/JarClassIterator.java @@ -28,20 +28,21 @@ public class JarClassIterator implements Iterator { String className = mTargetClassListIterator.next(); try { Class ret = mTarget.loadClass(className); - final String canonicalName; // Incremental builds can leave stale classfiles in the jar. Such classfiles will cause // an exception at this point. We can safely ignore these classes - they cannot possibly - // ever be loaded as they conflict with their parent class and will be killed by Proguard - // later on anyway. + // ever be loaded as they conflict with their parent class and will be killed by + // Proguard later on anyway. + final Class enclosingClass; try { - canonicalName = ret.getCanonicalName(); + enclosingClass = ret.getEnclosingClass(); } catch (IncompatibleClassChangeError e) { return next(); } - if (canonicalName == null || "null".equals(canonicalName)) { + if (enclosingClass != null) { // Anonymous inner class - unsupported. + // Or named inner class, which will be processed when we process the outer class. return next(); } diff --git a/build/annotationProcessors/utils/GeneratableElementIterator.java b/build/annotationProcessors/utils/GeneratableElementIterator.java index fa1e89dbc4..f7ed22350c 100644 --- a/build/annotationProcessors/utils/GeneratableElementIterator.java +++ b/build/annotationProcessors/utils/GeneratableElementIterator.java @@ -6,6 +6,7 @@ package org.mozilla.gecko.annotationProcessors.utils; import org.mozilla.gecko.annotationProcessors.AnnotationInfo; import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity; +import org.mozilla.gecko.annotationProcessors.classloader.ClassWithOptions; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; @@ -13,6 +14,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.util.Arrays; +import java.util.Comparator; import java.util.Iterator; /** @@ -21,13 +23,17 @@ import java.util.Iterator; * parameters) and the argument. */ public class GeneratableElementIterator implements Iterator { + private final ClassWithOptions mClass; private final Member[] mObjects; private AnnotatableEntity mNextReturnValue; private int mElementIndex; private boolean mIterateEveryEntry; - public GeneratableElementIterator(Class aClass) { + public GeneratableElementIterator(ClassWithOptions annotatedClass) { + mClass = annotatedClass; + + final Class aClass = annotatedClass.wrappedClass; // Get all the elements of this class as AccessibleObjects. Member[] aMethods = aClass.getDeclaredMethods(); Member[] aFields = aClass.getDeclaredFields(); @@ -59,6 +65,56 @@ public class GeneratableElementIterator implements Iterator { findNextValue(); } + private Class[] getFilteredInnerClasses() { + // Go through all inner classes and see which ones we want to generate. + final Class[] candidates = mClass.wrappedClass.getDeclaredClasses(); + int count = 0; + + for (int i = 0; i < candidates.length; ++i) { + final GeneratableElementIterator testIterator + = new GeneratableElementIterator(new ClassWithOptions(candidates[i], null)); + if (testIterator.hasNext() + || testIterator.getFilteredInnerClasses() != null) { + count++; + continue; + } + // Clear out ones that don't match. + candidates[i] = null; + } + return count > 0 ? candidates : null; + } + + public ClassWithOptions[] getInnerClasses() { + final Class[] candidates = getFilteredInnerClasses(); + if (candidates == null) { + return new ClassWithOptions[0]; + } + + int count = 0; + for (Class candidate : candidates) { + if (candidate != null) { + count++; + } + } + + final ClassWithOptions[] ret = new ClassWithOptions[count]; + count = 0; + for (Class candidate : candidates) { + if (candidate != null) { + ret[count++] = new ClassWithOptions( + candidate, mClass.generatedName + "::" + candidate.getSimpleName()); + } + } + assert ret.length == count; + + Arrays.sort(ret, new Comparator() { + @Override public int compare(ClassWithOptions lhs, ClassWithOptions rhs) { + return lhs.generatedName.compareTo(rhs.generatedName); + } + }); + return ret; + } + /** * Find and cache the next appropriately annotated method, plus the annotation parameter, if * one exists. Otherwise cache null, so hasNext returns false. diff --git a/build/annotationProcessors/utils/Utils.java b/build/annotationProcessors/utils/Utils.java index 03e71291cd..faa8e1ec56 100644 --- a/build/annotationProcessors/utils/Utils.java +++ b/build/annotationProcessors/utils/Utils.java @@ -225,6 +225,10 @@ public class Utils { return member.getName(); } + public static String getUnqualifiedName(String name) { + return name.substring(name.lastIndexOf(':') + 1); + } + /** * Determine if a member is declared static. * diff --git a/testing/mochitest/mach_test_package_commands.py b/testing/mochitest/mach_test_package_commands.py new file mode 100644 index 0000000000..641a469bbf --- /dev/null +++ b/testing/mochitest/mach_test_package_commands.py @@ -0,0 +1,41 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from __future__ import unicode_literals + +from argparse import Namespace +import os + +from mach.decorators import ( + CommandProvider, + Command, +) + + +def run_mochitest(context, **kwargs): + args = Namespace(**kwargs) + args.certPath = context.certs_dir + args.utilityPath = context.bin_dir + args.extraProfileFiles.append(os.path.join(context.bin_dir, 'plugins')) + + from runtests import run_test_harness + return run_test_harness(args) + + +def setup_argument_parser(): + from mochitest_options import MochitestArgumentParser + return MochitestArgumentParser(app='generic') + + +@CommandProvider +class MochitestCommands(object): + + def __init__(self, context): + self.context = context + + @Command('mochitest', category='testing', + description='Run the mochitest harness.', + parser=setup_argument_parser) + def mochitest(self, **kwargs): + return run_mochitest(self.context, **kwargs) diff --git a/testing/mochitest/mochitest_options.py b/testing/mochitest/mochitest_options.py index c910fe8163..d56b47517f 100644 --- a/testing/mochitest/mochitest_options.py +++ b/testing/mochitest/mochitest_options.py @@ -70,7 +70,7 @@ class MochitestArguments(ArgumentContainer): "help": "Override the default binary used to run tests with the path provided, e.g " "/usr/bin/firefox. If you have run ./mach package beforehand, you can " "specify 'dist' to run tests against the distribution bundle's binary.", - "suppress": True, + "suppress": build_obj is not None, }], [["--utility-path"], {"dest": "utilityPath", @@ -582,7 +582,9 @@ class MochitestArguments(ArgumentContainer): "could not find xre directory, --xre-path must be specified") # allow relative paths - options.xrePath = self.get_full_path(options.xrePath, parser.oldcwd) + if options.xrePath: + options.xrePath = self.get_full_path(options.xrePath, parser.oldcwd) + if options.profilePath: options.profilePath = self.get_full_path(options.profilePath, parser.oldcwd) @@ -1140,7 +1142,8 @@ container_map = { class MochitestArgumentParser(ArgumentParser): """ - Usage instructions for runtests.py. + Usage instructions for Mochitest. + All arguments are optional. If --chrome is specified, chrome tests will be run instead of web content tests. If --browser-chrome is specified, browser-chrome tests will be run instead of web content tests. diff --git a/testing/mochitest/moz.build b/testing/mochitest/moz.build index 69d3746b91..446d720eff 100644 --- a/testing/mochitest/moz.build +++ b/testing/mochitest/moz.build @@ -62,6 +62,7 @@ TEST_HARNESS_FILES.testing.mochitest += [ 'jetpack-addon-overlay.xul', 'jetpack-package-harness.js', 'jetpack-package-overlay.xul', + 'mach_test_package_commands.py', 'manifest.webapp', 'manifestLibrary.js', 'mochitest_options.py', diff --git a/testing/tools/mach_test_package_bootstrap.py b/testing/tools/mach_test_package_bootstrap.py index b5aa0de400..3577439563 100644 --- a/testing/tools/mach_test_package_bootstrap.py +++ b/testing/tools/mach_test_package_bootstrap.py @@ -11,11 +11,28 @@ import time SEARCH_PATHS = [ + 'mochitest', + 'mozbase/mozcrash', + 'mozbase/mozdebug', + 'mozbase/mozdevice', + 'mozbase/mozfile', + 'mozbase/mozhttpd', + 'mozbase/mozlog', + 'mozbase/moznetwork', + 'mozbase/mozprocess', + 'mozbase/mozprofile', + 'mozbase/mozrunner', + 'mozbase/mozsystemmonitor', + 'mozbase/mozinfo', + 'mozbase/moztest', + 'mozbase/mozversion', + 'mozbase/manifestparser', 'tools/mach', ] # Individual files providing mach commands. MACH_MODULES = [ + 'mochitest/mach_test_package_commands.py', 'tools/mach/mach/commands/commandinfo.py', ] @@ -60,6 +77,10 @@ def bootstrap(test_package_root): import mach.main def populate_context(context, key=None): + context.package_root = test_package_root + context.certs_dir = os.path.join(test_package_root, 'certs') + context.bin_dir = os.path.join(test_package_root, 'bin') + context.modules_dir = os.path.join(test_package_root, 'modules') return context mach = mach.main.Mach(os.getcwd()) diff --git a/testing/xpcshell/mach_commands.py b/testing/xpcshell/mach_commands.py index 429c255e5b..e743c95d55 100644 --- a/testing/xpcshell/mach_commands.py +++ b/testing/xpcshell/mach_commands.py @@ -82,7 +82,7 @@ class XPCShellRunner(MozbuildObject): if not os.path.isfile(os.path.join(self.topsrcdir, 'build', 'automationutils.py')): sys.path.append(os.path.join(self.topsrcdir, 'mozilla', 'build')) - if test_paths == ['all']: + if test_paths == 'all': self.run_suite(interactive=interactive, keep_going=keep_going, shuffle=shuffle, sequential=sequential, debugger=debugger, debuggerArgs=debuggerArgs,