// Copyright (c) 2010 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/ui/cocoa/table_row_nsimage_cache.h"

#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"

namespace {

class TestTable : public TableRowNSImageCache::Table {
 public:

  std::vector<SkBitmap>* rows() {
    return &rows_;
  }

  // TableRowNSImageCache::Table overrides.
  virtual int RowCount() const {
    return rows_.size();
  }
  virtual SkBitmap GetIcon(int index) const {
    return rows_[index];
  }

 private:
  std::vector<SkBitmap> rows_;
};

SkBitmap MakeImage(int width, int height) {
  SkBitmap image;
  image.setConfig(SkBitmap::kARGB_8888_Config, width, height);
  EXPECT_TRUE(image.allocPixels());
  image.eraseRGB(255, 0, 0);
  return image;
}

// Define this as a macro so that the original variable names can be used in
// test output.
#define COMPARE_SK_NS_IMG_SIZES(skia, cocoa) \
  EXPECT_EQ(skia.width(), [cocoa size].width); \
  EXPECT_EQ(skia.height(), [cocoa size].height);

TEST(TableRowNSImageCacheTest, ModelChanged) {
  TestTable table;
  std::vector<SkBitmap>* rows = table.rows();
  rows->push_back(MakeImage(10, 10));
  rows->push_back(MakeImage(20, 20));
  rows->push_back(MakeImage(30, 30));
  TableRowNSImageCache cache(&table);

  NSImage* image0 = cache.GetImageForRow(0);
  NSImage* image1 = cache.GetImageForRow(1);
  NSImage* image2 = cache.GetImageForRow(2);

  COMPARE_SK_NS_IMG_SIZES(rows->at(0), image0);
  COMPARE_SK_NS_IMG_SIZES(rows->at(1), image1);
  COMPARE_SK_NS_IMG_SIZES(rows->at(2), image2);

  rows->clear();

  rows->push_back(MakeImage(15, 15));
  rows->push_back(MakeImage(25, 25));
  rows->push_back(MakeImage(35, 35));
  rows->push_back(MakeImage(45, 45));

  // Invalidate the entire model.
  cache.OnModelChanged();

  EXPECT_NE(image0, cache.GetImageForRow(0));
  image0 = cache.GetImageForRow(0);

  EXPECT_NE(image1, cache.GetImageForRow(1));
  image1 = cache.GetImageForRow(1);

  EXPECT_NE(image2, cache.GetImageForRow(2));
  image2 = cache.GetImageForRow(2);

  NSImage* image3 = cache.GetImageForRow(3);

  COMPARE_SK_NS_IMG_SIZES(rows->at(0), image0);
  COMPARE_SK_NS_IMG_SIZES(rows->at(1), image1);
  COMPARE_SK_NS_IMG_SIZES(rows->at(2), image2);
  COMPARE_SK_NS_IMG_SIZES(rows->at(3), image3);
}


TEST(TableRowNSImageCacheTest, ItemsChanged) {
  TestTable table;
  std::vector<SkBitmap>* rows = table.rows();
  rows->push_back(MakeImage(10, 10));
  rows->push_back(MakeImage(20, 20));
  rows->push_back(MakeImage(30, 30));
  TableRowNSImageCache cache(&table);

  NSImage* image0 = cache.GetImageForRow(0);
  NSImage* image1 = cache.GetImageForRow(1);
  NSImage* image2 = cache.GetImageForRow(2);

  COMPARE_SK_NS_IMG_SIZES(rows->at(0), image0);
  COMPARE_SK_NS_IMG_SIZES(rows->at(1), image1);
  COMPARE_SK_NS_IMG_SIZES(rows->at(2), image2);

  // Update the middle image.
  (*rows)[1] = MakeImage(25, 25);
  cache.OnItemsChanged(/* start=*/1, /* count=*/1);

  // Make sure the other items remained the same.
  EXPECT_EQ(image0, cache.GetImageForRow(0));
  EXPECT_EQ(image2, cache.GetImageForRow(2));

  image1 = cache.GetImageForRow(1);
  COMPARE_SK_NS_IMG_SIZES(rows->at(1), image1);

  // Update more than one image.
  (*rows)[0] = MakeImage(15, 15);
  (*rows)[1] = MakeImage(27, 27);
  EXPECT_EQ(3U, rows->size());
  cache.OnItemsChanged(0, 2);

  image0 = cache.GetImageForRow(0);
  image1 = cache.GetImageForRow(1);

  COMPARE_SK_NS_IMG_SIZES(rows->at(0), image0);
  COMPARE_SK_NS_IMG_SIZES(rows->at(1), image1);
}


TEST(TableRowNSImageCacheTest, ItemsAdded) {
  TestTable table;
  std::vector<SkBitmap>* rows = table.rows();
  rows->push_back(MakeImage(10, 10));
  rows->push_back(MakeImage(20, 20));
  TableRowNSImageCache cache(&table);

  NSImage* image0 = cache.GetImageForRow(0);
  NSImage* image1 = cache.GetImageForRow(1);

  COMPARE_SK_NS_IMG_SIZES(rows->at(0), image0);
  COMPARE_SK_NS_IMG_SIZES(rows->at(1), image1);

  // Add an item to the end.
  rows->push_back(MakeImage(30, 30));
  cache.OnItemsAdded(2, 1);

  // Make sure image 1 stayed the same.
  EXPECT_EQ(image1, cache.GetImageForRow(1));
  COMPARE_SK_NS_IMG_SIZES(rows->at(1), image1);

  // Check that image 2 got added correctly.
  NSImage* image2 = cache.GetImageForRow(2);
  COMPARE_SK_NS_IMG_SIZES(rows->at(2), image2);

  // Add two items to the begging.
  rows->insert(rows->begin(), MakeImage(7, 7));
  rows->insert(rows->begin(), MakeImage(3, 3));
  cache.OnItemsAdded(0, 2);

  NSImage* image00 = cache.GetImageForRow(0);
  NSImage* image01 = cache.GetImageForRow(1);

  COMPARE_SK_NS_IMG_SIZES(rows->at(0), image00);
  COMPARE_SK_NS_IMG_SIZES(rows->at(1), image01);
}


TEST(TableRowNSImageCacheTest, ItemsRemoved) {
  TestTable table;
  std::vector<SkBitmap>* rows = table.rows();
  rows->push_back(MakeImage(10, 10));
  rows->push_back(MakeImage(20, 20));
  rows->push_back(MakeImage(30, 30));
  rows->push_back(MakeImage(40, 40));
  rows->push_back(MakeImage(50, 50));
  TableRowNSImageCache cache(&table);

  NSImage* image0 = cache.GetImageForRow(0);
  NSImage* image1 = cache.GetImageForRow(1);
  NSImage* image2 = cache.GetImageForRow(2);
  NSImage* image3 = cache.GetImageForRow(3);
  NSImage* image4 = cache.GetImageForRow(4);

  COMPARE_SK_NS_IMG_SIZES(rows->at(0), image0);
  COMPARE_SK_NS_IMG_SIZES(rows->at(1), image1);
  COMPARE_SK_NS_IMG_SIZES(rows->at(2), image2);
  COMPARE_SK_NS_IMG_SIZES(rows->at(3), image3);
  COMPARE_SK_NS_IMG_SIZES(rows->at(4), image4);

  rows->erase(rows->begin() + 1, rows->begin() + 4);  // [20x20, 50x50)
  cache.OnItemsRemoved(1, 3);

  image0 = cache.GetImageForRow(0);
  image1 = cache.GetImageForRow(1);

  COMPARE_SK_NS_IMG_SIZES(rows->at(0), image0);
  COMPARE_SK_NS_IMG_SIZES(rows->at(1), image1);
}

}  // namespace