Add business logic handling to compatibility infra
bug: 62867056
Test: run gts -j32
Change-Id: I4646a46e0951fbe1147613037b1aca1260ba8dd1
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutor.java b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutor.java
new file mode 100644
index 0000000..ec940ce
--- /dev/null
+++ b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutor.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 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.compatibility.common.util;
+
+import android.content.Context;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Execute business logic methods for device side test cases
+ */
+public class BusinessLogicDeviceExecutor extends BusinessLogicExecutor {
+
+ private Context mContext;
+ private Object mTestObj;
+
+ public BusinessLogicDeviceExecutor(Context context, Object testObj) {
+ mContext = context;
+ mTestObj = testObj;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected Object getTestObject() {
+ return mTestObj;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected ResolvedMethod getResolvedMethod(Class cls, String methodName, String... args)
+ throws ClassNotFoundException {
+ List<Method> nameMatches = getMethodsWithName(cls, methodName);
+ for (Method m : nameMatches) {
+ ResolvedMethod rm = new ResolvedMethod(m);
+ int paramTypesMatched = 0;
+ int argsUsed = 0;
+ Class[] paramTypes = m.getParameterTypes();
+ for (Class paramType : paramTypes) {
+ if (argsUsed == args.length && paramType.equals(String.class)) {
+ // We've used up all supplied string args, so this method will not match.
+ // If paramType is the Context class, we can match a paramType without needing
+ // more string args. similarly, paramType "String[]" can be matched with zero
+ // string args. If we add support for more paramTypes, this logic may require
+ // adjustment.
+ break;
+ }
+ if (paramType.equals(String.class)) {
+ // Type "String" -- supply the next available arg
+ rm.addArg(args[argsUsed++]);
+ } else if (Context.class.isAssignableFrom(paramType)) {
+ // Type "Context" -- supply the context from the test case
+ rm.addArg(mContext);
+ } else if (paramType.equals(Class.forName(STRING_ARRAY_CLASS))) {
+ // Type "String[]" (or "String...") -- supply all remaining args
+ rm.addArg(Arrays.copyOfRange(args, argsUsed, args.length));
+ argsUsed += (args.length - argsUsed);
+ } else {
+ break; // Param type is unrecognized, this method will not match.
+ }
+ paramTypesMatched++; // A param type has been matched when reaching this point.
+ }
+ if (paramTypesMatched == paramTypes.length && argsUsed == args.length) {
+ return rm; // Args match, methods match, so return the first method-args pairing.
+ }
+ // Not a match, try args for next method that matches by name.
+ }
+ throw new RuntimeException(String.format(
+ "BusinessLogic: Failed to invoke action method %s with args: %s", methodName,
+ Arrays.toString(args)));
+ }
+}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
new file mode 100644
index 0000000..9b3a0b6
--- /dev/null
+++ b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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.compatibility.common.util;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Device-side base class for tests leveraging the Business Logic service.
+ */
+public class BusinessLogicTestCase {
+
+ /* String marking the beginning of the parameter in a test name */
+ private static final String PARAM_START = "[";
+
+ /* Test name rule that tracks the current test method under execution */
+ @Rule public TestName mTestCase = new TestName();
+
+ private static BusinessLogic mBusinessLogic;
+
+ @BeforeClass
+ public static void prepareBusinessLogic() {
+ File businessLogicFile = new File(BusinessLogic.DEVICE_FILE);
+ mBusinessLogic = BusinessLogicFactory.createFromFile(businessLogicFile);
+ }
+
+ @Before
+ public void executeBusinessLogic() {
+ String methodName = mTestCase.getMethodName();
+ if (methodName.contains(PARAM_START)) {
+ // Strip parameter suffix (e.g. "[0]") from method name
+ methodName = methodName.substring(0, methodName.lastIndexOf(PARAM_START));
+ }
+ String testName = String.format("%s#%s", this.getClass().getName(), methodName);
+ if (mBusinessLogic.hasLogicFor(testName)) {
+ Log.i("Finding business logic for test case: ", testName);
+ BusinessLogicExecutor executor = new BusinessLogicDeviceExecutor(getContext(), this);
+ mBusinessLogic.applyLogicFor(testName, executor);
+ }
+ }
+
+ private Context getContext() {
+ return InstrumentationRegistry.getInstrumentation().getTargetContext();
+ }
+}
diff --git a/common/device-side/util/tests/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutorTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutorTest.java
new file mode 100644
index 0000000..578db9c
--- /dev/null
+++ b/common/device-side/util/tests/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutorTest.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2017 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.compatibility.common.util;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for {@line BusinessLogicDeviceExecutor}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class BusinessLogicDeviceExecutorTest {
+
+ private static final String THIS_CLASS =
+ "com.android.compatibility.common.util.BusinessLogicDeviceExecutorTest";
+ private static final String METHOD_1 = THIS_CLASS + ".method1";
+ private static final String METHOD_2 = THIS_CLASS + ".method2";
+ private static final String METHOD_3 = THIS_CLASS + ".method3";
+ private static final String METHOD_4 = THIS_CLASS + ".method4";
+ private static final String METHOD_5 = THIS_CLASS + ".method5";
+ private static final String METHOD_6 = THIS_CLASS + ".method6";
+ private static final String METHOD_7 = THIS_CLASS + ".method7";
+ private static final String METHOD_8 = THIS_CLASS + ".method8";
+ private static final String FAKE_METHOD = THIS_CLASS + ".methodDoesntExist";
+ private static final String ARG_STRING_1 = "arg1";
+ private static final String ARG_STRING_2 = "arg2";
+
+ private static final String OTHER_METHOD_1 = THIS_CLASS + "$OtherClass.method1";
+
+ private String mInvoked = null;
+ private Object[] mArgsUsed = null;
+ private Context mContext;
+ private BusinessLogicExecutor mExecutor;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mExecutor = new BusinessLogicDeviceExecutor(mContext, this);
+ // reset the instance variables tracking the method invoked and the args used
+ mInvoked = null;
+ mArgsUsed = null;
+ // reset the OtherClass class variable tracking the method invoked
+ OtherClass.otherInvoked = null;
+ }
+
+ @Test
+ public void testInvokeMethodInThisClass() throws Exception {
+ mExecutor.invokeMethod(METHOD_1);
+ // assert that mInvoked was set for this BusinessLogicDeviceExecutorTest instance
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_1);
+ }
+
+ @Test
+ public void testInvokeMethodInOtherClass() throws Exception {
+ mExecutor.invokeMethod(OTHER_METHOD_1);
+ // assert that OtherClass.method1 was invoked, and static field of OtherClass was changed
+ assertEquals("Failed to invoke method in other class", OtherClass.otherInvoked,
+ OTHER_METHOD_1);
+ }
+
+ @Test
+ public void testInvokeMethodWithStringArgs() throws Exception {
+ mExecutor.invokeMethod(METHOD_2, ARG_STRING_1, ARG_STRING_2);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_2);
+ // assert both String arguments were correctly set for method2
+ assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
+ assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
+ }
+
+ @Test
+ public void testInvokeMethodWithStringAndContextArgs() throws Exception {
+ mExecutor.invokeMethod(METHOD_3, ARG_STRING_1);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_3);
+ // assert that String arg and Context arg were correctly set for method3
+ assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
+ assertEquals("Failed to set second argument", mArgsUsed[1], mContext);
+ }
+
+ @Test
+ public void testInvokeMethodWithContextAndStringArgs() throws Exception {
+ mExecutor.invokeMethod(METHOD_4, ARG_STRING_1);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_4);
+ // Like testInvokeMethodWithStringAndContextArgs, but flip the args for method4
+ assertEquals("Failed to set first argument", mArgsUsed[0], mContext);
+ assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_1);
+ }
+
+ @Test
+ public void testInvokeMethodWithStringArrayArg() throws Exception {
+ mExecutor.invokeMethod(METHOD_5, ARG_STRING_1, ARG_STRING_2);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_5);
+ // assert both String arguments were correctly set for method5
+ assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
+ assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
+ }
+
+ @Test
+ public void testInvokeMethodWithEmptyStringArrayArg() throws Exception {
+ mExecutor.invokeMethod(METHOD_5);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_5);
+ // assert no String arguments were set for method5
+ assertEquals("Incorrectly set args", mArgsUsed.length, 0);
+ }
+
+ @Test
+ public void testInvokeMethodWithStringAndStringArrayArgs() throws Exception {
+ mExecutor.invokeMethod(METHOD_6, ARG_STRING_1, ARG_STRING_2);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_6);
+ // assert both String arguments were correctly set for method6
+ assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
+ assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
+ }
+
+ @Test
+ public void testInvokeMethodWithAllArgTypes() throws Exception {
+ mExecutor.invokeMethod(METHOD_7, ARG_STRING_1, ARG_STRING_2);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_7);
+ // assert all arguments were correctly set for method7
+ assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
+ assertEquals("Failed to set second argument", mArgsUsed[1], mContext);
+ assertEquals("Failed to set third argument", mArgsUsed[2], ARG_STRING_2);
+ }
+
+ @Test
+ public void testInvokeOverloadedMethodOneArg() throws Exception {
+ mExecutor.invokeMethod(METHOD_1, ARG_STRING_1);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_1);
+ assertEquals("Set wrong number of arguments", mArgsUsed.length, 1);
+ assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
+ }
+
+ @Test
+ public void testInvokeOverloadedMethodTwoArgs() throws Exception {
+ mExecutor.invokeMethod(METHOD_1, ARG_STRING_1, ARG_STRING_2);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_1);
+ assertEquals("Set wrong number of arguments", mArgsUsed.length, 2);
+ assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
+ assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testInvokeNonExistentMethod() throws Exception {
+ mExecutor.invokeMethod(FAKE_METHOD, ARG_STRING_1, ARG_STRING_2);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testInvokeMethodTooManyArgs() throws Exception {
+ mExecutor.invokeMethod(METHOD_3, ARG_STRING_1, ARG_STRING_2);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testInvokeMethodTooFewArgs() throws Exception {
+ mExecutor.invokeMethod(METHOD_2, ARG_STRING_1);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testInvokeMethodIncompatibleArgs() throws Exception {
+ mExecutor.invokeMethod(METHOD_8, ARG_STRING_1);
+ }
+
+ @Test
+ public void testExecuteConditionCheckReturnValue() throws Exception {
+ assertTrue("Wrong return value",
+ mExecutor.executeCondition(METHOD_2, ARG_STRING_1, ARG_STRING_1));
+ assertFalse("Wrong return value",
+ mExecutor.executeCondition(METHOD_2, ARG_STRING_1, ARG_STRING_2));
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testExecuteInvalidCondition() throws Exception {
+ mExecutor.executeCondition(METHOD_1); // method1 does not return type boolean
+ }
+
+ @Test
+ public void testExecuteAction() throws Exception {
+ mExecutor.executeAction(METHOD_2, ARG_STRING_1, ARG_STRING_2);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_2);
+ // assert both String arguments were correctly set for method2
+ assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
+ assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
+ }
+
+ public void method1() {
+ mInvoked = METHOD_1;
+ }
+
+ // overloaded method with one arg
+ public void method1(String arg1) {
+ mInvoked = METHOD_1;
+ mArgsUsed = new Object[]{arg1};
+ }
+
+ // overloaded method with two args
+ public void method1(String arg1, String arg2) {
+ mInvoked = METHOD_1;
+ mArgsUsed = new Object[]{arg1, arg2};
+ }
+
+ public boolean method2(String arg1, String arg2) {
+ mInvoked = METHOD_2;
+ mArgsUsed = new Object[]{arg1, arg2};
+ return arg1.equals(arg2);
+ }
+
+ public void method3(String arg1, Context arg2) {
+ mInvoked = METHOD_3;
+ mArgsUsed = new Object[]{arg1, arg2};
+ }
+
+ // Same as method3, but flipped args
+ public void method4(Context arg1, String arg2) {
+ mInvoked = METHOD_4;
+ mArgsUsed = new Object[]{arg1, arg2};
+ }
+
+ public void method5(String... args) {
+ mInvoked = METHOD_5;
+ mArgsUsed = args;
+ }
+
+ public void method6(String arg1, String... moreArgs) {
+ mInvoked = METHOD_6;
+ List<String> allArgs = new ArrayList<>();
+ allArgs.add(arg1);
+ allArgs.addAll(Arrays.asList(moreArgs));
+ mArgsUsed = allArgs.toArray(new String[0]);
+ }
+
+ public void method7(String arg1, Context arg2, String... moreArgs) {
+ mInvoked = METHOD_7;
+ List<Object> allArgs = new ArrayList<>();
+ allArgs.add(arg1);
+ allArgs.add(arg2);
+ allArgs.addAll(Arrays.asList(moreArgs));
+ mArgsUsed = allArgs.toArray(new Object[0]);
+ }
+
+ public void method8(String arg1, Integer arg2) {
+ // This method should never be successfully invoked, since Integer parameter types are
+ // unsupported for the BusinessLogic service
+ }
+
+ public static class OtherClass {
+
+ public static String otherInvoked = null;
+
+ public void method1() {
+ otherInvoked = OTHER_METHOD_1;
+ }
+ }
+}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
index 4eb0843..27d3976 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
@@ -44,6 +44,7 @@
private static final String ROOT_DIR2 = "ROOT_DIR2";
private static final String DYNAMIC_CONFIG_OVERRIDE_URL = "DYNAMIC_CONFIG_OVERRIDE_URL";
+ private static final String BUSINESS_LOGIC_HOST_FILE = "BUSINESS_LOGIC_HOST_FILE";
private static final String RETRY_COMMAND_LINE_ARGS = "retry_command_line_args";
private static final String ALT_HOST_TESTCASE_DIR = "ANDROID_HOST_OUT_TESTCASES";
private static final String ALT_TARGET_TESTCASE_DIR = "ANDROID_TARGET_OUT_TESTCASES";
@@ -111,6 +112,10 @@
configFile.getAbsolutePath());
}
+ public void setBusinessLogicHostFile(File hostFile) {
+ mBuildInfo.addBuildAttribute(BUSINESS_LOGIC_HOST_FILE, hostFile.getAbsolutePath());
+ }
+
public void setModuleIds(String[] moduleIds) {
mBuildInfo.addBuildAttribute(MODULE_IDS, String.join(",", moduleIds));
}
@@ -126,6 +131,10 @@
return configMap;
}
+ public File getBusinessLogicHostFile() {
+ return new File(mBuildInfo.getBuildAttributes().get(BUSINESS_LOGIC_HOST_FILE));
+ }
+
/**
* @return a {@link File} representing the directory holding the Compatibility installation
* @throws FileNotFoundException if the directory does not exist
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BusinessLogicPreparer.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BusinessLogicPreparer.java
new file mode 100644
index 0000000..fded8bf
--- /dev/null
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BusinessLogicPreparer.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2017 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.compatibility.common.tradefed.targetprep;
+
+import com.android.compatibility.SuiteInfo;
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.util.BusinessLogic;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.OptionClass;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.targetprep.BuildError;
+import com.android.tradefed.targetprep.ITargetCleaner;
+import com.android.tradefed.targetprep.TargetSetupError;
+import com.android.tradefed.util.FileUtil;
+import com.android.tradefed.util.StreamUtil;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+/**
+ * Pushes business Logic to the host and the test device, for use by test cases in the test suite.
+ */
+@OptionClass(alias="business-logic-preparer")
+public class BusinessLogicPreparer implements ITargetCleaner {
+
+ private static final String LOG_TAG = BusinessLogicPreparer.class.getSimpleName();
+
+ /* Placeholder in the service URL for the suite to be configured */
+ private static final String SUITE_PLACEHOLDER = "{suite-name}";
+
+ /* String for creating files to store the business logic configuration on the host */
+ private static final String FILE_LOCATION = "business-logic";
+ /* Extension of business logic files */
+ private static final String FILE_EXT = ".bl";
+
+ @Option(name = "business-logic-url", description = "The URL to use when accessing the " +
+ "business logic service, parameters not included", mandatory = true)
+ private String mUrl;
+
+ @Option(name = "business-logic-api-key", description = "The API key to use when accessing " +
+ "the business logic service.", mandatory = true)
+ private String mApiKey;
+
+ @Option(name = "cleanup", description = "Whether to remove config files from the test " +
+ "target after test completion.")
+ private boolean mCleanup = true;
+
+ private String mDeviceFilePushed;
+ private String mHostFilePushed;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError, BuildError,
+ DeviceNotAvailableException {
+ // Piece together request URL
+ String requestString = String.format("%s?key=%s", mUrl.replace(SUITE_PLACEHOLDER,
+ SuiteInfo.NAME), mApiKey);
+ // Retrieve business logic string from service
+ String businessLogicString = null;
+ try {
+ URL request = new URL(requestString);
+ businessLogicString = StreamUtil.getStringFromStream(request.openStream());
+ } catch (IOException e) {
+ throw new TargetSetupError(String.format(
+ "Cannot connect to business logic service for suite %s", SuiteInfo.NAME), e,
+ device.getDeviceDescriptor());
+ }
+ // Push business logic string to host file
+ try {
+ File hostFile = FileUtil.createTempFile(FILE_LOCATION, FILE_EXT);
+ FileUtil.writeToFile(businessLogicString, hostFile);
+ mHostFilePushed = hostFile.getAbsolutePath();
+ CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(buildInfo);
+ buildHelper.setBusinessLogicHostFile(hostFile);
+ } catch (IOException e) {
+ throw new TargetSetupError(String.format(
+ "Retrieved business logic for suite %s could not be written to host",
+ SuiteInfo.NAME), device.getDeviceDescriptor());
+ }
+ // Push business logic string to device file
+ removeDeviceFile(device); // remove any existing business logic file from device
+ if (device.pushString(businessLogicString, BusinessLogic.DEVICE_FILE)) {
+ mDeviceFilePushed = BusinessLogic.DEVICE_FILE;
+ } else {
+ throw new TargetSetupError(String.format(
+ "Retrieved business logic for suite %s could not be written to device %s",
+ SuiteInfo.NAME, device.getSerialNumber()), device.getDeviceDescriptor());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)
+ throws DeviceNotAvailableException {
+ // Clean up host file
+ if (mCleanup) {
+ if (mHostFilePushed != null) {
+ FileUtil.deleteFile(new File(mHostFilePushed));
+ }
+ if (mDeviceFilePushed != null && !(e instanceof DeviceNotAvailableException)) {
+ removeDeviceFile(device);
+ }
+ }
+ }
+
+ /** Remove business logic file from the device */
+ private static void removeDeviceFile(ITestDevice device) throws DeviceNotAvailableException {
+ device.executeShellCommand(String.format("rm -rf %s", BusinessLogic.DEVICE_FILE));
+ }
+}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicHostTestBase.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicHostTestBase.java
new file mode 100644
index 0000000..7d5ab12
--- /dev/null
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicHostTestBase.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 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.compatibility.common.tradefed.testtype;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.util.BusinessLogic;
+import com.android.compatibility.common.util.BusinessLogicExecutor;
+import com.android.compatibility.common.util.BusinessLogicFactory;
+import com.android.compatibility.common.util.BusinessLogicHostExecutor;
+import com.android.tradefed.log.LogUtil.CLog;
+
+import java.io.File;
+
+/**
+ * Host-side base class for tests leveraging the Business Logic service.
+ */
+public class BusinessLogicHostTestBase extends CompatibilityHostTestBase {
+
+ /* String marking the beginning of the parameter in a test name */
+ private static final String PARAM_START = "[";
+
+ /* Test name rule that tracks the current test method under execution */
+ @Rule public TestName mTestCase = new TestName();
+
+ private static BusinessLogic mBusinessLogic;
+
+ @Before
+ public void executeBusinessLogic() {
+ // Business logic must be retrieved in this @Before method, since the build info contains
+ // the location of the business logic file and cannot be referenced from a static context
+ if (mBusinessLogic == null) {
+ CompatibilityBuildHelper helper = new CompatibilityBuildHelper(mBuild);
+ File businessLogicFile = helper.getBusinessLogicHostFile();
+ mBusinessLogic = BusinessLogicFactory.createFromFile(businessLogicFile);
+ }
+
+ String methodName = mTestCase.getMethodName();
+ if (methodName.contains(PARAM_START)) {
+ // Strip parameter suffix (e.g. "[0]") from method name
+ methodName = methodName.substring(0, methodName.lastIndexOf(PARAM_START));
+ }
+ String testName = String.format("%s#%s", this.getClass().getName(), methodName);
+ if (mBusinessLogic.hasLogicFor(testName)) {
+ CLog.i("Applying business logic for test case: ", testName);
+ BusinessLogicExecutor executor = new BusinessLogicHostExecutor(getDevice(),
+ mBuild, this);
+ mBusinessLogic.applyLogicFor(testName, executor);
+ }
+ }
+}
diff --git a/common/host-side/util/src/com/android/compatibility/common/util/BusinessLogicHostExecutor.java b/common/host-side/util/src/com/android/compatibility/common/util/BusinessLogicHostExecutor.java
new file mode 100644
index 0000000..43bdea1
--- /dev/null
+++ b/common/host-side/util/src/com/android/compatibility/common/util/BusinessLogicHostExecutor.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 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.compatibility.common.util;
+
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.ITestDevice;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Execute business logic methods for host side test cases
+ */
+public class BusinessLogicHostExecutor extends BusinessLogicExecutor {
+
+ private ITestDevice mDevice;
+ private IBuildInfo mBuild;
+ private Object mTestObj;
+
+ public BusinessLogicHostExecutor(ITestDevice device, IBuildInfo build, Object testObj) {
+ mDevice = device;
+ mBuild = build;
+ mTestObj = testObj;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected Object getTestObject() {
+ return mTestObj;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected ResolvedMethod getResolvedMethod(Class cls, String methodName, String... args)
+ throws ClassNotFoundException {
+ List<Method> nameMatches = getMethodsWithName(cls, methodName);
+ for (Method m : nameMatches) {
+ ResolvedMethod rm = new ResolvedMethod(m);
+ int paramTypesMatched = 0;
+ int argsUsed = 0;
+ Class[] paramTypes = m.getParameterTypes();
+ for (Class paramType : paramTypes) {
+ if (argsUsed == args.length && paramType.equals(String.class)) {
+ // We've used up all supplied string args, so this method will not match.
+ // If paramType is the ITestDevice or IBuildInfo class, we can match a
+ // paramType without needing more string args. similarly, paramType "String[]"
+ // can be matched with zero string args. If we add support for more paramTypes,
+ // this logic may require adjustment.
+ break;
+ }
+ if (paramType.equals(String.class)) {
+ // Type "String" -- supply the next available arg
+ rm.addArg(args[argsUsed++]);
+ } else if (ITestDevice.class.isAssignableFrom(paramType)) {
+ rm.addArg(mDevice);
+ } else if (IBuildInfo.class.isAssignableFrom(paramType)) {
+ rm.addArg(mBuild);
+ } else if (paramType.equals(Class.forName(STRING_ARRAY_CLASS))) {
+ // Type "String[]" (or "String...") -- supply all remaining args
+ rm.addArg(Arrays.copyOfRange(args, argsUsed, args.length));
+ argsUsed += (args.length - argsUsed);
+ } else {
+ break; // Param type is unrecognized, this method will not match.
+ }
+ paramTypesMatched++; // A param type has been matched when reaching this point.
+ }
+ if (paramTypesMatched == paramTypes.length && argsUsed == args.length) {
+ return rm; // Args match, methods match, so return the first method-args pairing.
+ }
+ // Not a match, try args for next method that matches by name.
+ }
+ throw new RuntimeException(String.format(
+ "BusinessLogic: Failed to invoke action method %s with args: %s", methodName,
+ Arrays.toString(args)));
+ }
+}
diff --git a/common/host-side/util/tests/Android.mk b/common/host-side/util/tests/Android.mk
index 338d6c3..5741ec4 100644
--- a/common/host-side/util/tests/Android.mk
+++ b/common/host-side/util/tests/Android.mk
@@ -18,7 +18,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAVA_LIBRARIES := compatibility-host-util junit-host json-prebuilt tradefed
+LOCAL_JAVA_LIBRARIES := compatibility-host-util easymock junit-host json-prebuilt tradefed
LOCAL_MODULE := compatibility-host-util-tests
diff --git a/common/host-side/util/tests/src/com/android/compatibility/common/util/BusinessLogicHostExecutorTest.java b/common/host-side/util/tests/src/com/android/compatibility/common/util/BusinessLogicHostExecutorTest.java
new file mode 100644
index 0000000..2940f86
--- /dev/null
+++ b/common/host-side/util/tests/src/com/android/compatibility/common/util/BusinessLogicHostExecutorTest.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2017 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.compatibility.common.util;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.ITestDevice;
+
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for {@link BusinessLogicHostExecutor}.
+ */
+@RunWith(JUnit4.class)
+public class BusinessLogicHostExecutorTest {
+
+ private static final String THIS_CLASS =
+ "com.android.compatibility.common.util.BusinessLogicHostExecutorTest";
+ private static final String METHOD_1 = THIS_CLASS + ".method1";
+ private static final String METHOD_2 = THIS_CLASS + ".method2";
+ private static final String METHOD_3 = THIS_CLASS + ".method3";
+ private static final String METHOD_4 = THIS_CLASS + ".method4";
+ private static final String METHOD_5 = THIS_CLASS + ".method5";
+ private static final String METHOD_6 = THIS_CLASS + ".method6";
+ private static final String METHOD_7 = THIS_CLASS + ".method7";
+ private static final String METHOD_8 = THIS_CLASS + ".method8";
+ private static final String FAKE_METHOD = THIS_CLASS + ".methodDoesntExist";
+ private static final String ARG_STRING_1 = "arg1";
+ private static final String ARG_STRING_2 = "arg2";
+
+ private static final String OTHER_METHOD_1 = THIS_CLASS + "$OtherClass.method1";
+
+ private String mInvoked = null;
+ private Object[] mArgsUsed = null;
+ private IBuildInfo mMockBuild;
+ private ITestDevice mMockDevice;
+ private BusinessLogicExecutor mExecutor;
+
+ @Before
+ public void setUp() {
+ mMockBuild = EasyMock.createMock(IBuildInfo.class);
+ mMockDevice = EasyMock.createMock(ITestDevice.class);
+ mExecutor = new BusinessLogicHostExecutor(mMockDevice, mMockBuild, this);
+ // reset the instance variables tracking the method invoked and the args used
+ mInvoked = null;
+ mArgsUsed = null;
+ // reset the OtherClass class variable tracking the method invoked
+ OtherClass.otherInvoked = null;
+ }
+
+ @Test
+ public void testInvokeMethodInThisClass() throws Exception {
+ mExecutor.invokeMethod(METHOD_1);
+ // assert that mInvoked was set for this BusinessLogicDeviceExecutorTest instance
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_1);
+ }
+
+ @Test
+ public void testInvokeMethodInOtherClass() throws Exception {
+ mExecutor.invokeMethod(OTHER_METHOD_1);
+ // assert that OtherClass.method1 was invoked, and static field of OtherClass was changed
+ assertEquals("Failed to invoke method in other class", OtherClass.otherInvoked,
+ OTHER_METHOD_1);
+ }
+
+ @Test
+ public void testInvokeMethodWithStringArgs() throws Exception {
+ mExecutor.invokeMethod(METHOD_2, ARG_STRING_1, ARG_STRING_2);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_2);
+ // assert both String arguments were correctly set for method2
+ assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
+ assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
+ }
+
+ @Test
+ public void testInvokeMethodWithStringAndDeviceArgs() throws Exception {
+ mExecutor.invokeMethod(METHOD_3, ARG_STRING_1);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_3);
+ // assert that String arg and ITestDevice arg were correctly set for method3
+ assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
+ assertEquals("Failed to set second argument", mArgsUsed[1], mMockDevice);
+ }
+
+ @Test
+ public void testInvokeMethodWithDeviceAndStringArgs() throws Exception {
+ mExecutor.invokeMethod(METHOD_4, ARG_STRING_1);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_4);
+ // Like testInvokeMethodWithStringAndDeviceArgs, but flip the args for method4
+ assertEquals("Failed to set first argument", mArgsUsed[0], mMockDevice);
+ assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_1);
+ }
+
+ @Test
+ public void testInvokeMethodWithStringArrayArg() throws Exception {
+ mExecutor.invokeMethod(METHOD_5, ARG_STRING_1, ARG_STRING_2);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_5);
+ // assert both String arguments were correctly set for method5
+ assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
+ assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
+ }
+
+ @Test
+ public void testInvokeMethodWithEmptyStringArrayArg() throws Exception {
+ mExecutor.invokeMethod(METHOD_5);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_5);
+ // assert no String arguments were set for method5
+ assertEquals("Incorrectly set args", mArgsUsed.length, 0);
+ }
+
+ @Test
+ public void testInvokeMethodWithStringAndStringArrayArgs() throws Exception {
+ mExecutor.invokeMethod(METHOD_6, ARG_STRING_1, ARG_STRING_2);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_6);
+ // assert both String arguments were correctly set for method6
+ assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
+ assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
+ }
+
+ @Test
+ public void testInvokeMethodWithAllArgTypes() throws Exception {
+ mExecutor.invokeMethod(METHOD_7, ARG_STRING_1, ARG_STRING_2);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_7);
+ // assert all arguments were correctly set for method7
+ assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
+ assertEquals("Failed to set second argument", mArgsUsed[1], mMockBuild);
+ assertEquals("Failed to set second argument", mArgsUsed[2], mMockDevice);
+ assertEquals("Failed to set third argument", mArgsUsed[3], ARG_STRING_2);
+ }
+
+ @Test
+ public void testInvokeOverloadedMethodOneArg() throws Exception {
+ mExecutor.invokeMethod(METHOD_1, ARG_STRING_1);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_1);
+ assertEquals("Set wrong number of arguments", mArgsUsed.length, 1);
+ assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
+ }
+
+ @Test
+ public void testInvokeOverloadedMethodTwoArgs() throws Exception {
+ mExecutor.invokeMethod(METHOD_1, ARG_STRING_1, ARG_STRING_2);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_1);
+ assertEquals("Set wrong number of arguments", mArgsUsed.length, 2);
+ assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
+ assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testInvokeNonExistentMethod() throws Exception {
+ mExecutor.invokeMethod(FAKE_METHOD, ARG_STRING_1, ARG_STRING_2);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testInvokeMethodTooManyArgs() throws Exception {
+ mExecutor.invokeMethod(METHOD_3, ARG_STRING_1, ARG_STRING_2);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testInvokeMethodTooFewArgs() throws Exception {
+ mExecutor.invokeMethod(METHOD_2, ARG_STRING_1);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testInvokeMethodIncompatibleArgs() throws Exception {
+ mExecutor.invokeMethod(METHOD_8, ARG_STRING_1);
+ }
+
+ @Test
+ public void testExecuteConditionCheckReturnValue() throws Exception {
+ assertTrue("Wrong return value",
+ mExecutor.executeCondition(METHOD_2, ARG_STRING_1, ARG_STRING_1));
+ assertFalse("Wrong return value",
+ mExecutor.executeCondition(METHOD_2, ARG_STRING_1, ARG_STRING_2));
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testExecuteInvalidCondition() throws Exception {
+ mExecutor.executeCondition(METHOD_1); // method1 does not return type boolean
+ }
+
+ @Test
+ public void testExecuteAction() throws Exception {
+ mExecutor.executeAction(METHOD_2, ARG_STRING_1, ARG_STRING_2);
+ assertEquals("Failed to invoke method in this class", mInvoked, METHOD_2);
+ // assert both String arguments were correctly set for method2
+ assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
+ assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
+ }
+
+ public void method1() {
+ mInvoked = METHOD_1;
+ }
+
+ // overloaded method with one arg
+ public void method1(String arg1) {
+ mInvoked = METHOD_1;
+ mArgsUsed = new Object[]{arg1};
+ }
+
+ // overloaded method with two args
+ public void method1(String arg1, String arg2) {
+ mInvoked = METHOD_1;
+ mArgsUsed = new Object[]{arg1, arg2};
+ }
+
+ public boolean method2(String arg1, String arg2) {
+ mInvoked = METHOD_2;
+ mArgsUsed = new Object[]{arg1, arg2};
+ return arg1.equals(arg2);
+ }
+
+ public void method3(String arg1, ITestDevice arg2) {
+ mInvoked = METHOD_3;
+
+ mArgsUsed = new Object[]{arg1, arg2};
+ }
+
+ // Same as method3, but flipped args
+ public void method4(ITestDevice arg1, String arg2) {
+ mInvoked = METHOD_4;
+ mArgsUsed = new Object[]{arg1, arg2};
+ }
+
+ public void method5(String... args) {
+ mInvoked = METHOD_5;
+ mArgsUsed = args;
+ }
+
+ public void method6(String arg1, String... moreArgs) {
+ mInvoked = METHOD_6;
+ List<String> allArgs = new ArrayList<>();
+ allArgs.add(arg1);
+ allArgs.addAll(Arrays.asList(moreArgs));
+ mArgsUsed = allArgs.toArray(new String[0]);
+ }
+
+ public void method7(String arg1, IBuildInfo arg2, ITestDevice arg3, String... moreArgs) {
+ mInvoked = METHOD_7;
+ List<Object> allArgs = new ArrayList<>();
+ allArgs.add(arg1);
+ allArgs.add(arg2);
+ allArgs.add(arg3);
+ allArgs.addAll(Arrays.asList(moreArgs));
+ mArgsUsed = allArgs.toArray(new Object[0]);
+ }
+
+ public void method8(String arg1, Integer arg2) {
+ // This method should never be successfully invoked, since Integer parameter types are
+ // unsupported for the BusinessLogic service
+ }
+
+ public static class OtherClass {
+
+ public static String otherInvoked = null;
+
+ public void method1() {
+ otherInvoked = OTHER_METHOD_1;
+ }
+ }
+}
diff --git a/common/host-side/util/tests/src/com/android/compatibility/common/util/HostUnitTests.java b/common/host-side/util/tests/src/com/android/compatibility/common/util/HostUnitTests.java
index a706018..32408e4 100644
--- a/common/host-side/util/tests/src/com/android/compatibility/common/util/HostUnitTests.java
+++ b/common/host-side/util/tests/src/com/android/compatibility/common/util/HostUnitTests.java
@@ -15,24 +15,21 @@
*/
package com.android.compatibility.common.util;
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
/**
* A test suite for all host util unit tests.
* <p/>
* All tests listed here should be self-contained, and do not require any external dependencies.
*/
-public class HostUnitTests extends TestSuite {
-
- public HostUnitTests() {
- super();
- addTestSuite(DynamicConfigHandlerTest.class);
- addTestSuite(ModuleResultTest.class);
- addTestSuite(TestFilterTest.class);
- }
-
- public static Test suite() {
- return new HostUnitTests();
- }
-}
\ No newline at end of file
+@RunWith(Suite.class)
+@SuiteClasses({
+ BusinessLogicHostExecutorTest.class,
+ DynamicConfigHandlerTest.class,
+ ModuleResultTest.class,
+ TestFilterTest.class,
+})
+public class HostUnitTests {
+ // empty on purpose
+}
diff --git a/common/util/Android.mk b/common/util/Android.mk
index 20bef9d..d5c5f5a 100644
--- a/common/util/Android.mk
+++ b/common/util/Android.mk
@@ -45,6 +45,7 @@
LOCAL_MODULE := compatibility-common-util-hostsidelib
LOCAL_STATIC_JAVA_LIBRARIES := guavalib \
+ json-prebuilt \
junit-host \
kxml2-2.3.0 \
platform-test-annotations-host
diff --git a/common/util/src/com/android/compatibility/common/util/BusinessLogic.java b/common/util/src/com/android/compatibility/common/util/BusinessLogic.java
new file mode 100644
index 0000000..4b5c5af
--- /dev/null
+++ b/common/util/src/com/android/compatibility/common/util/BusinessLogic.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2017 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.compatibility.common.util;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper and constants accessible to host and device components that enable Business Logic
+ * configuration
+ */
+public class BusinessLogic {
+
+ // Device location to which business logic data is pushed
+ public static final String DEVICE_FILE = "/sdcard/bl";
+
+ /* A map from testcase name to the business logic rules for the test case */
+ protected Map<String, List<BusinessLogicRule>> mRules;
+
+ /**
+ * Determines whether business logic exists for a given test name
+ * @param testName the name of the test case, prefixed by fully qualified class name, then '#'.
+ * For example, "com.android.foo.FooTest#testFoo"
+ * @return whether business logic exists for this test for this suite
+ */
+ public boolean hasLogicFor(String testName) {
+ List<BusinessLogicRule> rules = mRules.get(testName);
+ return rules != null && !rules.isEmpty();
+ }
+
+ /**
+ * Apply business logic for the given test.
+ * @param testName the name of the test case, prefixed by fully qualified class name, then '#'.
+ * For example, "com.android.foo.FooTest#testFoo"
+ * @param executor a {@link BusinessLogicExecutor}
+ */
+ public void applyLogicFor(String testName, BusinessLogicExecutor executor) {
+ List<BusinessLogicRule> rules = mRules.get(testName);
+ if (rules == null || rules.isEmpty()) {
+ return;
+ }
+ for (BusinessLogicRule rule : rules) {
+ // Check conditions
+ if (rule.invokeConditions(executor)) {
+ rule.invokeActions(executor);
+ }
+ }
+ }
+
+ /**
+ * Nested class representing an Business Logic Rule. Stores a collection of conditions
+ * and actions for later invokation.
+ */
+ protected static class BusinessLogicRule {
+
+ /* Stored conditions and actions */
+ protected List<BusinessLogicRuleCondition> mConditions;
+ protected List<BusinessLogicRuleAction> mActions;
+
+ public BusinessLogicRule(List<BusinessLogicRuleCondition> conditions,
+ List<BusinessLogicRuleAction> actions) {
+ mConditions = conditions;
+ mActions = actions;
+ }
+
+ /**
+ * Method that invokes all Business Logic conditions for this rule, and returns true
+ * if all conditions evaluate to true.
+ */
+ public boolean invokeConditions(BusinessLogicExecutor executor) {
+ for (BusinessLogicRuleCondition condition : mConditions) {
+ if (!condition.invoke(executor)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Method that invokes all Business Logic actions for this rule
+ */
+ public void invokeActions(BusinessLogicExecutor executor) {
+ for (BusinessLogicRuleAction action : mActions) {
+ action.invoke(executor);
+ }
+ }
+ }
+
+ /**
+ * Nested class representing an Business Logic Rule Condition. Stores the name of a method
+ * to invoke, as well as String args to use during invokation.
+ */
+ protected static class BusinessLogicRuleCondition {
+
+ /* Stored method name and String args */
+ protected String mMethodName;
+ protected List<String> mMethodArgs;
+
+ public BusinessLogicRuleCondition(String methodName, List<String> methodArgs) {
+ mMethodName = methodName;
+ mMethodArgs = methodArgs;
+ }
+
+ /**
+ * Invoke this Business Logic condition with an executor.
+ */
+ public boolean invoke(BusinessLogicExecutor executor) {
+ return executor.executeCondition(mMethodName,
+ mMethodArgs.toArray(new String[mMethodArgs.size()]));
+ }
+ }
+
+ /**
+ * Nested class representing an Business Logic Rule Action. Stores the name of a method
+ * to invoke, as well as String args to use during invokation.
+ */
+ protected static class BusinessLogicRuleAction {
+
+ /* Stored method name and String args */
+ protected String mMethodName;
+ protected List<String> mMethodArgs;
+
+ public BusinessLogicRuleAction(String methodName, List<String> methodArgs) {
+ mMethodName = methodName;
+ mMethodArgs = methodArgs;
+ }
+
+ /**
+ * Invoke this Business Logic condition with an executor.
+ */
+ public void invoke(BusinessLogicExecutor executor) {
+ executor.executeAction(mMethodName,
+ mMethodArgs.toArray(new String[mMethodArgs.size()]));
+ }
+ }
+}
diff --git a/common/util/src/com/android/compatibility/common/util/BusinessLogicExecutor.java b/common/util/src/com/android/compatibility/common/util/BusinessLogicExecutor.java
new file mode 100644
index 0000000..e189208
--- /dev/null
+++ b/common/util/src/com/android/compatibility/common/util/BusinessLogicExecutor.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2017 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.compatibility.common.util;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Resolves methods provided by the BusinessLogicService and invokes them
+ */
+public abstract class BusinessLogicExecutor {
+
+ /** String representations of the String class and String[] class */
+ protected static final String STRING_CLASS = "java.lang.String";
+ protected static final String STRING_ARRAY_CLASS = "[Ljava.lang.String;";
+
+ /**
+ * Execute a business logic condition.
+ * @param method the name of the method to invoke. Must include fully qualified name of the
+ * enclosing class, followed by '.', followed by the name of the method
+ * @param args the string arguments to supply to the method
+ * @return the return value of the method invoked
+ * @throws RuntimeException when failing to resolve or invoke the condition method
+ */
+ public boolean executeCondition(String method, String... args) {
+ try {
+ return (Boolean) invokeMethod(method, args);
+ } catch (ClassNotFoundException | IllegalAccessException | InstantiationException |
+ InvocationTargetException | NoSuchMethodException e) {
+ throw new RuntimeException(String.format(
+ "BusinessLogic: Failed to invoke condition method %s with args: %s", method,
+ Arrays.toString(args)), e);
+ }
+ }
+
+ /**
+ * Execute a business logic action.
+ * @param method the name of the method to invoke. Must include fully qualified name of the
+ * enclosing class, followed by '.', followed by the name of the method
+ * @param args the string arguments to supply to the method
+ * @throws RuntimeException when failing to resolve or invoke the action method
+ */
+ public void executeAction(String method, String... args) {
+ try {
+ invokeMethod(method, args);
+ } catch (ClassNotFoundException | IllegalAccessException | InstantiationException |
+ InvocationTargetException | NoSuchMethodException e) {
+ throw new RuntimeException(String.format(
+ "BusinessLogic: Failed to invoke action method %s with args: %s", method,
+ Arrays.toString(args)), e);
+ }
+ }
+
+ /**
+ * Execute a business logic method.
+ * @param method the name of the method to invoke. Must include fully qualified name of the
+ * enclosing class, followed by '.', followed by the name of the method
+ * @param args the string arguments to supply to the method
+ * @return the return value of the method invoked (type Boolean if method is a condition)
+ * @throws RuntimeException when failing to resolve or invoke the method
+ */
+ protected Object invokeMethod(String method, String... args) throws ClassNotFoundException,
+ IllegalAccessException, InstantiationException, InvocationTargetException,
+ NoSuchMethodException {
+ // Method names served by the BusinessLogic service should assume format
+ // classname.methodName, but also handle format classname#methodName since test names use
+ // this format
+ int index = (method.indexOf('#') == -1) ? method.lastIndexOf('.') : method.indexOf('#');
+ if (index == -1) {
+ throw new RuntimeException(String.format("BusinessLogic: invalid method name "
+ + "\"%s\". Method string must include fully qualified class name. "
+ + "For example, \"com.android.packagename.ClassName.methodName\".", method));
+ }
+ String className = method.substring(0, index);
+ Class cls = Class.forName(className);
+ Object obj = cls.getDeclaredConstructor().newInstance();
+ if (getTestObject() != null && cls.isAssignableFrom(getTestObject().getClass())) {
+ // The given method is a member of the test class, use the known test class instance
+ obj = getTestObject();
+ }
+ ResolvedMethod rm = getResolvedMethod(cls, method.substring(index + 1), args);
+ return rm.invoke(obj);
+ }
+
+ /**
+ * Get the test object. This method is left abstract, since non-abstract subclasses will set
+ * the test object in the constructor.
+ * @return the test case instance
+ */
+ protected abstract Object getTestObject();
+
+ /**
+ * Get the method and list of arguments corresponding to the class, method name, and proposed
+ * argument values, in the form of a {@link ResolvedMethod} object. This object stores all
+ * information required to successfully invoke the method. getResolvedMethod is left abstract,
+ * since argument types differ between device-side (e.g. Context) and host-side
+ * (e.g. ITestDevice) implementations of this class.
+ * @param cls the Class to which the method belongs
+ * @param methodName the name of the method to invoke
+ * @param args the string arguments to use when invoking the method
+ * @return a {@link ResolvedMethod}
+ * @throws ClassNotFoundException
+ */
+ protected abstract ResolvedMethod getResolvedMethod(Class cls, String methodName,
+ String... args) throws ClassNotFoundException;
+
+ /**
+ * Retrieve all methods within a class that match a given name
+ * @param cls the class
+ * @param name the method name
+ * @return a list of method objects
+ */
+ protected List<Method> getMethodsWithName(Class cls, String name) {
+ List<Method> methodList = new ArrayList<>();
+ for (Method m : cls.getMethods()) {
+ if (name.equals(m.getName())) {
+ methodList.add(m);
+ }
+ }
+ return methodList;
+ }
+
+ /**
+ * Helper class for storing a method object, and a list of arguments to use when invoking the
+ * method. The class is also equipped with an "invoke" method for convenience.
+ */
+ protected static class ResolvedMethod {
+ private Method mMethod;
+ List<Object> mArgs;
+
+ public ResolvedMethod(Method method) {
+ mMethod = method;
+ mArgs = new ArrayList<>();
+ }
+
+ /** Add an argument to the argument list for this instance */
+ public void addArg(Object arg) {
+ mArgs.add(arg);
+ }
+
+ /** Invoke the stored method with the stored args on a given object */
+ public Object invoke(Object instance) throws IllegalAccessException,
+ InvocationTargetException {
+ return mMethod.invoke(instance, mArgs.toArray());
+ }
+ }
+}
diff --git a/common/util/src/com/android/compatibility/common/util/BusinessLogicFactory.java b/common/util/src/com/android/compatibility/common/util/BusinessLogicFactory.java
new file mode 100644
index 0000000..b21a4ad
--- /dev/null
+++ b/common/util/src/com/android/compatibility/common/util/BusinessLogicFactory.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2017 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.compatibility.common.util;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.android.compatibility.common.util.BusinessLogic.BusinessLogicRule;
+import com.android.compatibility.common.util.BusinessLogic.BusinessLogicRuleAction;
+import com.android.compatibility.common.util.BusinessLogic.BusinessLogicRuleCondition;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+
+/**
+ * Factory for creating a {@link BusinessLogic}
+ */
+public class BusinessLogicFactory {
+
+ // Name of list object storing test-rules pairs
+ private static final String BUSINESS_LOGIC_RULES_LISTS = "businessLogicRulesLists";
+ // Name of test name string
+ private static final String TEST_NAME = "testName";
+ // Name of rules object (one 'rules' object to a single test)
+ private static final String BUSINESS_LOGIC_RULES = "businessLogicRules";
+ // Name of rule conditions array
+ private static final String RULE_CONDITIONS = "ruleConditions";
+ // Name of rule actions array
+ private static final String RULE_ACTIONS = "ruleActions";
+ // Name of method name string
+ private static final String METHOD_NAME = "methodName";
+ // Name of method args array of strings
+ private static final String METHOD_ARGS = "methodArgs";
+
+ /**
+ * Create a BusinessLogic instance from a file of business logic data, formatted in JSON.
+ * This format is identical to that which is received from the Android Partner business logic
+ * service.
+ */
+ public static BusinessLogic createFromFile(File f) {
+ // Populate the map from testname to business rules for this new BusinessLogic instance
+ Map<String, List<BusinessLogicRule>> rulesMap = new HashMap<>();
+ BusinessLogic bl = new BusinessLogic();
+ try {
+ String businessLogicString = readFile(f);
+ JSONObject root = new JSONObject(businessLogicString);
+ JSONArray rulesLists = null;
+ try {
+ rulesLists = root.getJSONArray(BUSINESS_LOGIC_RULES_LISTS);
+ } catch (JSONException e) {
+ bl.mRules = rulesMap;
+ return bl; // no rules defined for this suite, leave internal map empty
+ }
+ for (int i = 0; i < rulesLists.length(); i++) {
+ JSONObject rulesList = rulesLists.getJSONObject(i);
+ String testName = rulesList.getString(TEST_NAME);
+ List<BusinessLogicRule> rules = new ArrayList<>();
+ JSONArray rulesJSONArray = null;
+ try {
+ rulesJSONArray = rulesList.getJSONArray(BUSINESS_LOGIC_RULES);
+ } catch (JSONException e) {
+ // no rules defined for this test case
+ rulesMap.put(testName, rules); // add empty rule list to internal map
+ continue; // advance to next test case
+ }
+ for (int j = 0; j < rulesJSONArray.length(); j++) {
+ JSONObject ruleJSONObject = rulesJSONArray.getJSONObject(j);
+ // Build conditions list
+ List<BusinessLogicRuleCondition> ruleConditions =
+ extractRuleConditionList(ruleJSONObject);
+ // Build actions list
+ List<BusinessLogicRuleAction> ruleActions =
+ extractRuleActionList(ruleJSONObject);
+ rules.add(new BusinessLogicRule(ruleConditions, ruleActions));
+ }
+ rulesMap.put(testName, rules);
+ }
+ } catch (IOException | JSONException e) {
+ throw new RuntimeException("Business Logic failed", e);
+ }
+ // Return business logic
+ bl.mRules = rulesMap;
+ return bl;
+ }
+
+ /* Extract all BusinessLogicRuleConditions from a JSON business logic rule */
+ private static List<BusinessLogicRuleCondition> extractRuleConditionList(
+ JSONObject ruleJSONObject) throws JSONException {
+ List<BusinessLogicRuleCondition> ruleConditions = new ArrayList<>();
+ // All rules require at least one condition, line below throws JSONException if not
+ JSONArray ruleConditionsJSONArray = ruleJSONObject.getJSONArray(RULE_CONDITIONS);
+ for (int i = 0; i < ruleConditionsJSONArray.length(); i++) {
+ JSONObject ruleConditionJSONObject = ruleConditionsJSONArray.getJSONObject(i);
+ String methodName = ruleConditionJSONObject.getString(METHOD_NAME);
+ // Each condition requires at least one arg, line below throws JSONException if not
+ JSONArray methodArgsJSONArray = ruleConditionJSONObject.getJSONArray(METHOD_ARGS);
+ List<String> methodArgs = new ArrayList<>();
+ for (int j = 0; j < methodArgsJSONArray.length(); j++) {
+ methodArgs.add(methodArgsJSONArray.getString(j));
+ }
+ ruleConditions.add(new BusinessLogicRuleCondition(methodName, methodArgs));
+ }
+ return ruleConditions;
+ }
+
+ /* Extract all BusinessLogicRuleActions from a JSON business logic rule */
+ private static List<BusinessLogicRuleAction> extractRuleActionList(JSONObject ruleJSONObject)
+ throws JSONException {
+ List<BusinessLogicRuleAction> ruleActions = new ArrayList<>();
+ // All rules require at least one action, line below throws JSONException if not
+ JSONArray ruleActionsJSONArray = ruleJSONObject.getJSONArray(RULE_ACTIONS);
+ for (int i = 0; i < ruleActionsJSONArray.length(); i++) {
+ JSONObject ruleActionJSONObject = ruleActionsJSONArray.getJSONObject(i);
+ String methodName = ruleActionJSONObject.getString(METHOD_NAME);
+ List<String> methodArgs = new ArrayList<>();
+ JSONArray methodArgsJSONArray = null;
+ try {
+ methodArgsJSONArray = ruleActionJSONObject.getJSONArray(METHOD_ARGS);
+ } catch (JSONException e) {
+ // No method args for this rule action, add rule action with empty args list
+ ruleActions.add(new BusinessLogicRuleAction(methodName, methodArgs));
+ continue;
+ }
+ for (int j = 0; j < methodArgsJSONArray.length(); j++) {
+ methodArgs.add(methodArgsJSONArray.getString(j));
+ }
+ ruleActions.add(new BusinessLogicRuleAction(methodName, methodArgs));
+ }
+ return ruleActions;
+ }
+
+ /* Extract string from file */
+ private static String readFile(File f) throws IOException {
+ StringBuilder sb = new StringBuilder((int) f.length());
+ String lineSeparator = System.getProperty("line.separator");
+ try (Scanner scanner = new Scanner(f)) {
+ while(scanner.hasNextLine()) {
+ sb.append(scanner.nextLine() + lineSeparator);
+ }
+ return sb.toString();
+ }
+ }
+}
diff --git a/common/util/tests/src/com/android/compatibility/common/util/BusinessLogicTest.java b/common/util/tests/src/com/android/compatibility/common/util/BusinessLogicTest.java
new file mode 100644
index 0000000..47ce5da
--- /dev/null
+++ b/common/util/tests/src/com/android/compatibility/common/util/BusinessLogicTest.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2017 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.compatibility.common.util;
+
+import static junit.framework.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import com.android.compatibility.common.util.BusinessLogic.BusinessLogicRule;
+import com.android.compatibility.common.util.BusinessLogic.BusinessLogicRuleAction;
+import com.android.compatibility.common.util.BusinessLogic.BusinessLogicRuleCondition;
+import com.android.tradefed.util.FileUtil;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Unit tests for {@link BusinessLogic}
+ */
+@RunWith(JUnit4.class)
+public class BusinessLogicTest {
+
+ private static final String CORRECT_LOGIC =
+ "{\n" +
+ " \"name\": \"businessLogic/suites/gts\",\n" +
+ " \"businessLogicRulesLists\": [\n" +
+ " {\n" +
+ " \"testName\": \"testCaseName1\",\n" +
+ " \"businessLogicRules\": [\n" +
+ " {\n" +
+ " \"ruleConditions\": [\n" +
+ " {\n" +
+ " \"methodName\": \"conditionMethodName1\",\n" +
+ " \"methodArgs\": [\n" +
+ " \"arg1\"\n" +
+ " ]\n" +
+ " }\n" +
+ " ],\n" +
+ " \"ruleActions\": [\n" +
+ " {\n" +
+ " \"methodName\": \"actionMethodName1\",\n" +
+ " \"methodArgs\": [\n" +
+ " \"arg1\",\n" +
+ " \"arg2\"\n" +
+ " ]\n" +
+ " }\n" +
+ " ]\n" +
+ " }\n" +
+ " ]\n" +
+ " },\n" +
+ " {\n" +
+ " \"testName\": \"testCaseName2\",\n" +
+ " \"businessLogicRules\": [\n" +
+ " {\n" +
+ " \"ruleConditions\": [\n" +
+ " {\n" +
+ " \"methodName\": \"conditionMethodName1\",\n" +
+ " \"methodArgs\": [\n" +
+ " \"arg1\"\n" +
+ " ]\n" +
+ " }\n" +
+ " ],\n" +
+ " \"ruleActions\": [\n" +
+ " {\n" +
+ " \"methodName\": \"actionMethodName1\",\n" +
+ " \"methodArgs\": [\n" +
+ " \"arg1\",\n" +
+ " \"arg2\"\n" +
+ " ]\n" +
+ " }\n" +
+ " ]\n" +
+ " },\n" +
+ " {\n" +
+ " \"ruleConditions\": [\n" +
+ " {\n" +
+ " \"methodName\": \"conditionMethodName1\",\n" +
+ " \"methodArgs\": [\n" +
+ " \"arg1\"\n" +
+ " ]\n" +
+ " },\n" +
+ " {\n" +
+ " \"methodName\": \"conditionMethodName2\",\n" +
+ " \"methodArgs\": [\n" +
+ " \"arg2\"\n" +
+ " ]\n" +
+ " }\n" +
+ " ],\n" +
+ " \"ruleActions\": [\n" +
+ " {\n" +
+ " \"methodName\": \"actionMethodName1\",\n" +
+ " \"methodArgs\": [\n" +
+ " \"arg1\",\n" +
+ " \"arg2\"\n" +
+ " ]\n" +
+ " },\n" +
+ " {\n" +
+ " \"methodName\": \"actionMethodName2\"\n" +
+ " }\n" +
+ " ]\n" +
+ " }\n" +
+ " ]\n" +
+ " },\n" +
+ " {\n" +
+ " \"testName\": \"testCaseName3\"\n" +
+ " }\n" +
+ " ]\n" +
+ "}";
+
+ @Test
+ public void testCorrectLogic() throws Exception {
+ File file = createFileFromStr(CORRECT_LOGIC);
+ try {
+ BusinessLogic bl = BusinessLogicFactory.createFromFile(file);
+ assertEquals("Wrong number of business logic rule lists", 3, bl.mRules.size());
+
+ List<BusinessLogicRule> ruleList1 = bl.mRules.get("testCaseName1");
+ assertEquals("Wrong number of rules in first rule list", 1, ruleList1.size());
+ BusinessLogicRule rule1 = ruleList1.get(0);
+ List<BusinessLogicRuleCondition> rule1Conditions = rule1.mConditions;
+ assertEquals("Wrong number of conditions", 1, rule1Conditions.size());
+ BusinessLogicRuleCondition rule1Condition = rule1Conditions.get(0);
+ assertEquals("Wrong method name for business logic rule condition",
+ "conditionMethodName1", rule1Condition.mMethodName);
+ assertEquals("Wrong arg string count for business logic rule condition", 1,
+ rule1Condition.mMethodArgs.size());
+ assertEquals("Wrong arg for business logic rule condition", "arg1",
+ rule1Condition.mMethodArgs.get(0));
+ List<BusinessLogicRuleAction> rule1Actions = rule1.mActions;
+ assertEquals("Wrong number of actions", 1, rule1Actions.size());
+ BusinessLogicRuleAction rule1Action = rule1Actions.get(0);
+ assertEquals("Wrong method name for business logic rule action",
+ "actionMethodName1", rule1Action.mMethodName);
+ assertEquals("Wrong arg string count for business logic rule action", 2,
+ rule1Action.mMethodArgs.size());
+ assertEquals("Wrong arg for business logic rule action", "arg1",
+ rule1Action.mMethodArgs.get(0));
+ assertEquals("Wrong arg for business logic rule action", "arg2",
+ rule1Action.mMethodArgs.get(1));
+
+ List<BusinessLogicRule> ruleList2 = bl.mRules.get("testCaseName2");
+ assertEquals("Wrong number of rules in second rule list", 2, ruleList2.size());
+ BusinessLogicRule rule2 = ruleList2.get(0);
+ List<BusinessLogicRuleCondition> rule2Conditions = rule2.mConditions;
+ assertEquals("Wrong number of conditions", 1, rule2Conditions.size());
+ BusinessLogicRuleCondition rule2Condition = rule2Conditions.get(0);
+ assertEquals("Wrong method name for business logic rule condition",
+ "conditionMethodName1", rule2Condition.mMethodName);
+ assertEquals("Wrong arg string count for business logic rule condition", 1,
+ rule2Condition.mMethodArgs.size());
+ assertEquals("Wrong arg for business logic rule condition", "arg1",
+ rule2Condition.mMethodArgs.get(0));
+ List<BusinessLogicRuleAction> rule2Actions = rule2.mActions;
+ assertEquals("Wrong number of actions", 1, rule2Actions.size());
+ BusinessLogicRuleAction rule2Action = rule2Actions.get(0);
+ assertEquals("Wrong method name for business logic rule action",
+ "actionMethodName1", rule2Action.mMethodName);
+ assertEquals("Wrong arg string count for business logic rule action", 2,
+ rule2Action.mMethodArgs.size());
+ assertEquals("Wrong arg for business logic rule action", "arg1",
+ rule2Action.mMethodArgs.get(0));
+ assertEquals("Wrong arg for business logic rule action", "arg2",
+ rule2Action.mMethodArgs.get(1));
+ BusinessLogicRule rule3 = ruleList2.get(1);
+ List<BusinessLogicRuleCondition> rule3Conditions = rule3.mConditions;
+ assertEquals("Wrong number of conditions", 2, rule3Conditions.size());
+ BusinessLogicRuleCondition rule3Condition1 = rule3Conditions.get(0);
+ assertEquals("Wrong method name for business logic rule condition",
+ "conditionMethodName1", rule3Condition1.mMethodName);
+ assertEquals("Wrong arg string count for business logic rule condition", 1,
+ rule3Condition1.mMethodArgs.size());
+ assertEquals("Wrong arg for business logic rule condition", "arg1",
+ rule3Condition1.mMethodArgs.get(0));
+ BusinessLogicRuleCondition rule3Condition2 = rule3Conditions.get(1);
+ assertEquals("Wrong method name for business logic rule condition",
+ "conditionMethodName2", rule3Condition2.mMethodName);
+ assertEquals("Wrong arg string count for business logic rule condition", 1,
+ rule3Condition2.mMethodArgs.size());
+ assertEquals("Wrong arg for business logic rule condition", "arg2",
+ rule3Condition2.mMethodArgs.get(0));
+ List<BusinessLogicRuleAction> rule3Actions = rule3.mActions;
+ assertEquals("Wrong number of actions", 2, rule3Actions.size());
+ BusinessLogicRuleAction rule3Action1 = rule3Actions.get(0);
+ assertEquals("Wrong method name for business logic rule action",
+ "actionMethodName1", rule3Action1.mMethodName);
+ assertEquals("Wrong arg string count for business logic rule action", 2,
+ rule3Action1.mMethodArgs.size());
+ assertEquals("Wrong arg for business logic rule action", "arg1",
+ rule3Action1.mMethodArgs.get(0));
+ assertEquals("Wrong arg for business logic rule action", "arg2",
+ rule3Action1.mMethodArgs.get(1));
+ BusinessLogicRuleAction rule3Action2 = rule3Actions.get(1);
+ assertEquals("Wrong method name for business logic rule action",
+ "actionMethodName2", rule3Action2.mMethodName);
+ assertEquals("Wrong arg string count for business logic rule action", 0,
+ rule3Action2.mMethodArgs.size());
+
+ List<BusinessLogicRule> ruleList3 = bl.mRules.get("testCaseName3");
+ assertEquals("Wrong number of rules in third rule list", 0, ruleList3.size());
+ } finally {
+ FileUtil.deleteFile(file);
+ }
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testLogicWithWrongNodeName() throws Exception {
+ File file = createFileFromStr(CORRECT_LOGIC.replace("testName", "testNam3"));
+ try {
+ BusinessLogic bl = BusinessLogicFactory.createFromFile(file);
+ } finally {
+ FileUtil.deleteFile(file);
+ }
+ }
+
+ private static File createFileFromStr(String blString) throws IOException {
+ File file = File.createTempFile("test", "bl");
+ FileOutputStream stream = new FileOutputStream(file);
+ stream.write(blString.getBytes());
+ stream.flush();
+ stream.close();
+ return file;
+ }
+}
diff --git a/common/util/tests/src/com/android/compatibility/common/util/UnitTests.java b/common/util/tests/src/com/android/compatibility/common/util/UnitTests.java
index 9f8235c..6333342 100644
--- a/common/util/tests/src/com/android/compatibility/common/util/UnitTests.java
+++ b/common/util/tests/src/com/android/compatibility/common/util/UnitTests.java
@@ -15,30 +15,28 @@
*/
package com.android.compatibility.common.util;
-import junit.framework.Test;
-import junit.framework.TestSuite;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
/**
* A test suite for all util unit tests.
* <p/>
* All tests listed here should be self-contained, and do not require any external dependencies.
*/
-public class UnitTests extends TestSuite {
-
- public UnitTests() {
- super();
- addTestSuite(CaseResultTest.class);
- addTestSuite(DynamicConfigTest.class);
- addTestSuite(LightInvocationResultTest.class);
- addTestSuite(MetricsXmlSerializerTest.class);
- addTestSuite(MultipartFormTest.class);
- addTestSuite(ReportLogTest.class);
- addTestSuite(ResultHandlerTest.class);
- addTestSuite(StatTest.class);
- addTestSuite(TestResultTest.class);
- }
-
- public static Test suite() {
- return new UnitTests();
- }
+@RunWith(Suite.class)
+@SuiteClasses({
+ BusinessLogicTest.class,
+ CaseResultTest.class,
+ DynamicConfigTest.class,
+ LightInvocationResultTest.class,
+ MetricsXmlSerializerTest.class,
+ MultipartFormTest.class,
+ ReportLogTest.class,
+ ResultHandlerTest.class,
+ StatTest.class,
+ TestResultTest.class,
+})
+public class UnitTests {
+ // empty on purpose
}