// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/icon_transcoder/svg_icon_transcoder.h"

#include <memory>
#include <string>

#include "base/files/file_util.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/test/browser_test.h"

constexpr char kSvgData[] =
    "<svg width='20px' height='20px' viewBox='0 0 24 24' fill='rgb(95,99,104)' "
    "xmlns='http://www.w3.org/2000/svg'><path d='M0 0h24v24H0V0z' "
    "fill='none'/><path d='M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 "
    "2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 "
    "6.41V10h2V3h-7z'/></svg>";

constexpr char kInvalidSvgData[] = "<svg garbled not really useful>bad</svg>";
constexpr char kGarbageData[] = "this is not even svg-like";
constexpr gfx::Size kSize(48, 48);

class SvgIconTranscoderTest : public InProcessBrowserTest {
 public:
  void SetUpInProcessBrowserTestFixture() override {
    InProcessBrowserTest::SetUpInProcessBrowserTestFixture();
    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    svg_path_ = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("icon.svg"));
    png_path_ = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("icon.png"));
  }

  void TearDownOnMainThread() override {
    svg_icon_transcoder_.reset();
    InProcessBrowserTest::TearDownOnMainThread();
  }

  apps::SvgIconTranscoder* svg_icon_transcoder() {
    if (!svg_icon_transcoder_) {
      svg_icon_transcoder_ =
          std::make_unique<apps::SvgIconTranscoder>(browser()->GetProfile());
    }
    return svg_icon_transcoder_.get();
  }

  void ExpectTranscodeSuccess(base::OnceClosure done_closure,
                              std::string icon_data) {
    EXPECT_FALSE(icon_data.empty());
    compressed_icon_data_ = std::move(icon_data);
    std::move(done_closure).Run();
  }

  void ExpectTranscodeFailure(base::OnceClosure done_closure,
                              std::string icon_data) {
    EXPECT_TRUE(icon_data.empty());
    compressed_icon_data_ = std::move(icon_data);
    std::move(done_closure).Run();
  }

  void ExpectSavedIcon(bool saved) {
    base::ScopedAllowBlockingForTesting allow_blocking;
    EXPECT_EQ(base::PathExists(png_path()), saved);
    if (saved) {
      std::string saved_data;
      ASSERT_TRUE(base::ReadFileToString(png_path(), &saved_data));
      EXPECT_EQ(saved_data, compressed_icon_data_);
    }
  }

  void WriteIconData(const base::FilePath& path,
                     const std::string& icon_data) const {
    base::RunLoop run_loop;
    base::ThreadPool::PostTaskAndReply(
        FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
        base::BindOnce(
            [](const base::FilePath& path, const std::string& icon_data) {
              EXPECT_TRUE(base::WriteFile(path, icon_data));
            },
            path, icon_data),
        run_loop.QuitClosure());
    run_loop.Run();
  }

  const base::FilePath png_path() const { return png_path_; }
  const base::FilePath svg_path() const { return svg_path_; }

 protected:
  std::string compressed_icon_data_;
  std::unique_ptr<apps::SvgIconTranscoder> svg_icon_transcoder_;
  base::FilePath svg_path_;
  base::FilePath png_path_;
  base::ScopedTempDir temp_dir_;
};

IN_PROC_BROWSER_TEST_F(SvgIconTranscoderTest, TranscodeFromDataSuccessNoSave) {
  base::RunLoop run_loop;
  svg_icon_transcoder()->Transcode(
      std::string(kSvgData), base::FilePath(), kSize,
      base::BindOnce(&SvgIconTranscoderTest::ExpectTranscodeSuccess,
                     base::Unretained(this), run_loop.QuitClosure()));
  run_loop.Run();
  ExpectSavedIcon(false);
}

IN_PROC_BROWSER_TEST_F(SvgIconTranscoderTest, TranscodeFromDataSuccessAndSave) {
  base::RunLoop run_loop;
  svg_icon_transcoder()->Transcode(
      std::string(kSvgData), png_path(), kSize,
      base::BindOnce(&SvgIconTranscoderTest::ExpectTranscodeSuccess,
                     base::Unretained(this), run_loop.QuitClosure()));
  run_loop.Run();
  ExpectSavedIcon(true);
}

IN_PROC_BROWSER_TEST_F(SvgIconTranscoderTest, TranscodeFromInvalid) {
  base::RunLoop run_loop;
  svg_icon_transcoder()->Transcode(
      std::string(kInvalidSvgData), base::FilePath(), kSize,
      base::BindOnce(&SvgIconTranscoderTest::ExpectTranscodeFailure,
                     base::Unretained(this), run_loop.QuitClosure()));
  run_loop.Run();
  ExpectSavedIcon(false);
}

IN_PROC_BROWSER_TEST_F(SvgIconTranscoderTest, TranscodeFromGarbage) {
  base::RunLoop run_loop;
  svg_icon_transcoder()->Transcode(
      std::string(kGarbageData), base::FilePath(), kSize,
      base::BindOnce(&SvgIconTranscoderTest::ExpectTranscodeFailure,
                     base::Unretained(this), run_loop.QuitClosure()));
  run_loop.Run();
  ExpectSavedIcon(false);
}

IN_PROC_BROWSER_TEST_F(SvgIconTranscoderTest, TranscodeFromFileSuccessNoSave) {
  base::RunLoop run_loop;
  WriteIconData(svg_path_, kSvgData);

  svg_icon_transcoder()->Transcode(
      svg_path(), base::FilePath(), kSize,
      base::BindOnce(&SvgIconTranscoderTest::ExpectTranscodeSuccess,
                     base::Unretained(this), run_loop.QuitClosure()));
  run_loop.Run();
  ExpectSavedIcon(false);
}

IN_PROC_BROWSER_TEST_F(SvgIconTranscoderTest, TranscodeFromFileSuccessAndSave) {
  base::RunLoop run_loop;
  WriteIconData(svg_path_, kSvgData);

  svg_icon_transcoder()->Transcode(
      svg_path(), png_path(), kSize,
      base::BindOnce(&SvgIconTranscoderTest::ExpectTranscodeSuccess,
                     base::Unretained(this), run_loop.QuitClosure()));
  run_loop.Run();
  ExpectSavedIcon(true);
}
