blob: 226ac994dd05a8befda7d84adb0bdce053fcd9b7 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "pdf/pdf_ink_undo_redo_model.h"
#include <stddef.h>
#include <numeric>
#include <optional>
#include <set>
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::ElementsAreArray;
using testing::Optional;
namespace chrome_pdf {
using DiscardedDrawCommands = PdfInkUndoRedoModel::DiscardedDrawCommands;
using enum PdfInkUndoRedoModel::CommandsType;
namespace {
// Shorthand for test setup that is expected to succeed.
void DoDrawCommandsCycle(PdfInkUndoRedoModel& undo_redo,
const std::set<size_t>& ids) {
std::optional<DiscardedDrawCommands> discards = undo_redo.StartDraw();
ASSERT_THAT(discards, Optional(DiscardedDrawCommands()));
for (size_t id : ids) {
ASSERT_TRUE(undo_redo.Draw(id));
}
ASSERT_TRUE(undo_redo.FinishDraw());
}
TEST(PdfInkUndoRedoModelTest, BadActionDoubleStartDraw) {
PdfInkUndoRedoModel undo_redo;
std::optional<DiscardedDrawCommands> discards = undo_redo.StartDraw();
ASSERT_THAT(discards, Optional(DiscardedDrawCommands()));
ASSERT_FALSE(undo_redo.StartDraw());
}
TEST(PdfInkUndoRedoModelTest, BadActionSpuriousDraw) {
PdfInkUndoRedoModel undo_redo;
ASSERT_FALSE(undo_redo.Draw(1));
}
TEST(PdfInkUndoRedoModelTest, BadActionSpuriousFinishDraw) {
PdfInkUndoRedoModel undo_redo;
ASSERT_FALSE(undo_redo.FinishDraw());
}
TEST(PdfInkUndoRedoModelTest, BadActionEraseWhileDrawing) {
PdfInkUndoRedoModel undo_redo;
std::optional<DiscardedDrawCommands> discards = undo_redo.StartDraw();
ASSERT_THAT(discards, Optional(DiscardedDrawCommands()));
ASSERT_TRUE(undo_redo.Draw(1));
ASSERT_FALSE(undo_redo.StartErase());
ASSERT_FALSE(undo_redo.Erase(1));
ASSERT_FALSE(undo_redo.FinishErase());
}
TEST(PdfInkUndoRedoModelTest, BadActionDoubleStartErase) {
PdfInkUndoRedoModel undo_redo;
std::optional<DiscardedDrawCommands> discards = undo_redo.StartErase();
ASSERT_THAT(discards, Optional(DiscardedDrawCommands()));
ASSERT_FALSE(undo_redo.StartErase());
}
TEST(PdfInkUndoRedoModelTest, BadActionSpuriousErase) {
PdfInkUndoRedoModel undo_redo;
ASSERT_FALSE(undo_redo.Erase(1));
}
TEST(PdfInkUndoRedoModelTest, BadActionSpuriousFinishErase) {
PdfInkUndoRedoModel undo_redo;
ASSERT_FALSE(undo_redo.FinishErase());
}
TEST(PdfInkUndoRedoModelTest, BadActionDrawWhileErasing) {
PdfInkUndoRedoModel undo_redo;
DoDrawCommandsCycle(undo_redo, {1});
std::optional<DiscardedDrawCommands> discards = undo_redo.StartErase();
ASSERT_THAT(discards, Optional(DiscardedDrawCommands()));
ASSERT_FALSE(undo_redo.StartDraw());
ASSERT_FALSE(undo_redo.Draw(2));
ASSERT_FALSE(undo_redo.FinishDraw());
}
TEST(PdfInkUndoRedoModelTest, BadActionSpuriousDrawAfterUndo) {
PdfInkUndoRedoModel undo_redo;
DoDrawCommandsCycle(undo_redo, {4});
PdfInkUndoRedoModel::Commands commands = undo_redo.Undo();
ASSERT_EQ(kErase, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetEraseCommands(commands).value(),
ElementsAreArray({4}));
ASSERT_FALSE(undo_redo.Draw(1));
}
TEST(PdfInkUndoRedoModelTest, BadActionSpuriousFinishDrawAfterUndo) {
PdfInkUndoRedoModel undo_redo;
DoDrawCommandsCycle(undo_redo, {4});
PdfInkUndoRedoModel::Commands commands = undo_redo.Undo();
ASSERT_EQ(kErase, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetEraseCommands(commands).value(),
ElementsAreArray({4}));
ASSERT_FALSE(undo_redo.FinishDraw());
}
TEST(PdfInkUndoRedoModelTest, BadActionSpuriousEraseAfterUndo) {
PdfInkUndoRedoModel undo_redo;
DoDrawCommandsCycle(undo_redo, {4});
PdfInkUndoRedoModel::Commands commands = undo_redo.Undo();
ASSERT_EQ(kErase, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetEraseCommands(commands).value(),
ElementsAreArray({4}));
ASSERT_FALSE(undo_redo.Erase(4));
}
TEST(PdfInkUndoRedoModelTest, BadActionSpuriousFinishEraseAfterUndo) {
PdfInkUndoRedoModel undo_redo;
DoDrawCommandsCycle(undo_redo, {4});
PdfInkUndoRedoModel::Commands commands = undo_redo.Undo();
ASSERT_EQ(kErase, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetEraseCommands(commands).value(),
ElementsAreArray({4}));
ASSERT_FALSE(undo_redo.FinishErase());
}
TEST(PdfInkUndoRedoModelTest, BadActionEraseUnknownId) {
PdfInkUndoRedoModel undo_redo;
DoDrawCommandsCycle(undo_redo, {1});
std::optional<DiscardedDrawCommands> discards = undo_redo.StartErase();
ASSERT_THAT(discards, Optional(DiscardedDrawCommands()));
ASSERT_FALSE(undo_redo.Erase(3));
}
TEST(PdfInkUndoRedoModelTest, BadActionEraseTwice) {
PdfInkUndoRedoModel undo_redo;
DoDrawCommandsCycle(undo_redo, {0});
std::optional<DiscardedDrawCommands> discards = undo_redo.StartErase();
ASSERT_THAT(discards, Optional(DiscardedDrawCommands()));
ASSERT_TRUE(undo_redo.Erase(0));
ASSERT_FALSE(undo_redo.Erase(0));
}
TEST(PdfInkUndoRedoModelTest, Empty) {
PdfInkUndoRedoModel undo_redo;
PdfInkUndoRedoModel::Commands commands = undo_redo.Undo();
EXPECT_EQ(kNone, PdfInkUndoRedoModel::GetCommandsType(commands));
commands = undo_redo.Undo();
EXPECT_EQ(kNone, PdfInkUndoRedoModel::GetCommandsType(commands));
commands = undo_redo.Redo();
EXPECT_EQ(kNone, PdfInkUndoRedoModel::GetCommandsType(commands));
commands = undo_redo.Redo();
EXPECT_EQ(kNone, PdfInkUndoRedoModel::GetCommandsType(commands));
}
TEST(PdfInkUndoRedoModelTest, EmptyDraw) {
PdfInkUndoRedoModel undo_redo;
DoDrawCommandsCycle(undo_redo, {});
PdfInkUndoRedoModel::Commands commands = undo_redo.Undo();
EXPECT_EQ(kNone, PdfInkUndoRedoModel::GetCommandsType(commands));
commands = undo_redo.Redo();
EXPECT_EQ(kNone, PdfInkUndoRedoModel::GetCommandsType(commands));
}
TEST(PdfInkUndoRedoModelTest, EmptyErase) {
PdfInkUndoRedoModel undo_redo;
std::optional<DiscardedDrawCommands> discards = undo_redo.StartErase();
ASSERT_THAT(discards, Optional(DiscardedDrawCommands()));
ASSERT_TRUE(undo_redo.FinishErase());
PdfInkUndoRedoModel::Commands commands = undo_redo.Undo();
EXPECT_EQ(kNone, PdfInkUndoRedoModel::GetCommandsType(commands));
commands = undo_redo.Redo();
EXPECT_EQ(kNone, PdfInkUndoRedoModel::GetCommandsType(commands));
}
TEST(PdfInkUndoRedoModelTest, DrawCannotRepeatId) {
PdfInkUndoRedoModel undo_redo;
DoDrawCommandsCycle(undo_redo, {1, 2, 3});
std::optional<DiscardedDrawCommands> discards = undo_redo.StartDraw();
ASSERT_THAT(discards, Optional(DiscardedDrawCommands()));
ASSERT_FALSE(undo_redo.Draw(1));
ASSERT_FALSE(undo_redo.Draw(3));
ASSERT_TRUE(undo_redo.Draw(97));
ASSERT_TRUE(undo_redo.Draw(99));
ASSERT_TRUE(undo_redo.Draw(98));
ASSERT_FALSE(undo_redo.Draw(1));
ASSERT_FALSE(undo_redo.Draw(98));
}
TEST(PdfInkUndoRedoModelTest, DrawCanRepeatIdAfterUndo) {
PdfInkUndoRedoModel undo_redo;
DoDrawCommandsCycle(undo_redo, {1, 2, 3});
DoDrawCommandsCycle(undo_redo, {97, 98, 99});
PdfInkUndoRedoModel::Commands commands = undo_redo.Undo();
ASSERT_EQ(kErase, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetEraseCommands(commands).value(),
ElementsAreArray({97, 98, 99}));
commands = undo_redo.Undo();
ASSERT_EQ(kErase, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetEraseCommands(commands).value(),
ElementsAreArray({1, 2, 3}));
std::optional<DiscardedDrawCommands> discards = undo_redo.StartDraw();
ASSERT_THAT(discards, Optional(DiscardedDrawCommands({1, 2, 3, 97, 98, 99})));
ASSERT_TRUE(undo_redo.Draw(2));
ASSERT_TRUE(undo_redo.Draw(98));
}
TEST(PdfInkUndoRedoModelTest, DrawUndoRedo) {
PdfInkUndoRedoModel undo_redo;
DoDrawCommandsCycle(undo_redo, {1, 2, 3});
PdfInkUndoRedoModel::Commands commands = undo_redo.Undo();
ASSERT_EQ(kErase, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetEraseCommands(commands).value(),
ElementsAreArray({1, 2, 3}));
commands = undo_redo.Undo();
EXPECT_EQ(kNone, PdfInkUndoRedoModel::GetCommandsType(commands));
commands = undo_redo.Redo();
ASSERT_EQ(kDraw, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetDrawCommands(commands).value(),
ElementsAreArray({1, 2, 3}));
commands = undo_redo.Redo();
EXPECT_EQ(kNone, PdfInkUndoRedoModel::GetCommandsType(commands));
}
TEST(PdfInkUndoRedoModelTest, DrawDrawEraseUndoRedo) {
PdfInkUndoRedoModel undo_redo;
DoDrawCommandsCycle(undo_redo, {1, 2, 3});
DoDrawCommandsCycle(undo_redo, {4});
std::optional<DiscardedDrawCommands> discards = undo_redo.StartErase();
ASSERT_THAT(discards, Optional(DiscardedDrawCommands()));
ASSERT_TRUE(undo_redo.Erase(1));
ASSERT_TRUE(undo_redo.Erase(4));
ASSERT_TRUE(undo_redo.FinishErase());
PdfInkUndoRedoModel::Commands commands = undo_redo.Undo();
ASSERT_EQ(kDraw, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetDrawCommands(commands).value(),
ElementsAreArray({1, 4}));
commands = undo_redo.Undo();
ASSERT_EQ(kErase, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetEraseCommands(commands).value(),
ElementsAreArray({4}));
commands = undo_redo.Undo();
ASSERT_EQ(kErase, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetEraseCommands(commands).value(),
ElementsAreArray({1, 2, 3}));
commands = undo_redo.Redo();
ASSERT_EQ(kDraw, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetDrawCommands(commands).value(),
ElementsAreArray({1, 2, 3}));
commands = undo_redo.Redo();
ASSERT_EQ(kDraw, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetDrawCommands(commands).value(),
ElementsAreArray({4}));
commands = undo_redo.Undo();
ASSERT_EQ(kErase, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetEraseCommands(commands).value(),
ElementsAreArray({4}));
commands = undo_redo.Redo();
ASSERT_EQ(kDraw, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetDrawCommands(commands).value(),
ElementsAreArray({4}));
commands = undo_redo.Redo();
ASSERT_EQ(kErase, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetEraseCommands(commands).value(),
ElementsAreArray({1, 4}));
}
TEST(PdfInkUndoRedoModelTest, DrawDrawUndoEraseUndo) {
PdfInkUndoRedoModel undo_redo;
DoDrawCommandsCycle(undo_redo, {5});
DoDrawCommandsCycle(undo_redo, {4, 8});
PdfInkUndoRedoModel::Commands commands = undo_redo.Undo();
ASSERT_EQ(kErase, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetEraseCommands(commands).value(),
ElementsAreArray({4, 8}));
std::optional<DiscardedDrawCommands> discards = undo_redo.StartErase();
ASSERT_THAT(discards, Optional(ElementsAreArray({4, 8})));
ASSERT_TRUE(undo_redo.Erase(5));
ASSERT_TRUE(undo_redo.FinishErase());
commands = undo_redo.Undo();
ASSERT_EQ(kDraw, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetDrawCommands(commands).value(),
ElementsAreArray({5}));
}
// TODO(crbug.com/335521182): Figure out why this times out on bots and enable.
TEST(PdfInkUndoRedoModelTest, DISABLED_Stress) {
constexpr size_t kCycles = 10000;
PdfInkUndoRedoModel undo_redo;
size_t id = 0;
for (size_t i = 0; i < kCycles; ++i) {
DoDrawCommandsCycle(undo_redo, {id, id + 1});
id += 2;
}
ASSERT_EQ(2 * kCycles, id);
ASSERT_TRUE(undo_redo.StartErase());
for (size_t i = 0; i < kCycles; ++i) {
std::optional<DiscardedDrawCommands> discards = undo_redo.StartErase();
ASSERT_THAT(discards, Optional(DiscardedDrawCommands()));
ASSERT_TRUE(undo_redo.Erase(--id));
ASSERT_TRUE(undo_redo.Erase(--id));
ASSERT_TRUE(undo_redo.FinishErase());
}
ASSERT_EQ(0u, id);
for (size_t i = 0; i < kCycles; ++i) {
PdfInkUndoRedoModel::Commands commands = undo_redo.Undo();
ASSERT_EQ(kDraw, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetDrawCommands(commands).value(),
ElementsAreArray({id, id + 1}));
id += 2;
}
ASSERT_EQ(2 * kCycles, id);
for (size_t i = 0; i < kCycles; ++i) {
id -= 2;
PdfInkUndoRedoModel::Commands commands = undo_redo.Undo();
ASSERT_EQ(kErase, PdfInkUndoRedoModel::GetCommandsType(commands));
EXPECT_THAT(PdfInkUndoRedoModel::GetEraseCommands(commands).value(),
ElementsAreArray({id, id + 1}));
}
std::vector<size_t> expected_discards(kCycles * 2);
std::iota(expected_discards.begin(), expected_discards.end(), 0);
std::optional<DiscardedDrawCommands> discards = undo_redo.StartDraw();
ASSERT_THAT(discards, Optional(ElementsAreArray(expected_discards)));
ASSERT_TRUE(undo_redo.Draw(0));
ASSERT_TRUE(undo_redo.FinishDraw());
}
} // namespace
} // namespace chrome_pdf