build: Add support for building testng within the AOSP (host only).

Also abstracts out some dependencies that are unsupported on android
to be behind interfaces. Ant and Bsh support is excluded.

Bug: 27552463
Change-Id: I71b4f3b26b9307b36444cecc75d67de03be9cb24
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..52f15b3
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,78 @@
+# 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.
+#
+
+#
+# Build support for testng within the Android Open Source Project
+# See https://source.android.com/source/building.html for more information
+#
+#
+# The following optional support has been disabled:
+# - ant
+# - bsh
+#
+# JUnit support is enabled, but needs to be explicitly added in with LOCAL_STATIC_JAVA_LIBRARIES
+# by whichever app/library is also including testng.
+
+LOCAL_PATH := $(call my-dir)
+
+##
+## Common variables, don't repeat yourself.
+##
+
+# Memorize path so we can use it later.
+testng_path := $(LOCAL_PATH)
+
+# These files don't build on Android, either due to missing java.* APIs or due to missing dependencies (see above).
+testng_android_unsupported_src_files := \
+  src/main/java/com/beust/testng/TestNGAntTask.java \
+  src/main/java/org/testng/TestNGAntTask.java \
+  src/main/java/org/testng/internal/Bsh.java \
+  src/main/java/org/testng/internal/PropertyUtils.java \
+  src/main/java/org/testng/internal/PathUtils.java
+
+# These files don't exist in the source tree, they need to be generated during the build.
+testng_src_files_need_gen := src/generated/java/org/testng/internal/Version.java
+
+# Android-specific replacements of some of the above files.
+testng_src_files_android_specific := $(call all-java-files-under,android-src)
+# Everything under src/main, before we remove android-unsupported files.
+testng_src_files_unfiltered := $(call all-java-files-under,src/main)
+# The nominal files we use to build for Android is everything in src/main that's supported, plus everything in android-src.
+testng_src_files := $(filter-out $(testng_android_unsupported_src_files),$(testng_src_files_unfiltered)) $(testng_src_files_android_specific)
+
+##
+## Build rules follow.
+##
+
+# host jar
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(testng_src_files)
+LOCAL_MODULE := testng-host
+LOCAL_STATIC_JAVA_LIBRARIES := jcommander-host snakeyaml-host guice-host
+LOCAL_JAVA_LIBRARIES := junit
+LOCAL_IS_HOST_MODULE := true
+include $(LOCAL_PATH)/GenerateTemplates.mk # Generate Version.java
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+# host dex
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(testng_src_files)
+LOCAL_MODULE := testng-hostdex
+LOCAL_STATIC_JAVA_LIBRARIES := jcommander-hostdex snakeyaml-hostdex guice-hostdex
+LOCAL_JAVA_LIBRARIES := junit-hostdex junit4-target-hostdex
+include $(LOCAL_PATH)/GenerateTemplates.mk # Generate Version.java
+include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
+
+# TODO: also add the tests once we have testng working.
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..8f54a5f
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,53 @@
+# 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# Clean up generated code from testng-hostdex
+# (When removing a file from $(testng_src_files_need_gen),
+# make a rule similar to below to remove stale files from incremental builds).
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/gen/JAVA_LIBRARIES/testng-hostdex_intermediates)/src/main/resources/org/testng/internal/VersionTemplateJava
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/GenerateTemplates.mk b/GenerateTemplates.mk
new file mode 100644
index 0000000..ae02421
--- /dev/null
+++ b/GenerateTemplates.mk
@@ -0,0 +1,42 @@
+# 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.
+#
+
+#
+# Build support for testng within the Android Open Source Project
+# See https://source.android.com/source/building.html for more information
+#
+#
+
+# This file generates Version.java.
+# Factored out as a separate file for reuse between multiple Android build rules.
+
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+
+# Apply all of the Android patches in src/patches/android by running patch-android-src script on them.
+intermediates:= $(local-generated-sources-dir)
+GEN := $(addprefix $(intermediates)/, $(testng_src_files_need_gen)) # List of all files that need to be generated.
+$(GEN) : PRIVATE_PATH := $(testng_path)
+## ./generate-version-file ./src/main/resources/org/testng/internal/VersionTemplateJava "@version@" kobalt/src/Build.kt "VERSION" > Version.java
+$(GEN) : PRIVATE_CUSTOM_TOOL = \
+  $(PRIVATE_PATH)/generate-version-file "$(PRIVATE_PATH)/src/main/resources/org/testng/internal/VersionTemplateJava" \
+  "@version@" \
+  "$(PRIVATE_PATH)/kobalt/src/Build.kt" \
+  "VERSION" > $@
+$(GEN): $(intermediates)/%.java : $(LOCAL_PATH)/src/main/resources/org/testng/internal/VersionTemplateJava \
+  $(LOCAL_PATH)/kobalt/src/Build.kt \
+  $(LOCAL_PATH)/generate-version-file
+	$(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
diff --git a/android-src/java/org/testng/internal/Bsh.java b/android-src/java/org/testng/internal/Bsh.java
new file mode 100644
index 0000000..84c598f
--- /dev/null
+++ b/android-src/java/org/testng/internal/Bsh.java
@@ -0,0 +1,24 @@
+/**
+ * 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 org.testng.internal;
+
+public class Bsh extends BshMock {
+  public Bsh() {
+    throw new AssertionError("Bsh not supported on AOSP, use BshMock instead.");
+  }
+}
+
diff --git a/generate-version-file b/generate-version-file
new file mode 100755
index 0000000..d17edc3
--- /dev/null
+++ b/generate-version-file
@@ -0,0 +1,59 @@
+#!/usr/bin/env bash
+
+# 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.
+#
+
+# Nominally used to generate the "Version.java" file, but in theory could be used
+# for any super simple template generation as well.
+
+if [[ $# -lt 4 ]]; then
+  echo "Usage: $(basename $0) <template-file> <template-variable> <value-file> <value-variable>" >&2
+  echo ""
+  echo 'For example, ./generate-version-file src/main/resources/org/testng/internal/VersionTemplateJava "@version@" kobalt/src/Build.kt "VERSION"'
+  exit 1
+fi
+
+template_file="$1"
+template_variable="$2"
+value_file="$3"
+value_variable="$4"
+
+if ! [[ -f $template_file ]]; then
+  echo "Error: Template file $template_file does not exist." >&2
+  exit 1
+fi
+
+if ! [[ -f $value_file ]]; then
+  echo "Error: Value file $value_File does not exist." >&2
+  exit 1
+fi
+
+# Read a 'val VERSION = "SOME_VERSION"' from the file, trim down to $SOME_VERSION.
+stored_value="$(egrep "val[[:space:]]+$value_variable" "$value_file" | awk 'NF>1{print $NF}' | tr -d '"')"
+if [[ $? -ne 0 ]]; then
+  echo "Error: Could not find value $value_variable in $value_file of syntax 'val $value_variable = \"SOME_VALUE\"'" >&2
+  exit 1
+fi
+
+# Ensure that the template does indeed have @version@
+if ! grep --silent "$template_variable" "$template_file"; then
+  echo "Error: Template file $template_file has no instances of template variable $template_variable." >&2
+  exit 1
+fi
+
+set -e
+
+# Apply the template, replacing @version@ with the VERSION.
+sed -e "s:$template_variable:$stored_value:g" "$template_file"
diff --git a/src/main/java/org/testng/ReporterConfig.java b/src/main/java/org/testng/ReporterConfig.java
index de7f362..e3e0639 100755
--- a/src/main/java/org/testng/ReporterConfig.java
+++ b/src/main/java/org/testng/ReporterConfig.java
@@ -2,7 +2,8 @@
 
 import org.testng.collections.Lists;
 import org.testng.internal.ClassHelper;
-import org.testng.internal.PropertyUtils;
+import org.testng.internal.IPropertyUtils;
+import org.testng.internal.PropertyUtilsFactory;
 import org.testng.internal.Utils;
 
 import java.util.List;
@@ -27,6 +28,11 @@
    */
   private List<Property> m_properties = Lists.newArrayList();
 
+  /**
+   * JavaBeans properties access helper
+   */
+  private IPropertyUtils mPropertyUtils = PropertyUtilsFactory.newInstance();
+
   public void addProperty(Property property) {
     m_properties.add(property);
   }
@@ -99,7 +105,7 @@
     if (reporterClass != null) {
       result = ClassHelper.newInstance(reporterClass);
       for (ReporterConfig.Property property : m_properties) {
-        PropertyUtils.setProperty(result, property.getName(), property.getValue());
+        mPropertyUtils.setProperty(result, property.getName(), property.getValue());
       }
     }
     return result;
diff --git a/src/main/java/org/testng/TestNG.java b/src/main/java/org/testng/TestNG.java
index 1925690..c3c7d7b 100644
--- a/src/main/java/org/testng/TestNG.java
+++ b/src/main/java/org/testng/TestNG.java
@@ -5,8 +5,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URLClassLoader;
-import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -30,8 +28,10 @@
 import org.testng.internal.Configuration;
 import org.testng.internal.DynamicGraph;
 import org.testng.internal.IConfiguration;
+import org.testng.internal.IPathUtils;
 import org.testng.internal.IResultListener2;
 import org.testng.internal.OverrideProcessor;
+import org.testng.internal.PathUtilsFactory;
 import org.testng.internal.SuiteRunnerMap;
 import org.testng.internal.Utils;
 import org.testng.internal.Version;
@@ -191,6 +191,8 @@
 
   private boolean m_isInitialized = false;
 
+  private IPathUtils m_pathUtils = PathUtilsFactory.newInstance();
+
   /**
    * Default constructor. Setting also usage of default listeners/reporters.
    */
@@ -278,9 +280,8 @@
     	//to parse the suite files (<suite-file>), if any
     	for (XmlSuite s: m_suites) {
         for (String suiteFile : s.getSuiteFiles()) {
-            Path rootPath = Paths.get(s.getFileName()).getParent();
             try {
-                Collection<XmlSuite> childSuites = getParser(rootPath.resolve(suiteFile).normalize().toString()).parse();
+                Collection<XmlSuite> childSuites = getParser(m_pathUtils.getSuiteNormalizedPath(s, suiteFile)).parse();
                 for (XmlSuite cSuite : childSuites){
                     cSuite.setParentSuite(s);
                     s.getChildSuites().add(cSuite);
diff --git a/src/main/java/org/testng/internal/IPathUtils.java b/src/main/java/org/testng/internal/IPathUtils.java
new file mode 100755
index 0000000..325e3bd
--- /dev/null
+++ b/src/main/java/org/testng/internal/IPathUtils.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 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 org.testng.internal;
+
+import org.testng.xml.XmlSuite;
+
+/**
+ * Utility class for setting JavaBeans-style properties on instances.
+ */
+public interface IPathUtils {
+  public String getSuiteNormalizedPath(XmlSuite suite, String suiteFile);
+}
diff --git a/src/main/java/org/testng/internal/IPropertyUtils.java b/src/main/java/org/testng/internal/IPropertyUtils.java
new file mode 100755
index 0000000..2ea178e
--- /dev/null
+++ b/src/main/java/org/testng/internal/IPropertyUtils.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 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 org.testng.internal;
+
+/**
+ * Utility class for setting JavaBeans-style properties on instances.
+ */
+public interface IPropertyUtils {
+  public void setProperty(Object instance, String name, String value);
+
+  public Class getPropertyType(Class instanceClass, String propertyName);
+
+  public void setPropertyRealValue(Object instance, String name, Object value);
+}
diff --git a/src/main/java/org/testng/internal/PathUtils.java b/src/main/java/org/testng/internal/PathUtils.java
new file mode 100755
index 0000000..2a2ec33
--- /dev/null
+++ b/src/main/java/org/testng/internal/PathUtils.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 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 org.testng.internal;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.testng.xml.XmlSuite;
+
+/**
+ * Utility class for using Paths.
+ */
+public class PathUtils implements IPathUtils {
+  public String getSuiteNormalizedPath(XmlSuite suite, String suiteFile) {
+    Path rootPath = Paths.get(suite.getFileName()).getParent();
+    return rootPath.resolve(suiteFile).normalize().toString();
+  }
+}
diff --git a/src/main/java/org/testng/internal/PathUtilsFactory.java b/src/main/java/org/testng/internal/PathUtilsFactory.java
new file mode 100644
index 0000000..f13c7c6
--- /dev/null
+++ b/src/main/java/org/testng/internal/PathUtilsFactory.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 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 org.testng.internal;
+
+import java.lang.reflect.Constructor;
+
+/**
+ * Factory for IPathUtils that returns a concrete instance.
+ */
+public class PathUtilsFactory {
+
+  /**
+   * Tries to make a real PathUtils, if the platform supports it. Otherwise creates
+   * a mock PathUtils that throws UnsupportedOperationException if any method is called on it.
+   */
+  public static IPathUtils newInstance() {
+    try {
+      Class<?> propertyUtilsClass = Class.forName("org.testng.internal.PathUtils");
+      Constructor<?> constructor = propertyUtilsClass.getConstructor();
+      try {
+        return (IPathUtils)constructor.newInstance();
+      }
+      catch (Exception e) {
+        // Impossible: Constructor should not be failing.
+        throw new AssertionError(e);
+      }
+    } catch (ClassNotFoundException e) {
+      // OK: On a platform where java beans are not supported
+      return new PathUtilsMock();
+    } catch (NoSuchMethodException e) {
+      // Impossible. PathUtils should have a 0-arg constructor.
+      throw new AssertionError(e);
+    }
+  }
+
+  private PathUtilsFactory() {}
+}
diff --git a/src/main/java/org/testng/internal/PathUtilsMock.java b/src/main/java/org/testng/internal/PathUtilsMock.java
new file mode 100755
index 0000000..ada51b4
--- /dev/null
+++ b/src/main/java/org/testng/internal/PathUtilsMock.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 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 org.testng.internal;
+
+import org.testng.xml.XmlSuite;
+
+/**
+ * Utility class for using Paths.
+ */
+public class PathUtilsMock implements IPathUtils {
+  public String getSuiteNormalizedPath(XmlSuite suite, String suiteFile) {
+    throw new UnsupportedOperationException();
+  }
+}
diff --git a/src/main/java/org/testng/internal/PropertyUtils.java b/src/main/java/org/testng/internal/PropertyUtils.java
index ba69110..0864d07 100755
--- a/src/main/java/org/testng/internal/PropertyUtils.java
+++ b/src/main/java/org/testng/internal/PropertyUtils.java
@@ -14,11 +14,11 @@
  *
  * @author Cosmin Marginean, Apr 12, 2007
  */
-public class PropertyUtils {
+public class PropertyUtils implements IPropertyUtils {
 
   private static final Logger LOGGER = Logger.getLogger(PropertyUtils.class);
 
-  public static void setProperty(Object instance, String name, String value) {
+  public void setProperty(Object instance, String name, String value) {
     if (instance == null) {
       LOGGER.warn("Cannot set property " + name + " with value " + value + ". The target instance is null");
       return;
@@ -35,7 +35,7 @@
     setPropertyRealValue(instance, name, realValue);
   }
 
-  public static Class getPropertyType(Class instanceClass, String propertyName) {
+  public Class getPropertyType(Class instanceClass, String propertyName) {
     if (instanceClass == null) {
       LOGGER.warn("Cannot retrieve property class for " + propertyName + ". Target instance class is null");
     }
@@ -64,7 +64,7 @@
     return result;
   }
 
-  public static void setPropertyRealValue(Object instance, String name, Object value) {
+  public void setPropertyRealValue(Object instance, String name, Object value) {
     if (instance == null) {
       LOGGER.warn("Cannot set property " + name + " with value " + value + ". Targe instance is null");
       return;
diff --git a/src/main/java/org/testng/internal/PropertyUtilsFactory.java b/src/main/java/org/testng/internal/PropertyUtilsFactory.java
new file mode 100755
index 0000000..11a446e
--- /dev/null
+++ b/src/main/java/org/testng/internal/PropertyUtilsFactory.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 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 org.testng.internal;
+
+import java.lang.reflect.Constructor;
+
+/**
+ * Factory for IPropertyUtils that returns a concrete instance.
+ */
+public class PropertyUtilsFactory {
+
+  /**
+   * Tries to make a real PropertyUtils, if the platform supports it. Otherwise creates
+   * a mock PropertyUtils that throws UnsupportedOperationException if any method is called on it.
+   */
+  public static IPropertyUtils newInstance() {
+    try {
+      Class<?> propertyUtilsClass = Class.forName("org.testng.internal.PropertyUtils");
+      Constructor<?> constructor = propertyUtilsClass.getConstructor();
+      try {
+        return (IPropertyUtils)constructor.newInstance();
+      }
+      catch (Exception e) {
+        // Impossible: Constructor should not be failing.
+        throw new AssertionError(e);
+      }
+    } catch (ClassNotFoundException e) {
+      // OK: On a platform where java beans are not supported
+      return new PropertyUtilsMock();
+    } catch (NoSuchMethodException e) {
+      // Impossible. PropertyUtils should have a 0-arg constructor.
+      throw new AssertionError(e);
+    }
+  }
+
+  private PropertyUtilsFactory() {}
+}
diff --git a/src/main/java/org/testng/internal/PropertyUtilsMock.java b/src/main/java/org/testng/internal/PropertyUtilsMock.java
new file mode 100755
index 0000000..9333177
--- /dev/null
+++ b/src/main/java/org/testng/internal/PropertyUtilsMock.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 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 org.testng.internal;
+
+/**
+ * Utility class for setting JavaBeans-style properties on instances.
+ *
+ * <p>Mock version for platforms that don't support java beans</p>.
+ */
+public class PropertyUtilsMock implements IPropertyUtils {
+  public void setProperty(Object instance, String name, String value) {
+    throw new UnsupportedOperationException();
+  }
+
+  public Class getPropertyType(Class instanceClass, String propertyName) {
+    throw new UnsupportedOperationException();
+  }
+
+  public void setPropertyRealValue(Object instance, String name, Object value) {
+    throw new UnsupportedOperationException();
+  }
+}