[android] - Render tests with PixelMatch
This commit is contained in:
committed by
Tobrun
parent
a721b74b87
commit
be66de6fcd
16
Makefile
16
Makefile
@@ -588,6 +588,22 @@ run-android-ui-test-$1-%: platform/android/gradle/configuration.gradle
|
||||
android-ndk-stack-$1: platform/android/gradle/configuration.gradle
|
||||
adb logcat | ndk-stack -sym platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/debug/obj/$2/
|
||||
|
||||
# Run render tests with pixelmatch
|
||||
.PHONY: run-android-render-test-$1
|
||||
run-android-render-test-$1: $(BUILD_DEPS) platform/android/gradle/configuration.gradle
|
||||
-adb uninstall com.mapbox.mapboxsdk.testapp 2> /dev/null
|
||||
# delete old test results
|
||||
rm -rf platform/android/build/render-test/mapbox/
|
||||
# copy test definitions to test app assets folder, clear old ones first
|
||||
rm -rf platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/integration
|
||||
cp -r mapbox-gl-js/test/integration platform/android/MapboxGLAndroidSDKTestApp/src/main/assets
|
||||
# run RenderTest.java to generate static map images
|
||||
cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class="com.mapbox.mapboxsdk.testapp.render.RenderTest"
|
||||
# pull generated images from the device
|
||||
adb pull "`adb shell 'printenv EXTERNAL_STORAGE' | tr -d '\r'`/mapbox/render" platform/android/build/render-test
|
||||
# copy expected result and run pixelmatch
|
||||
python platform/android/scripts/run-render-test.py
|
||||
|
||||
endef
|
||||
|
||||
# Explodes the arguments into individual variables
|
||||
|
||||
4
platform/android/.gitignore
vendored
4
platform/android/.gitignore
vendored
@@ -32,3 +32,7 @@ MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/acti
|
||||
|
||||
# Generated list files from code generation
|
||||
/scripts/generate-style-code.list
|
||||
|
||||
# Input files for running render tests
|
||||
MapboxGLAndroidSDKTestApp/src/main/assets/integration/
|
||||
|
||||
|
||||
@@ -110,8 +110,8 @@ android {
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
|
||||
@@ -6,22 +6,10 @@ import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.mapbox.android.telemetry.TelemetryUtils;
|
||||
import com.mapbox.mapboxsdk.BuildConfig;
|
||||
import com.mapbox.mapboxsdk.Mapbox;
|
||||
import com.mapbox.mapboxsdk.constants.MapboxConstants;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.NoRouteToHostException;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.Dispatcher;
|
||||
@@ -32,6 +20,15 @@ import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
import timber.log.Timber;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.NoRouteToHostException;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import static android.util.Log.DEBUG;
|
||||
import static android.util.Log.ERROR;
|
||||
import static android.util.Log.INFO;
|
||||
@@ -57,43 +54,12 @@ class HTTPRequest implements Callback {
|
||||
private HTTPRequest(long nativePtr, String resourceUrl, String etag, String modified) {
|
||||
this.nativePtr = nativePtr;
|
||||
|
||||
try {
|
||||
HttpUrl httpUrl = HttpUrl.parse(resourceUrl);
|
||||
if (httpUrl == null) {
|
||||
log(Log.ERROR, String.format("[HTTP] Unable to parse resourceUrl %s", resourceUrl));
|
||||
}
|
||||
|
||||
final String host = httpUrl.host().toLowerCase(MapboxConstants.MAPBOX_LOCALE);
|
||||
// Don't try a request to remote server if we aren't connected
|
||||
if (!Mapbox.isConnected() && !host.equals("127.0.0.1") && !host.equals("localhost")) {
|
||||
throw new NoRouteToHostException("No Internet connection available.");
|
||||
}
|
||||
|
||||
if (host.equals("mapbox.com") || host.endsWith(".mapbox.com") || host.equals("mapbox.cn")
|
||||
|| host.endsWith(".mapbox.cn")) {
|
||||
if (httpUrl.querySize() == 0) {
|
||||
resourceUrl = resourceUrl + "?";
|
||||
} else {
|
||||
resourceUrl = resourceUrl + "&";
|
||||
}
|
||||
resourceUrl = resourceUrl + "events=true";
|
||||
}
|
||||
|
||||
Request.Builder builder = new Request.Builder()
|
||||
.url(resourceUrl)
|
||||
.tag(resourceUrl.toLowerCase(MapboxConstants.MAPBOX_LOCALE))
|
||||
.addHeader("User-Agent", getUserAgent());
|
||||
if (etag.length() > 0) {
|
||||
builder = builder.addHeader("If-None-Match", etag);
|
||||
} else if (modified.length() > 0) {
|
||||
builder = builder.addHeader("If-Modified-Since", modified);
|
||||
}
|
||||
Request request = builder.build();
|
||||
call = client.newCall(request);
|
||||
call.enqueue(this);
|
||||
} catch (Exception exception) {
|
||||
handleFailure(call, exception);
|
||||
if (resourceUrl.startsWith("local://")) {
|
||||
// used by render test to serve files from assets
|
||||
executeLocalRequest(resourceUrl);
|
||||
return;
|
||||
}
|
||||
executeRequest(resourceUrl, etag, modified);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
@@ -178,6 +144,57 @@ class HTTPRequest implements Callback {
|
||||
return dispatcher;
|
||||
}
|
||||
|
||||
private void executeRequest(String resourceUrl, String etag, String modified) {
|
||||
try {
|
||||
HttpUrl httpUrl = HttpUrl.parse(resourceUrl);
|
||||
if (httpUrl == null) {
|
||||
log(Log.ERROR, String.format("[HTTP] Unable to parse resourceUrl %s", resourceUrl));
|
||||
}
|
||||
|
||||
final String host = httpUrl.host().toLowerCase(MapboxConstants.MAPBOX_LOCALE);
|
||||
// Don't try a request to remote server if we aren't connected
|
||||
if (!Mapbox.isConnected() && !host.equals("127.0.0.1") && !host.equals("localhost")) {
|
||||
throw new NoRouteToHostException("No Internet connection available.");
|
||||
}
|
||||
|
||||
if (host.equals("mapbox.com") || host.endsWith(".mapbox.com") || host.equals("mapbox.cn")
|
||||
|| host.endsWith(".mapbox.cn")) {
|
||||
if (httpUrl.querySize() == 0) {
|
||||
resourceUrl = resourceUrl + "?";
|
||||
} else {
|
||||
resourceUrl = resourceUrl + "&";
|
||||
}
|
||||
resourceUrl = resourceUrl + "events=true";
|
||||
}
|
||||
|
||||
Request.Builder builder = new Request.Builder()
|
||||
.url(resourceUrl)
|
||||
.tag(resourceUrl.toLowerCase(MapboxConstants.MAPBOX_LOCALE))
|
||||
.addHeader("User-Agent", getUserAgent());
|
||||
if (etag.length() > 0) {
|
||||
builder = builder.addHeader("If-None-Match", etag);
|
||||
} else if (modified.length() > 0) {
|
||||
builder = builder.addHeader("If-Modified-Since", modified);
|
||||
}
|
||||
Request request = builder.build();
|
||||
call = client.newCall(request);
|
||||
call.enqueue(this);
|
||||
} catch (Exception exception) {
|
||||
handleFailure(call, exception);
|
||||
}
|
||||
}
|
||||
|
||||
private void executeLocalRequest(String resourceUrl) {
|
||||
new LocalRequestTask(new LocalRequestTask.OnLocalRequestResponse() {
|
||||
@Override
|
||||
public void onResponse(byte[] bytes) {
|
||||
if (bytes != null) {
|
||||
nativeOnResponse(200, null, null, null, null, null, null, bytes);
|
||||
}
|
||||
}
|
||||
}).execute(resourceUrl);
|
||||
}
|
||||
|
||||
private void handleFailure(Call call, Exception e) {
|
||||
String errorMessage = e.getMessage() != null ? e.getMessage() : "Error processing the request";
|
||||
int type = getFailureType(e);
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.mapbox.mapboxsdk.http;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.AsyncTask;
|
||||
import com.mapbox.mapboxsdk.Mapbox;
|
||||
import timber.log.Timber;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
class LocalRequestTask extends AsyncTask<String, Void, byte[]> {
|
||||
|
||||
private OnLocalRequestResponse requestResponse;
|
||||
|
||||
LocalRequestTask(OnLocalRequestResponse requestResponse) {
|
||||
this.requestResponse = requestResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] doInBackground(String... strings) {
|
||||
return loadFile(Mapbox.getApplicationContext().getAssets(),
|
||||
"integration/" + strings[0]
|
||||
.substring(8)
|
||||
.replaceAll("%20", " ")
|
||||
.replaceAll("%2c", ","));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(byte[] bytes) {
|
||||
super.onPostExecute(bytes);
|
||||
if (bytes != null && requestResponse != null) {
|
||||
requestResponse.onResponse(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] loadFile(AssetManager assets, String path) {
|
||||
byte[] buffer = null;
|
||||
try (InputStream input = assets.open(path)) {
|
||||
int size = input.available();
|
||||
buffer = new byte[size];
|
||||
input.read(buffer);
|
||||
} catch (IOException exception) {
|
||||
Timber.e(exception);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public interface OnLocalRequestResponse {
|
||||
void onResponse(byte[] bytes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.mapbox.mapboxsdk.testapp.render;
|
||||
|
||||
import android.Manifest;
|
||||
import android.support.test.espresso.IdlingPolicies;
|
||||
import android.support.test.espresso.IdlingRegistry;
|
||||
import android.support.test.espresso.IdlingResourceTimeoutException;
|
||||
import android.support.test.rule.ActivityTestRule;
|
||||
import android.support.test.rule.GrantPermissionRule;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import com.mapbox.mapboxsdk.testapp.activity.render.RenderTestActivity;
|
||||
import com.mapbox.mapboxsdk.testapp.utils.SnapshotterIdlingResource;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import timber.log.Timber;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static android.support.test.espresso.Espresso.onView;
|
||||
import static android.support.test.espresso.assertion.ViewAssertions.matches;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
||||
|
||||
/**
|
||||
* Instrumentation render tests
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class RenderTest {
|
||||
|
||||
private static final int RENDER_TEST_TIMEOUT = 30;
|
||||
private SnapshotterIdlingResource idlingResource;
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<RenderTestActivity> activityRule = new ActivityTestRule<>(RenderTestActivity.class);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule writeRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule readRule = GrantPermissionRule.grant(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
|
||||
@Before
|
||||
public void beforeTest() {
|
||||
IdlingPolicies.setMasterPolicyTimeout(RENDER_TEST_TIMEOUT, TimeUnit.MINUTES);
|
||||
setupIdlingResource();
|
||||
}
|
||||
|
||||
private void setupIdlingResource() {
|
||||
try {
|
||||
Timber.e("@Before test: register idle resource");
|
||||
IdlingPolicies.setIdlingResourceTimeout(RENDER_TEST_TIMEOUT, TimeUnit.MINUTES);
|
||||
IdlingRegistry.getInstance().register(idlingResource = new SnapshotterIdlingResource(activityRule.getActivity()));
|
||||
} catch (IdlingResourceTimeoutException idlingResourceTimeoutException) {
|
||||
throw new RuntimeException("Idling out!");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRender() {
|
||||
onView(withId(android.R.id.content)).check(matches(isDisplayed()));
|
||||
}
|
||||
|
||||
@After
|
||||
public void afterTest() {
|
||||
Timber.e("@After test: unregister idle resource");
|
||||
IdlingRegistry.getInstance().unregister(idlingResource);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import com.mapbox.mapboxsdk.testapp.activity.FeatureOverviewActivity;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -68,6 +69,7 @@ public class FileSourceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testRotateMapView() throws Exception {
|
||||
assertFalse("1) FileSource should not be active", fileSource.isActivated());
|
||||
onView(withText("Simple Map")).perform(click());
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.mapbox.mapboxsdk.testapp.utils;
|
||||
|
||||
import android.support.test.espresso.IdlingResource;
|
||||
|
||||
import com.mapbox.mapboxsdk.testapp.activity.render.RenderTestActivity;
|
||||
|
||||
public class SnapshotterIdlingResource implements IdlingResource, RenderTestActivity.OnRenderTestCompletionListener {
|
||||
|
||||
private IdlingResource.ResourceCallback resourceCallback;
|
||||
private boolean isSnapshotReady;
|
||||
|
||||
public SnapshotterIdlingResource(RenderTestActivity activity) {
|
||||
activity.setOnRenderTestCompletionListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "SnapshotterIdlingResource";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIdleNow() {
|
||||
return isSnapshotReady;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
|
||||
this.resourceCallback = resourceCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
isSnapshotReady = true;
|
||||
if (resourceCallback != null) {
|
||||
resourceCallback.onTransitionToIdle();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -791,6 +791,9 @@
|
||||
<activity
|
||||
android:name=".activity.style.FillExtrusionStyleTestActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".activity.render.RenderTestActivity"
|
||||
android:screenOrientation="landscape"/>
|
||||
<!-- Configuration Settings -->
|
||||
<meta-data
|
||||
android:name="com.mapbox.TestEventsServer"
|
||||
|
||||
@@ -12,20 +12,18 @@ import android.support.annotation.StringRes;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
|
||||
import com.mapbox.mapboxsdk.testapp.R;
|
||||
import com.mapbox.mapboxsdk.testapp.adapter.FeatureAdapter;
|
||||
import com.mapbox.mapboxsdk.testapp.adapter.FeatureSectionAdapter;
|
||||
import com.mapbox.mapboxsdk.testapp.model.activity.Feature;
|
||||
import com.mapbox.mapboxsdk.testapp.utils.ItemClickSupport;
|
||||
import timber.log.Timber;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Activity shown when application is started
|
||||
* <p>
|
||||
@@ -79,6 +77,9 @@ public class FeatureOverviewActivity extends AppCompatActivity {
|
||||
|
||||
private void onFeaturesLoaded(List<Feature> featuresList) {
|
||||
features = featuresList;
|
||||
if (featuresList == null || featuresList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<FeatureSectionAdapter.Section> sections = new ArrayList<>();
|
||||
String currentCat = "";
|
||||
|
||||
@@ -0,0 +1,316 @@
|
||||
package com.mapbox.mapboxsdk.testapp.activity.render;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.Gravity;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import com.google.gson.Gson;
|
||||
import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter;
|
||||
import okio.BufferedSource;
|
||||
import okio.Okio;
|
||||
import timber.log.Timber;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Activity that generates map snapshots based on the node render test suite.
|
||||
*/
|
||||
public class RenderTestActivity extends AppCompatActivity {
|
||||
|
||||
private static final String TEST_BASE_PATH = "integration/render-tests";
|
||||
|
||||
// TODO read out excluded tests from /platform/node/test/ignore.json
|
||||
private static final List<String> EXCLUDED_TESTS = new ArrayList<String>() {
|
||||
{
|
||||
add("overlay,background-opacity");
|
||||
add("collision-lines-pitched,debug");
|
||||
add("1024-circle,extent");
|
||||
add("empty,empty");
|
||||
add("rotation-alignment-map,icon-pitch-scaling");
|
||||
add("rotation-alignment-viewport,icon-pitch-scaling");
|
||||
add("pitch15,line-pitch");
|
||||
add("pitch30,line-pitch");
|
||||
add("line-placement-true-pitched,text-keep-upright");
|
||||
add("180,raster-rotation");
|
||||
add("45,raster-rotation");
|
||||
add("90,raster-rotation");
|
||||
add("mapbox-gl-js#5631,regressions"); // crashes
|
||||
add("overlapping,raster-masking");
|
||||
add("missing,raster-loading");
|
||||
add("pitchAndBearing,line-pitch");
|
||||
}
|
||||
};
|
||||
|
||||
private final Map<RenderTestDefinition, Bitmap> renderResultMap = new HashMap<>();
|
||||
private List<RenderTestDefinition> renderTestDefinitions;
|
||||
private OnRenderTestCompletionListener onRenderTestCompletionListener;
|
||||
private MapSnapshotter mapSnapshotter;
|
||||
private ImageView imageView;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(imageView = new ImageView(RenderTestActivity.this));
|
||||
imageView.setLayoutParams(new FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
if (mapSnapshotter != null) {
|
||||
mapSnapshotter.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Loads the render test definitions from assets folder
|
||||
//
|
||||
private static class LoadRenderDefinitionTask extends AsyncTask<Void, Void, List<RenderTestDefinition>> {
|
||||
|
||||
private WeakReference<RenderTestActivity> renderTestActivityWeakReference;
|
||||
|
||||
LoadRenderDefinitionTask(RenderTestActivity renderTestActivity) {
|
||||
this.renderTestActivityWeakReference = new WeakReference<>(renderTestActivity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<RenderTestDefinition> doInBackground(Void... voids) {
|
||||
List<RenderTestDefinition> definitions = new ArrayList<>();
|
||||
AssetManager assetManager = renderTestActivityWeakReference.get().getAssets();
|
||||
String[] categories = new String[0];
|
||||
try {
|
||||
categories = assetManager.list(TEST_BASE_PATH);
|
||||
} catch (IOException exception) {
|
||||
Timber.e(exception);
|
||||
}
|
||||
for (int counter = categories.length - 1; counter >= 0; counter--) {
|
||||
try {
|
||||
String[] tests = assetManager.list(String.format("%s/%s", TEST_BASE_PATH, categories[counter]));
|
||||
for (String test : tests) {
|
||||
String styleJson = loadStyleJson(assetManager, categories[counter], test);
|
||||
RenderTestStyleDefinition renderTestStyleDefinition = new Gson()
|
||||
.fromJson(styleJson, RenderTestStyleDefinition.class);
|
||||
RenderTestDefinition definition = new RenderTestDefinition(
|
||||
categories[counter], test, styleJson, renderTestStyleDefinition);
|
||||
if (!definition.hasOperations()) {
|
||||
if (!definition.getCategory().equals("combinations")
|
||||
&& !EXCLUDED_TESTS.contains(definition.getName() + "," + definition.getCategory())) {
|
||||
definitions.add(definition);
|
||||
}
|
||||
} else {
|
||||
Timber.e("could not add test, test requires operations: %s from %s", test, categories[counter]);
|
||||
}
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
Timber.e(exception);
|
||||
}
|
||||
}
|
||||
return definitions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(List<RenderTestDefinition> renderTestDefinitions) {
|
||||
super.onPostExecute(renderTestDefinitions);
|
||||
RenderTestActivity renderTestActivity = renderTestActivityWeakReference.get();
|
||||
if (renderTestActivity != null) {
|
||||
renderTestActivity.startRenderTests(renderTestDefinitions);
|
||||
}
|
||||
}
|
||||
|
||||
private static String loadStyleJson(AssetManager assets, String category, String test) {
|
||||
String styleJson = null;
|
||||
try (InputStream input = assets.open(String.format("%s/%s/%s/style.json", TEST_BASE_PATH, category, test))) {
|
||||
BufferedSource source = Okio.buffer(Okio.source(input));
|
||||
styleJson = source.readByteString().string(Charset.forName("utf-8"));
|
||||
} catch (IOException exception) {
|
||||
Timber.e(exception);
|
||||
}
|
||||
return styleJson;
|
||||
}
|
||||
}
|
||||
|
||||
private void startRenderTests(List<RenderTestDefinition> renderTestDefinitions) {
|
||||
this.renderTestDefinitions = renderTestDefinitions;
|
||||
if (!renderTestDefinitions.isEmpty()) {
|
||||
render(renderTestDefinitions.get(0), renderTestDefinitions.size());
|
||||
} else {
|
||||
// no definitions, finish test without rendering
|
||||
onRenderTestCompletionListener.onFinish();
|
||||
}
|
||||
}
|
||||
|
||||
private void render(final RenderTestDefinition renderTestDefinition, final int testSize) {
|
||||
Timber.d("Render test %s,%s", renderTestDefinition.getName(), renderTestDefinition.getCategory());
|
||||
mapSnapshotter = new RenderTestSnapshotter(this, renderTestDefinition.toOptions());
|
||||
mapSnapshotter.start(result -> {
|
||||
Bitmap snapshot = result.getBitmap();
|
||||
imageView.setImageBitmap(snapshot);
|
||||
renderResultMap.put(renderTestDefinition, snapshot);
|
||||
if (renderResultMap.size() != testSize) {
|
||||
continueTesting(renderTestDefinition);
|
||||
} else {
|
||||
finishTesting();
|
||||
}
|
||||
}, error -> Timber.e(error));
|
||||
}
|
||||
|
||||
private void continueTesting(RenderTestDefinition renderTestDefinition) {
|
||||
int next = renderTestDefinitions.indexOf(renderTestDefinition) + 1;
|
||||
Timber.d("Next test: %s / %s", next, renderTestDefinitions.size());
|
||||
render(renderTestDefinitions.get(next), renderTestDefinitions.size());
|
||||
}
|
||||
|
||||
private void finishTesting() {
|
||||
new SaveResultToDiskTask(onRenderTestCompletionListener, renderResultMap).execute();
|
||||
}
|
||||
|
||||
//
|
||||
// Save tests results to disk
|
||||
//
|
||||
private static class SaveResultToDiskTask extends AsyncTask<Void, Void, Void> {
|
||||
|
||||
private OnRenderTestCompletionListener onRenderTestCompletionListener;
|
||||
private Map<RenderTestDefinition, Bitmap> renderResultMap;
|
||||
|
||||
SaveResultToDiskTask(OnRenderTestCompletionListener onRenderTestCompletionListener,
|
||||
Map<RenderTestDefinition, Bitmap> renderResultMap) {
|
||||
this.onRenderTestCompletionListener = onRenderTestCompletionListener;
|
||||
this.renderResultMap = renderResultMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
if (isExternalStorageWritable()) {
|
||||
try {
|
||||
File testResultDir = FileUtils.createTestResultRootFolder();
|
||||
String basePath = testResultDir.getAbsolutePath();
|
||||
for (Map.Entry<RenderTestDefinition, Bitmap> testResult : renderResultMap.entrySet()) {
|
||||
writeResultToDisk(basePath, testResult);
|
||||
}
|
||||
} catch (final Exception exception) {
|
||||
Timber.e(exception);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void writeResultToDisk(String path, Map.Entry<RenderTestDefinition, Bitmap> result) {
|
||||
RenderTestDefinition definition = result.getKey();
|
||||
String categoryName = definition.getCategory();
|
||||
String categoryPath = String.format("%s/%s", path, categoryName);
|
||||
FileUtils.createCategoryDirectory(categoryPath);
|
||||
String testName = result.getKey().getName();
|
||||
String testDir = FileUtils.createTestDirectory(categoryPath, testName);
|
||||
FileUtils.writeTestResultToDisk(testDir, result.getValue());
|
||||
}
|
||||
|
||||
private boolean isExternalStorageWritable() {
|
||||
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
super.onPostExecute(aVoid);
|
||||
if (onRenderTestCompletionListener != null) {
|
||||
onRenderTestCompletionListener.onFinish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Callback configuration to notify test executor of test finishing
|
||||
//
|
||||
public interface OnRenderTestCompletionListener {
|
||||
void onFinish();
|
||||
}
|
||||
|
||||
public void setOnRenderTestCompletionListener(OnRenderTestCompletionListener listener) {
|
||||
this.onRenderTestCompletionListener = listener;
|
||||
new LoadRenderDefinitionTask(this).execute();
|
||||
}
|
||||
|
||||
//
|
||||
// FileUtils
|
||||
//
|
||||
|
||||
private static class FileUtils {
|
||||
|
||||
private static void createCategoryDirectory(String catPath) {
|
||||
File testResultDir = new File(catPath);
|
||||
if (testResultDir.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!testResultDir.mkdirs()) {
|
||||
throw new RuntimeException("can't create root test directory");
|
||||
}
|
||||
}
|
||||
|
||||
private static File createTestResultRootFolder() {
|
||||
File testResultDir = new File(Environment.getExternalStorageDirectory()
|
||||
+ File.separator + "mapbox" + File.separator + "render");
|
||||
if (testResultDir.exists()) {
|
||||
// cleanup old files
|
||||
deleteRecursive(testResultDir);
|
||||
}
|
||||
|
||||
if (!testResultDir.mkdirs()) {
|
||||
throw new RuntimeException("can't create root test directory");
|
||||
}
|
||||
return testResultDir;
|
||||
}
|
||||
|
||||
private static void deleteRecursive(File fileOrDirectory) {
|
||||
if (fileOrDirectory.isDirectory()) {
|
||||
File[] files = fileOrDirectory.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
deleteRecursive(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fileOrDirectory.delete()) {
|
||||
Timber.e("can't delete directory");
|
||||
}
|
||||
}
|
||||
|
||||
private static String createTestDirectory(String basePath, String testName) {
|
||||
File testDir = new File(basePath + "/" + testName);
|
||||
if (!testDir.exists()) {
|
||||
if (!testDir.mkdir()) {
|
||||
throw new RuntimeException("can't create sub directory for " + testName);
|
||||
}
|
||||
}
|
||||
return testDir.getAbsolutePath();
|
||||
}
|
||||
|
||||
private static void writeTestResultToDisk(String testPath, Bitmap testResult) {
|
||||
String filePath = testPath + "/actual.png";
|
||||
try (FileOutputStream out = new FileOutputStream(filePath)) {
|
||||
testResult.compress(Bitmap.CompressFormat.PNG, 100, out);
|
||||
} catch (IOException exception) {
|
||||
Timber.e(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.mapbox.mapboxsdk.testapp.activity.render;
|
||||
|
||||
import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter;
|
||||
|
||||
public class RenderTestDefinition {
|
||||
|
||||
private static final int DEFAULT_WIDTH = 512;
|
||||
private static final int DEFAULT_HEIGHT = 512;
|
||||
|
||||
private String category; // eg. background-color
|
||||
private String name; // eg. colorSpace-hcl
|
||||
private String styleJson;
|
||||
private RenderTestStyleDefinition definition;
|
||||
|
||||
RenderTestDefinition(String category, String name, String styleJson, RenderTestStyleDefinition definition) {
|
||||
this.category = category;
|
||||
this.name = name;
|
||||
this.styleJson = styleJson;
|
||||
this.definition = definition;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
RenderTestStyleDefinition.Test test = getTest();
|
||||
if (test != null) {
|
||||
Integer testWidth = test.getWidth();
|
||||
if (testWidth != null && testWidth > 0) {
|
||||
return testWidth;
|
||||
}
|
||||
}
|
||||
return DEFAULT_WIDTH;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
RenderTestStyleDefinition.Test test = getTest();
|
||||
if (test != null) {
|
||||
Integer testHeight = test.getHeight();
|
||||
if (testHeight != null && testHeight > 0) {
|
||||
return testHeight;
|
||||
}
|
||||
}
|
||||
return DEFAULT_HEIGHT;
|
||||
}
|
||||
|
||||
public String getStyleJson() {
|
||||
return styleJson;
|
||||
}
|
||||
|
||||
public boolean hasOperations() {
|
||||
return getTest().getOperations() != null;
|
||||
}
|
||||
|
||||
public RenderTestStyleDefinition.Test getTest() {
|
||||
return definition.getMetadata().getTest();
|
||||
}
|
||||
|
||||
public MapSnapshotter.Options toOptions() {
|
||||
return new MapSnapshotter
|
||||
.Options(getWidth(), getHeight())
|
||||
.withStyleJson(styleJson)
|
||||
.withLogo(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RenderTestDefinition{"
|
||||
+ "category='" + category + '\''
|
||||
+ ", name='" + name + '\''
|
||||
+ ", styleJson='" + styleJson + '\''
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.mapbox.mapboxsdk.testapp.activity.render;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import com.mapbox.mapboxsdk.snapshotter.MapSnapshot;
|
||||
import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter;
|
||||
|
||||
public class RenderTestSnapshotter extends MapSnapshotter {
|
||||
|
||||
RenderTestSnapshotter(@NonNull Context context, @NonNull Options options) {
|
||||
super(context, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addOverlay(MapSnapshot mapSnapshot) {
|
||||
// don't add an overlay
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.mapbox.mapboxsdk.testapp.activity.render;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class RenderTestStyleDefinition {
|
||||
|
||||
private Integer version;
|
||||
private Metadata metadata;
|
||||
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
|
||||
|
||||
public Integer getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Integer version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Metadata getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
public void setMetadata(Metadata metadata) {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
public Map<String, Object> getAdditionalProperties() {
|
||||
return this.additionalProperties;
|
||||
}
|
||||
|
||||
public void setAdditionalProperty(String name, Object value) {
|
||||
this.additionalProperties.put(name, value);
|
||||
}
|
||||
|
||||
|
||||
public class Metadata {
|
||||
|
||||
private Test test;
|
||||
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
|
||||
|
||||
public Test getTest() {
|
||||
return test;
|
||||
}
|
||||
|
||||
public void setTest(Test test) {
|
||||
this.test = test;
|
||||
}
|
||||
|
||||
public Map<String, Object> getAdditionalProperties() {
|
||||
return this.additionalProperties;
|
||||
}
|
||||
|
||||
public void setAdditionalProperty(String name, Object value) {
|
||||
this.additionalProperties.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
public class Test {
|
||||
|
||||
private Integer width;
|
||||
private Integer height;
|
||||
private List<Integer> center = null;
|
||||
private Integer zoom;
|
||||
private Double diff;
|
||||
private List<List<String>> operations = null;
|
||||
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
|
||||
|
||||
public Integer getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void setWidth(Integer width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public Integer getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void setHeight(Integer height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public List<Integer> getCenter() {
|
||||
return center;
|
||||
}
|
||||
|
||||
public void setCenter(List<Integer> center) {
|
||||
this.center = center;
|
||||
}
|
||||
|
||||
public Integer getZoom() {
|
||||
return zoom;
|
||||
}
|
||||
|
||||
public void setZoom(Integer zoom) {
|
||||
this.zoom = zoom;
|
||||
}
|
||||
|
||||
public Double getDiff() {
|
||||
return diff;
|
||||
}
|
||||
|
||||
public void setDiff(Double diff) {
|
||||
this.diff = diff;
|
||||
}
|
||||
|
||||
public List<List<String>> getOperations() {
|
||||
return operations;
|
||||
}
|
||||
|
||||
public void setOperations(List<List<String>> operations) {
|
||||
this.operations = operations;
|
||||
}
|
||||
|
||||
public Map<String, Object> getAdditionalProperties() {
|
||||
return this.additionalProperties;
|
||||
}
|
||||
|
||||
public void setAdditionalProperty(String name, Object value) {
|
||||
this.additionalProperties.put(name, value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -34,5 +34,6 @@
|
||||
"ZoomFunctionSymbolLayerActivity",
|
||||
"SymbolGeneratorActivity",
|
||||
"TextureViewTransparentBackgroundActivity",
|
||||
"SimpleMapActivity"
|
||||
"SimpleMapActivity",
|
||||
"RenderTestActivity"
|
||||
]
|
||||
|
||||
24
platform/android/scripts/run-render-test.py
Normal file
24
platform/android/scripts/run-render-test.py
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os
|
||||
from shutil import copyfile
|
||||
|
||||
catPath = os.getcwd() + "/platform/android/build/render-test/render/"
|
||||
for cat in os.listdir(catPath):
|
||||
testPath = catPath + cat + "/"
|
||||
for test in os.listdir(testPath):
|
||||
inputPath = os.getcwd() + "/mapbox-gl-js/test/integration/render-tests/" + cat + "/" + test
|
||||
outputPath = testPath + test
|
||||
|
||||
expected = outputPath + "/expected.png"
|
||||
actual = outputPath + "/actual.png"
|
||||
output = outputPath + "/output.png"
|
||||
|
||||
copyfile(inputPath + "/expected.png", expected)
|
||||
copyfile(inputPath + "/style.json", outputPath + "/style.json")
|
||||
pixelmatch = "node_modules/pixelmatch/bin/pixelmatch " + actual + " " + expected + " " + output + " 0.1"
|
||||
|
||||
print
|
||||
print "Pixel match "+ cat + " " + test
|
||||
os.system(pixelmatch)
|
||||
print
|
||||
Reference in New Issue
Block a user