blob: 26e9f7e0cab1affab9479edf17fb35602d1a8e05 [file] [log] [blame]
// Copyright (c) 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/accessibility/accessibility_highlight_manager.h"
#include "ash/shell.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/run_loop.h"
#include "chrome/browser/chromeos/ui/accessibility_focus_ring_controller.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chromeos/chromeos_switches.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/focused_node_details.h"
#include "content/public/browser/notification_types.h"
#include "ui/aura/window.h"
#include "ui/base/ime/dummy_text_input_client.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/compositor/compositor_switches.h"
#include "ui/events/event_utils.h"
#include "ui/gfx/image/image.h"
#include "ui/snapshot/snapshot.h"
namespace chromeos {
namespace {
class MockTextInputClient : public ui::DummyTextInputClient {
public:
MockTextInputClient() : ui::DummyTextInputClient(ui::TEXT_INPUT_TYPE_TEXT) {}
void SetCaretBounds(const gfx::Rect& bounds) { caret_bounds_ = bounds; }
private:
gfx::Rect GetCaretBounds() const override { return caret_bounds_; }
gfx::Rect caret_bounds_;
DISALLOW_COPY_AND_ASSIGN(MockTextInputClient);
};
class TestAccessibilityHighlightManager : public AccessibilityHighlightManager {
public:
TestAccessibilityHighlightManager() {}
void OnCaretBoundsChanged(const ui::TextInputClient* client) override {
AccessibilityHighlightManager::OnCaretBoundsChanged(client);
}
void OnMouseEvent(ui::MouseEvent* event) override {
AccessibilityHighlightManager::OnMouseEvent(event);
}
void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) override {
AccessibilityHighlightManager::Observe(type, source, details);
}
private:
bool IsCursorVisible() override { return true; }
DISALLOW_COPY_AND_ASSIGN(TestAccessibilityHighlightManager);
};
} // namespace
class AccessibilityHighlightManagerTest : public InProcessBrowserTest {
protected:
AccessibilityHighlightManagerTest() {}
~AccessibilityHighlightManagerTest() override {}
void SetUp() override {
AccessibilityFocusRingController::GetInstance()->SetNoFadeForTesting();
InProcessBrowserTest::SetUp();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(::switches::kEnablePixelOutputInTests);
}
void CaptureBeforeImage(const gfx::Rect& bounds) {
Capture(bounds);
if (before_bmp_.tryAllocPixels(image_.AsBitmap().info())) {
image_.AsBitmap().readPixels(before_bmp_.info(), before_bmp_.getPixels(),
before_bmp_.rowBytes(), 0, 0);
}
}
void CaptureAfterImage(const gfx::Rect& bounds) {
Capture(bounds);
if (after_bmp_.tryAllocPixels(image_.AsBitmap().info())) {
image_.AsBitmap().readPixels(after_bmp_.info(), after_bmp_.getPixels(),
after_bmp_.rowBytes(), 0, 0);
}
}
void ComputeImageStats() {
diff_count_ = 0;
double accum[4] = {0, 0, 0, 0};
for (int x = 0; x < before_bmp_.width(); ++x) {
for (int y = 0; y < before_bmp_.height(); ++y) {
SkColor before_color = before_bmp_.getColor(x, y);
SkColor after_color = after_bmp_.getColor(x, y);
if (before_color != after_color) {
diff_count_++;
accum[0] += SkColorGetB(after_color);
accum[1] += SkColorGetG(after_color);
accum[2] += SkColorGetR(after_color);
accum[3] += SkColorGetA(after_color);
}
}
}
average_diff_color_ =
SkColorSetARGB(static_cast<unsigned char>(accum[3] / diff_count_),
static_cast<unsigned char>(accum[2] / diff_count_),
static_cast<unsigned char>(accum[1] / diff_count_),
static_cast<unsigned char>(accum[0] / diff_count_));
}
int diff_count() { return diff_count_; }
SkColor average_diff_color() { return average_diff_color_; }
private:
void GotSnapshot(const gfx::Image& image) {
image_ = image;
content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
run_loop_quitter_);
}
void Capture(const gfx::Rect& bounds) {
// Occasionally we don't get any pixels the first try.
// Keep trying until we get the correct size bitmap and
// the first pixel is not transparent.
while (true) {
aura::Window* window = ash::Shell::GetPrimaryRootWindow();
ui::GrabWindowSnapshotAndScaleAsync(
window, bounds, bounds.size(),
base::Bind(&AccessibilityHighlightManagerTest::GotSnapshot,
base::Unretained(this)));
base::RunLoop run_loop;
run_loop_quitter_ = run_loop.QuitClosure();
run_loop.Run();
SkBitmap bitmap = image_.AsBitmap();
if (bitmap.width() != bounds.width() ||
bitmap.height() != bounds.height()) {
LOG(INFO) << "Bitmap not correct size, trying to capture again";
continue;
}
if (255 == SkColorGetA(bitmap.getColor(0, 0))) {
LOG(INFO) << "Bitmap is transparent, trying to capture again";
break;
}
}
}
base::Closure run_loop_quitter_;
gfx::Image image_;
SkBitmap before_bmp_;
SkBitmap after_bmp_;
int diff_count_ = 0;
SkColor average_diff_color_ = 0;
DISALLOW_COPY_AND_ASSIGN(AccessibilityHighlightManagerTest);
};
IN_PROC_BROWSER_TEST_F(AccessibilityHighlightManagerTest,
TestCaretRingDrawsBluePixels) {
gfx::Rect capture_bounds(200, 300, 100, 100);
gfx::Rect caret_bounds(230, 330, 1, 25);
CaptureBeforeImage(capture_bounds);
TestAccessibilityHighlightManager manager;
manager.HighlightCaret(true);
MockTextInputClient text_input_client;
text_input_client.SetCaretBounds(caret_bounds);
manager.OnCaretBoundsChanged(&text_input_client);
CaptureAfterImage(capture_bounds);
ComputeImageStats();
// This is a smoke test to assert that something is drawn in the right
// part of the screen of approximately the right size and color.
// There's deliberately some tolerance for tiny errors.
EXPECT_NEAR(1487, diff_count(), 50);
EXPECT_NEAR(175, SkColorGetR(average_diff_color()), 5);
EXPECT_NEAR(175, SkColorGetG(average_diff_color()), 5);
EXPECT_NEAR(255, SkColorGetB(average_diff_color()), 5);
}
IN_PROC_BROWSER_TEST_F(AccessibilityHighlightManagerTest,
TestCursorRingDrawsRedPixels) {
gfx::Rect capture_bounds(200, 300, 100, 100);
gfx::Point cursor_point(250, 350);
CaptureBeforeImage(capture_bounds);
TestAccessibilityHighlightManager manager;
manager.HighlightCursor(true);
ui::MouseEvent mouse_move(ui::ET_MOUSE_MOVED, cursor_point, cursor_point,
ui::EventTimeForNow(), 0, 0);
manager.OnMouseEvent(&mouse_move);
CaptureAfterImage(capture_bounds);
ComputeImageStats();
// This is a smoke test to assert that something is drawn in the right
// part of the screen of approximately the right size and color.
// There's deliberately some tolerance for tiny errors.
EXPECT_NEAR(1521, diff_count(), 50);
EXPECT_NEAR(255, SkColorGetR(average_diff_color()), 5);
EXPECT_NEAR(176, SkColorGetG(average_diff_color()), 5);
EXPECT_NEAR(176, SkColorGetB(average_diff_color()), 5);
}
IN_PROC_BROWSER_TEST_F(AccessibilityHighlightManagerTest,
TestFocusRingDrawsOrangePixels) {
gfx::Rect capture_bounds(200, 300, 100, 100);
gfx::Rect focus_bounds(230, 330, 40, 40);
CaptureBeforeImage(capture_bounds);
TestAccessibilityHighlightManager manager;
manager.HighlightFocus(true);
content::FocusedNodeDetails details{false, focus_bounds};
manager.Observe(content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE,
content::Source<AccessibilityHighlightManagerTest>(this),
content::Details<content::FocusedNodeDetails>(&details));
CaptureAfterImage(capture_bounds);
ComputeImageStats();
// This is a smoke test to assert that something is drawn in the right
// part of the screen of approximately the right size and color.
// There's deliberately some tolerance for tiny errors.
EXPECT_NEAR(1608, diff_count(), 50);
EXPECT_NEAR(255, SkColorGetR(average_diff_color()), 5);
EXPECT_NEAR(201, SkColorGetG(average_diff_color()), 5);
EXPECT_NEAR(152, SkColorGetB(average_diff_color()), 5);
}
} // namespace chromeos