Merge "[view_compiler] Add end-to-end DexBuilder tests"
This commit is contained in:
@@ -62,3 +62,22 @@ cc_test_host {
|
|||||||
],
|
],
|
||||||
test_suites: ["general-tests"],
|
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.
|
application.
|
||||||
* This only works for apps that do not use a custom layout inflater.
|
* This only works for apps that do not use a custom layout inflater.
|
||||||
* Other limitations yet to be discovered.
|
* 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