Loading tests/common/com/android/documentsui/FuzzyColor.java 0 → 100644 +154 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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.documentsui; import android.graphics.Color; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.util.Locale; /** * To help the image verification by using RGB, HSV, and HSL color space to make sure whether the * pixels in the image over the threshold or not. Not only to use color ARGB match but also to * fuzzy color match(RGB, HSV, HSL). */ public class FuzzyColor { public static class Threshold { public int getColor() { return 20; } public float getHue() { return 8f; } public float getSaturation() { return 0.25f; } public float getValue() { return 0.25f; } public float getLuminance() { return 0.1f; } } private static Threshold sThreshold = new Threshold(); /** * To set the threshold adjust the verify algorithm. * * @param threshold the threshold that want to set */ public static synchronized void setThreshold(Threshold threshold) { if (threshold == null) { return; } sThreshold = threshold; } private final int mColor; private final float [] mHsv; private final float mLuminance; private final int mRed; private final int mGreen; private final int mBlue; private int mCount; /** * To initial a FuzzyColor according to color parameter. * * @param color the ARGB color for the pixel in the image */ public FuzzyColor(int color) { mColor = color; mRed = Color.red(color); mGreen = Color.green(color); mBlue = Color.blue(color); mHsv = new float[3]; mLuminance = Color.luminance(color); Color.RGBToHSV(Color.red(color), Color.green(color), Color.blue(color), mHsv); } public void add(int count) { mCount += count; } public int getCount() { return mCount; } public int getColor() { return mColor; } @Override public boolean equals(@Nullable Object obj) { if (obj instanceof FuzzyColor) { FuzzyColor other = (FuzzyColor) obj; return compare(this, other); } else if (obj instanceof Integer) { int color = (int) obj; return compare(this, new FuzzyColor(color)); } else { return false; } } private static boolean compare(FuzzyColor c1, FuzzyColor c2) { if (Math.abs(c1.mRed - c2.mRed) < sThreshold.getColor() && Math.abs(c1.mGreen - c2.mGreen) < sThreshold.getColor() && Math.abs(c1.mBlue - c2.mBlue) < sThreshold.getColor()) { return true; } float hueDiff = Math.abs(c1.mHsv[0] - c2.mHsv[0]); if (hueDiff > sThreshold.getHue() && hueDiff < (360f - sThreshold.getHue())) { return false; } float saturationDiff = Math.abs(c1.mHsv[1] - c2.mHsv[1]); if (saturationDiff > sThreshold.getSaturation()) { return false; } float valueDiff = Math.abs(c1.mHsv[2] - c2.mHsv[2]); if (valueDiff > sThreshold.getValue()) { return false; } float luminanceDiff = Math.abs(c1.mLuminance - c2.mLuminance); if (luminanceDiff > sThreshold.getLuminance()) { return false; } return true; } @NonNull @Override public String toString() { return String.format(Locale.ENGLISH, "[#%08x] count = %d, hue = %f, saturation = %f, value = %f, luminance = %f", mColor, mCount, mHsv[0], mHsv[1], mHsv[2], mLuminance); } } tests/common/com/android/documentsui/ScreenshotCaptureRule.java 0 → 100644 +53 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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.documentsui; import android.graphics.Bitmap; import android.util.Log; import androidx.test.runner.screenshot.BasicScreenCaptureProcessor; import androidx.test.runner.screenshot.ScreenCapture; import androidx.test.runner.screenshot.Screenshot; import java.io.IOException; import org.junit.rules.TestWatcher; import org.junit.runner.Description; /** * When the test fail happen, the screen capture will help the developer to judge * what does the UI looks like, where is the asserted view, and why test fail. */ public class ScreenshotCaptureRule extends TestWatcher { private static final String TAG = ScreenshotCaptureRule.class.getSimpleName(); @Override protected void failed(Throwable e, Description description) { super.failed(e, description); ScreenCapture capture = Screenshot.capture(); capture.setFormat(Bitmap.CompressFormat.PNG); capture.setName(description.getMethodName()); try { new BasicScreenCaptureProcessor().process(capture); } catch (IOException e1) { Log.e(TAG, "Can't handle the capture. " + e1.getMessage()); } } } tests/unit/com/android/documentsui/picker/PickActivityTest.java 0 → 100644 +123 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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.documentsui.picker; import static androidx.test.espresso.Espresso.onView; import static androidx.test.espresso.matcher.ViewMatchers.withText; import static com.google.common.truth.Truth.assertThat; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Color; import androidx.test.espresso.ViewAssertion; import androidx.test.filters.MediumTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.screenshot.ScreenCapture; import androidx.test.runner.screenshot.Screenshot; import com.android.documentsui.FuzzyColor; import com.android.documentsui.R; import com.android.documentsui.ScreenshotCaptureRule; import java.util.ArrayList; import java.util.List; import org.junit.Rule; import org.junit.Test; import org.junit.rules.RuleChain; import org.junit.rules.TestName; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @MediumTest public class PickActivityTest { @Rule public ActivityTestRule mActivityTestRule = new ActivityTestRule(PickActivity.class, false, false); @Rule public RuleChain mRuleChain = RuleChain.outerRule(mActivityTestRule) .around(new ScreenshotCaptureRule()); @Rule public TestName mTestName = new TestName(); private static List<FuzzyColor> getFuzzyHistogram(Bitmap bitmap, FuzzyColor.Threshold threshold) { List<FuzzyColor> histogram = new ArrayList<>(); FuzzyColor.setThreshold(threshold); final int size = bitmap.getWidth() * bitmap.getHeight(); final int [] colorPixels = new int[size]; bitmap.getPixels(colorPixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight()); for (int color : colorPixels) { if (Color.alpha(color) < 127) { continue; } FuzzyColor target = null; for (FuzzyColor matcher : histogram) { if (matcher.equals(color)) { matcher.add(1); target = matcher; } } if (target == null) { target = new FuzzyColor(color); target.add(1); histogram.add(target); } } histogram.sort((o1, o2) -> o2.getCount() - o1.getCount()); return histogram; } static ViewAssertion assertColorCorrect(TestName testName, FuzzyColor.Threshold threshold) { return (view, noViewFoundException) -> { ScreenCapture capture = Screenshot.capture(view); capture.setName(testName.getMethodName()); capture.setFormat(Bitmap.CompressFormat.PNG); Bitmap bitmap = capture.getBitmap(); List<FuzzyColor> fuzzyHistogram = getFuzzyHistogram(bitmap, threshold); assertThat(fuzzyHistogram.size()).named("The number of dominant color") .isGreaterThan(1); }; } @Test public void onCreate_actionCreate_shouldLaunchSuccess() throws InterruptedException { Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("text/plain"); intent.putExtra(Intent.EXTRA_TITLE, "foobar.txt"); mActivityTestRule.launchActivity(intent); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); onView(withText(R.string.menu_save)).check(assertColorCorrect(mTestName, null)); } } Loading
tests/common/com/android/documentsui/FuzzyColor.java 0 → 100644 +154 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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.documentsui; import android.graphics.Color; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.util.Locale; /** * To help the image verification by using RGB, HSV, and HSL color space to make sure whether the * pixels in the image over the threshold or not. Not only to use color ARGB match but also to * fuzzy color match(RGB, HSV, HSL). */ public class FuzzyColor { public static class Threshold { public int getColor() { return 20; } public float getHue() { return 8f; } public float getSaturation() { return 0.25f; } public float getValue() { return 0.25f; } public float getLuminance() { return 0.1f; } } private static Threshold sThreshold = new Threshold(); /** * To set the threshold adjust the verify algorithm. * * @param threshold the threshold that want to set */ public static synchronized void setThreshold(Threshold threshold) { if (threshold == null) { return; } sThreshold = threshold; } private final int mColor; private final float [] mHsv; private final float mLuminance; private final int mRed; private final int mGreen; private final int mBlue; private int mCount; /** * To initial a FuzzyColor according to color parameter. * * @param color the ARGB color for the pixel in the image */ public FuzzyColor(int color) { mColor = color; mRed = Color.red(color); mGreen = Color.green(color); mBlue = Color.blue(color); mHsv = new float[3]; mLuminance = Color.luminance(color); Color.RGBToHSV(Color.red(color), Color.green(color), Color.blue(color), mHsv); } public void add(int count) { mCount += count; } public int getCount() { return mCount; } public int getColor() { return mColor; } @Override public boolean equals(@Nullable Object obj) { if (obj instanceof FuzzyColor) { FuzzyColor other = (FuzzyColor) obj; return compare(this, other); } else if (obj instanceof Integer) { int color = (int) obj; return compare(this, new FuzzyColor(color)); } else { return false; } } private static boolean compare(FuzzyColor c1, FuzzyColor c2) { if (Math.abs(c1.mRed - c2.mRed) < sThreshold.getColor() && Math.abs(c1.mGreen - c2.mGreen) < sThreshold.getColor() && Math.abs(c1.mBlue - c2.mBlue) < sThreshold.getColor()) { return true; } float hueDiff = Math.abs(c1.mHsv[0] - c2.mHsv[0]); if (hueDiff > sThreshold.getHue() && hueDiff < (360f - sThreshold.getHue())) { return false; } float saturationDiff = Math.abs(c1.mHsv[1] - c2.mHsv[1]); if (saturationDiff > sThreshold.getSaturation()) { return false; } float valueDiff = Math.abs(c1.mHsv[2] - c2.mHsv[2]); if (valueDiff > sThreshold.getValue()) { return false; } float luminanceDiff = Math.abs(c1.mLuminance - c2.mLuminance); if (luminanceDiff > sThreshold.getLuminance()) { return false; } return true; } @NonNull @Override public String toString() { return String.format(Locale.ENGLISH, "[#%08x] count = %d, hue = %f, saturation = %f, value = %f, luminance = %f", mColor, mCount, mHsv[0], mHsv[1], mHsv[2], mLuminance); } }
tests/common/com/android/documentsui/ScreenshotCaptureRule.java 0 → 100644 +53 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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.documentsui; import android.graphics.Bitmap; import android.util.Log; import androidx.test.runner.screenshot.BasicScreenCaptureProcessor; import androidx.test.runner.screenshot.ScreenCapture; import androidx.test.runner.screenshot.Screenshot; import java.io.IOException; import org.junit.rules.TestWatcher; import org.junit.runner.Description; /** * When the test fail happen, the screen capture will help the developer to judge * what does the UI looks like, where is the asserted view, and why test fail. */ public class ScreenshotCaptureRule extends TestWatcher { private static final String TAG = ScreenshotCaptureRule.class.getSimpleName(); @Override protected void failed(Throwable e, Description description) { super.failed(e, description); ScreenCapture capture = Screenshot.capture(); capture.setFormat(Bitmap.CompressFormat.PNG); capture.setName(description.getMethodName()); try { new BasicScreenCaptureProcessor().process(capture); } catch (IOException e1) { Log.e(TAG, "Can't handle the capture. " + e1.getMessage()); } } }
tests/unit/com/android/documentsui/picker/PickActivityTest.java 0 → 100644 +123 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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.documentsui.picker; import static androidx.test.espresso.Espresso.onView; import static androidx.test.espresso.matcher.ViewMatchers.withText; import static com.google.common.truth.Truth.assertThat; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Color; import androidx.test.espresso.ViewAssertion; import androidx.test.filters.MediumTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.screenshot.ScreenCapture; import androidx.test.runner.screenshot.Screenshot; import com.android.documentsui.FuzzyColor; import com.android.documentsui.R; import com.android.documentsui.ScreenshotCaptureRule; import java.util.ArrayList; import java.util.List; import org.junit.Rule; import org.junit.Test; import org.junit.rules.RuleChain; import org.junit.rules.TestName; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @MediumTest public class PickActivityTest { @Rule public ActivityTestRule mActivityTestRule = new ActivityTestRule(PickActivity.class, false, false); @Rule public RuleChain mRuleChain = RuleChain.outerRule(mActivityTestRule) .around(new ScreenshotCaptureRule()); @Rule public TestName mTestName = new TestName(); private static List<FuzzyColor> getFuzzyHistogram(Bitmap bitmap, FuzzyColor.Threshold threshold) { List<FuzzyColor> histogram = new ArrayList<>(); FuzzyColor.setThreshold(threshold); final int size = bitmap.getWidth() * bitmap.getHeight(); final int [] colorPixels = new int[size]; bitmap.getPixels(colorPixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight()); for (int color : colorPixels) { if (Color.alpha(color) < 127) { continue; } FuzzyColor target = null; for (FuzzyColor matcher : histogram) { if (matcher.equals(color)) { matcher.add(1); target = matcher; } } if (target == null) { target = new FuzzyColor(color); target.add(1); histogram.add(target); } } histogram.sort((o1, o2) -> o2.getCount() - o1.getCount()); return histogram; } static ViewAssertion assertColorCorrect(TestName testName, FuzzyColor.Threshold threshold) { return (view, noViewFoundException) -> { ScreenCapture capture = Screenshot.capture(view); capture.setName(testName.getMethodName()); capture.setFormat(Bitmap.CompressFormat.PNG); Bitmap bitmap = capture.getBitmap(); List<FuzzyColor> fuzzyHistogram = getFuzzyHistogram(bitmap, threshold); assertThat(fuzzyHistogram.size()).named("The number of dominant color") .isGreaterThan(1); }; } @Test public void onCreate_actionCreate_shouldLaunchSuccess() throws InterruptedException { Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("text/plain"); intent.putExtra(Intent.EXTRA_TITLE, "foobar.txt"); mActivityTestRule.launchActivity(intent); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); onView(withText(R.string.menu_save)).check(assertColorCorrect(mTestName, null)); } }