Merge "[view_compiler] Add end-to-end DexBuilder tests"
am: 60b722a005
Change-Id: I86c5f92c4ec53919a96540f4568e1b602b26c350
This commit is contained in:
@@ -62,3 +62,22 @@ cc_test_host {
|
||||
],
|
||||
test_suites: ["general-tests"],
|
||||
}
|
||||
|
||||
cc_binary_host {
|
||||
name: "dex_testcase_generator",
|
||||
defaults: ["viewcompiler_defaults"],
|
||||
srcs: ["dex_testcase_generator.cc"],
|
||||
static_libs: [
|
||||
"libviewcompiler",
|
||||
],
|
||||
}
|
||||
|
||||
genrule {
|
||||
name: "generate_dex_testcases",
|
||||
tools: [":dex_testcase_generator"],
|
||||
cmd: "$(location :dex_testcase_generator) $(genDir)",
|
||||
out: [
|
||||
"simple.dex",
|
||||
"trivial.dex",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -23,3 +23,31 @@ This tool is still in its early stages and has a number of limitations.
|
||||
application.
|
||||
* This only works for apps that do not use a custom layout inflater.
|
||||
* Other limitations yet to be discovered.
|
||||
|
||||
## DexBuilder Tests
|
||||
|
||||
The DexBuilder has several low-level end to end tests to verify generated DEX
|
||||
code validates, runs, and has the correct behavior. There are, unfortunately, a
|
||||
number of pieces that must be added to generate new tests. Here are the
|
||||
components:
|
||||
|
||||
* `dex_testcase_generator` - Written in C++ using `DexBuilder`. This runs as a
|
||||
build step produce the DEX files that will be tested on device. See the
|
||||
`genrule` named `generate_dex_testcases` in `Android.bp`. These files are then
|
||||
copied over to the device by TradeFed when running tests.
|
||||
* `DexBuilderTest` - This is a Java Language test harness that loads the
|
||||
generated DEX files and exercises methods in the file.
|
||||
|
||||
To add a new DEX file test, follow these steps:
|
||||
1. Modify `dex_testcase_generator` to produce the DEX file.
|
||||
2. Add the filename to the `out` list of the `generate_dex_testcases` rule in
|
||||
`Android.bp`.
|
||||
3. Add a new `push` option to `AndroidTest.xml` to copy the DEX file to the
|
||||
device.
|
||||
4. Modify `DexBuilderTest.java` to load and exercise the new test.
|
||||
|
||||
In each case, you should be able to cargo-cult the existing test cases.
|
||||
|
||||
In general, you can probably get by without adding a new generated DEX file, and
|
||||
instead add more methods to the files that are already generated. In this case,
|
||||
you can skip all of steps 2 and 3 above, and simplify steps 1 and 4.
|
||||
|
||||
7
startop/view_compiler/TEST_MAPPING
Normal file
7
startop/view_compiler/TEST_MAPPING
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"presubmit": [
|
||||
{
|
||||
"name": "dex-builder-test"
|
||||
}
|
||||
]
|
||||
}
|
||||
29
startop/view_compiler/dex_builder_test/Android.bp
Normal file
29
startop/view_compiler/dex_builder_test/Android.bp
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Copyright (C) 2018 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.
|
||||
//
|
||||
|
||||
android_test {
|
||||
name: "dex-builder-test",
|
||||
srcs: ["src/android/startop/test/DexBuilderTest.java"],
|
||||
sdk_version: "current",
|
||||
data: [":generate_dex_testcases"],
|
||||
static_libs: [
|
||||
"android-support-test",
|
||||
"guava",
|
||||
],
|
||||
manifest: "AndroidManifest.xml",
|
||||
test_config: "AndroidTest.xml",
|
||||
test_suites: ["general-tests"],
|
||||
}
|
||||
29
startop/view_compiler/dex_builder_test/AndroidManifest.xml
Normal file
29
startop/view_compiler/dex_builder_test/AndroidManifest.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2018 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.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="android.startop.test" >
|
||||
|
||||
<uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
|
||||
android:targetPackage="android.startop.test"
|
||||
android:label="DexBuilder Tests"/>
|
||||
|
||||
</manifest>
|
||||
34
startop/view_compiler/dex_builder_test/AndroidTest.xml
Normal file
34
startop/view_compiler/dex_builder_test/AndroidTest.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2018 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.
|
||||
-->
|
||||
<configuration description="Runs DexBuilder Tests.">
|
||||
<option name="test-suite-tag" value="apct" />
|
||||
<option name="test-suite-tag" value="apct-instrumentation" />
|
||||
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
|
||||
<option name="cleanup-apks" value="true" />
|
||||
<option name="test-file-name" value="dex-builder-test.apk" />
|
||||
</target_preparer>
|
||||
|
||||
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
|
||||
<option name="cleanup" value="true" />
|
||||
<option name="push" value="trivial.dex->/data/local/tmp/dex-builder-test/trivial.dex" />
|
||||
<option name="push" value="simple.dex->/data/local/tmp/dex-builder-test/simple.dex" />
|
||||
</target_preparer>
|
||||
|
||||
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
|
||||
<option name="package" value="android.startop.test" />
|
||||
<option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
|
||||
</test>
|
||||
</configuration>
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 android.startop.test;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import dalvik.system.InMemoryDexClassLoader;
|
||||
import dalvik.system.PathClassLoader;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteBuffer;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
// Adding tests here requires changes in several other places. See README.md in
|
||||
// the view_compiler directory for more information.
|
||||
public class DexBuilderTest {
|
||||
static ClassLoader loadDexFile(String filename) throws Exception {
|
||||
return new PathClassLoader("/data/local/tmp/dex-builder-test/" + filename,
|
||||
ClassLoader.getSystemClassLoader());
|
||||
}
|
||||
|
||||
public void hello() {}
|
||||
|
||||
@Test
|
||||
public void loadTrivialDex() throws Exception {
|
||||
ClassLoader loader = loadDexFile("trivial.dex");
|
||||
loader.loadClass("android.startop.test.testcases.Trivial");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void return5() throws Exception {
|
||||
ClassLoader loader = loadDexFile("simple.dex");
|
||||
Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
|
||||
Method method = clazz.getMethod("return5");
|
||||
Assert.assertEquals(5, method.invoke(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnParam() throws Exception {
|
||||
ClassLoader loader = loadDexFile("simple.dex");
|
||||
Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
|
||||
Method method = clazz.getMethod("returnParam", int.class);
|
||||
Assert.assertEquals(5, method.invoke(null, 5));
|
||||
Assert.assertEquals(42, method.invoke(null, 42));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnStringLength() throws Exception {
|
||||
ClassLoader loader = loadDexFile("simple.dex");
|
||||
Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests");
|
||||
Method method = clazz.getMethod("returnStringLength", String.class);
|
||||
Assert.assertEquals(13, method.invoke(null, "Hello, World!"));
|
||||
}
|
||||
}
|
||||
85
startop/view_compiler/dex_testcase_generator.cc
Normal file
85
startop/view_compiler/dex_testcase_generator.cc
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
#include "android-base/logging.h"
|
||||
#include "dex_builder.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
// Adding tests here requires changes in several other places. See README.md in
|
||||
// the view_compiler directory for more information.
|
||||
|
||||
using namespace startop::dex;
|
||||
using namespace std;
|
||||
|
||||
void GenerateTrivialDexFile(const string& outdir) {
|
||||
DexBuilder dex_file;
|
||||
|
||||
ClassBuilder cbuilder{dex_file.MakeClass("android.startop.test.testcases.Trivial")};
|
||||
cbuilder.set_source_file("dex_testcase_generator.cc#GenerateTrivialDexFile");
|
||||
|
||||
slicer::MemView image{dex_file.CreateImage()};
|
||||
std::ofstream out_file(outdir + "/trivial.dex");
|
||||
out_file.write(image.ptr<const char>(), image.size());
|
||||
}
|
||||
|
||||
// Generates test cases that test around 1 instruction.
|
||||
void GenerateSimpleTestCases(const string& outdir) {
|
||||
DexBuilder dex_file;
|
||||
|
||||
ClassBuilder cbuilder{dex_file.MakeClass("android.startop.test.testcases.SimpleTests")};
|
||||
cbuilder.set_source_file("dex_testcase_generator.cc#GenerateSimpleTestCases");
|
||||
|
||||
// int return5() { return 5; }
|
||||
auto return5{cbuilder.CreateMethod("return5", Prototype{TypeDescriptor::Int()})};
|
||||
Value r{return5.MakeRegister()};
|
||||
return5.BuildConst4(r, 5);
|
||||
return5.BuildReturn(r);
|
||||
return5.Encode();
|
||||
|
||||
// // int returnParam(int x) { return x; }
|
||||
auto returnParam{cbuilder.CreateMethod("returnParam",
|
||||
Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})};
|
||||
returnParam.BuildReturn(Value::Parameter(0));
|
||||
returnParam.Encode();
|
||||
|
||||
// int returnStringLength(String x) { return x.length(); }
|
||||
auto string_type{TypeDescriptor::FromClassname("java.lang.String")};
|
||||
MethodDeclData string_length{
|
||||
dex_file.GetOrDeclareMethod(string_type, "length", Prototype{TypeDescriptor::Int()})};
|
||||
|
||||
auto returnStringLength{
|
||||
cbuilder.CreateMethod("returnStringLength", Prototype{TypeDescriptor::Int(), string_type})};
|
||||
Value result = returnStringLength.MakeRegister();
|
||||
returnStringLength.AddInstruction(
|
||||
Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0)));
|
||||
returnStringLength.BuildReturn(result);
|
||||
returnStringLength.Encode();
|
||||
|
||||
slicer::MemView image{dex_file.CreateImage()};
|
||||
std::ofstream out_file(outdir + "/simple.dex");
|
||||
out_file.write(image.ptr<const char>(), image.size());
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
CHECK_EQ(argc, 2);
|
||||
|
||||
string outdir = argv[1];
|
||||
|
||||
GenerateTrivialDexFile(outdir);
|
||||
GenerateSimpleTestCases(outdir);
|
||||
}
|
||||
Reference in New Issue
Block a user