text: // Copyright 2020 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 "ash/accessibility/switch_access/point_scan_controller.h"
  
  #include "ash/shell.h"
  #include "ash/test/ash_test_base.h"
  #include "ash/test/test_window_builder.h"
  #include "base/bind.h"
  #include "base/command_line.h"
  #include "base/macros.h"
  #include "base/test/scoped_feature_list.h"
  #include "third_party/skia/include/core/SkBitmap.h"
  #include "ui/aura/window.h"
  #include "ui/compositor/compositor_switches.h"
  #include "ui/gfx/image/image.h"
  #include "ui/snapshot/snapshot.h"
  
  namespace ash {
  
  class PointScanControllerTest : public AshTestBase {
   protected:
    PointScanControllerTest() = default;
    ~PointScanControllerTest() override = default;
  
    // AshTestBase:
    void SetUp() override {
      base::CommandLine::ForCurrentProcess()->AppendSwitch(
          ::switches::kEnablePixelOutputInTests);
      AshTestBase::SetUp();
    }
  
    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 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 transparent.
      while (true) {
        aura::Window* window = Shell::GetPrimaryRootWindow();
        base::RunLoop run_loop;
        ui::GrabWindowSnapshotAndScaleAsync(
            window, bounds, bounds.size(),
            base::BindOnce(
                [](base::RunLoop* run_loop, gfx::Image* image,
                   gfx::Image got_image) {
                  run_loop->Quit();
                  *image = got_image;
                },
                &run_loop, &image_));
        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;
        } else if (255 == SkColorGetA(bitmap.getColor(0, 0))) {
          LOG(INFO) << "Bitmap is transparent, trying to capture again";
          break;
        }
      }
    }
  
    void ComputeImageStats() {
      diff_count_ = 0;
      row_diff_count_ = 0;
      col_diff_count_ = 0;
      bool row_diff_tracker_[before_bmp_.height()];
      for (int i = 0; i < before_bmp_.height(); ++i) {
        row_diff_tracker_[i] = false;
      }
  
      for (int x = 0; x < before_bmp_.width(); ++x) {
        bool col_diff = false;
        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_++;
            col_diff = true;
            row_diff_tracker_[y] = true;
          }
        }
        if (col_diff)
          ++col_diff_count_;
      }
  
      for (int i = 0; i < before_bmp_.height(); ++i) {
        if (row_diff_tracker_[i])
          ++row_diff_count_;
      }
    }
  
    int diff_count() const { return diff_count_; }
    int row_diff_count() const { return row_diff_count_; }
    int col_diff_count() const { return col_diff_count_; }
  
   private:
    gfx::Image image_;
    SkBitmap before_bmp_;
    SkBitmap after_bmp_;
    int diff_count_ = 0;
    int row_diff_count_ = 0;
    int col_diff_count_ = 0;
  
    DISALLOW_COPY_AND_ASSIGN(PointScanControllerTest);
  };
  
  TEST_F(PointScanControllerTest, StartScanning) {
    gfx::Rect bounds = Shell::GetPrimaryRootWindow()->bounds();
  
    // Create a white background window for captured image color smoke test.
    std::unique_ptr<aura::Window> window =
        TestWindowBuilder()
            .SetColorWindowDelegate(SK_ColorWHITE)
            .SetBounds(bounds)
            .Build();
  
    // For some reason, the white window is not fully drawn the first time we call
    // CaptureBeforeImage.
    CaptureBeforeImage(bounds);
    CaptureBeforeImage(bounds);
  
    PointScanController controller;
    controller.StartHorizontalRangeScan();
  
    CaptureAfterImage(bounds);
    ComputeImageStats();
  }
  
  }  // namespace ash
  