diff options
Diffstat (limited to 'platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox')
5 files changed, 544 insertions, 3 deletions
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java index c8b15593ec..9279472cb5 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java @@ -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 = ""; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestActivity.java new file mode 100644 index 0000000000..81a7758d44 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestActivity.java @@ -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); + } + } + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestDefinition.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestDefinition.java new file mode 100644 index 0000000000..fa8c816203 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestDefinition.java @@ -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 + '\'' + + '}'; + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestSnapshotter.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestSnapshotter.java new file mode 100644 index 0000000000..cb971fee70 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestSnapshotter.java @@ -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 + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestStyleDefinition.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestStyleDefinition.java new file mode 100644 index 0000000000..fdd7e9aaf1 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestStyleDefinition.java @@ -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); + } + + } +}
\ No newline at end of file |