1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "page.h"
18 
19 #include <android-base/file.h>
20 #include <gtest/gtest.h>
21 
22 #include <memory>
23 #include <string>
24 
25 // Goes first due to conflicts.
26 #include "document.h"
27 #include "rect.h"
28 // #include "file/base/path.h"
29 #include "cpp/fpdf_scopers.h"
30 #include "fpdfview.h"
31 
32 namespace {
33 
34 using ::pdfClient::Document;
35 using ::pdfClient::Page;
36 using ::pdfClient::Rectangle_i;
37 
38 static const std::string kTestdata = "testdata";
39 static const std::string kSekretNoPassword = "sekret_no_password.pdf";
40 
GetTestDataDir()41 std::string GetTestDataDir() {
42     return android::base::GetExecutableDirectory();
43 }
44 
GetTestFile(std::string filename)45 std::string GetTestFile(std::string filename) {
46     return GetTestDataDir() + "/" + kTestdata + "/" + filename;
47 }
48 
LoadTestDocument(const std::string filename)49 ScopedFPDFDocument LoadTestDocument(const std::string filename) {
50     return ScopedFPDFDocument(FPDF_LoadDocument(GetTestFile(filename).c_str(), nullptr));
51 }
52 
53 // Note on coordinates used in below tests:
54 // This document has height == 792. Due to constraints of rect.h functions
55 // that require top < bottom, top/bottom are flipped from what page
56 // coordinates normally would be in these examples. So expected values when
57 // we consume the rectangles in this test are: top = (792 - bottom),
58 // bottom = (792 - top).
59 
60 /*
61  * Test that when a single rectangle is passed to NotifyInvalidRect
62  * invalid_rect_ will match its coordinates.
63  */
TEST(Test,NotifyInvalidRectSingleRectTest)64 TEST(Test, NotifyInvalidRectSingleRectTest) {
65     Document doc(LoadTestDocument(kSekretNoPassword), false);
66 
67     std::shared_ptr<Page> page = doc.GetPage(0);
68     EXPECT_FALSE(page->HasInvalidRect());
69     page->NotifyInvalidRect(pdfClient::IntRect(100, 100, 200, 200));
70 
71     EXPECT_TRUE(page->HasInvalidRect());
72     Rectangle_i expected = Rectangle_i{100, 592, 200, 692};
73     ASSERT_EQ(expected, page->ConsumeInvalidRect());
74 }
75 
76 /*
77  * Tests the coalescing of rectangles. Result should be the minimal rectangle
78  * that covers all rectangles that have been added.
79  */
TEST(Test,NotifyInvalidRectCoalesceTest)80 TEST(Test, NotifyInvalidRectCoalesceTest) {
81     Document doc(LoadTestDocument(kSekretNoPassword), false);
82 
83     std::shared_ptr<Page> page = doc.GetPage(0);
84     EXPECT_FALSE(page->HasInvalidRect());
85 
86     page->NotifyInvalidRect(pdfClient::IntRect(100, 100, 200, 200));
87     page->NotifyInvalidRect(pdfClient::IntRect(400, 100, 500, 200));
88     page->NotifyInvalidRect(pdfClient::IntRect(100, 400, 200, 500));
89     EXPECT_TRUE(page->HasInvalidRect());
90     Rectangle_i expected = Rectangle_i{100, 292, 500, 692};
91     ASSERT_EQ(expected, page->ConsumeInvalidRect());
92 }
93 
94 /*
95  * Tests adding a rectangle to invalid_rect_ whose area is already covered by
96  * the existing rect. Should not change boundaries.
97  */
TEST(Test,NotifyInvalidRectAlreadyCoveredTest)98 TEST(Test, NotifyInvalidRectAlreadyCoveredTest) {
99     Document doc(LoadTestDocument(kSekretNoPassword), false);
100 
101     std::shared_ptr<Page> page = doc.GetPage(0);
102     EXPECT_FALSE(page->HasInvalidRect());
103 
104     page->NotifyInvalidRect(pdfClient::IntRect(100, 100, 200, 200));
105     page->NotifyInvalidRect(pdfClient::IntRect(400, 100, 500, 200));
106     page->NotifyInvalidRect(pdfClient::IntRect(100, 400, 200, 500));
107     // add a rectangle that's already covered by existing one
108     page->NotifyInvalidRect(pdfClient::IntRect(400, 400, 500, 500));
109     EXPECT_TRUE(page->HasInvalidRect());
110     Rectangle_i expected = Rectangle_i{100, 292, 500, 692};
111     ASSERT_EQ(expected, page->ConsumeInvalidRect());
112 }
113 
114 /**
115  * Try calling NotifyInvalidRect with negative indices. No error should be
116  * thrown. Confirm all rectangles have been ignored by the page.
117  */
TEST(Test,NotifyInvalidRectNegativeIndicesTest)118 TEST(Test, NotifyInvalidRectNegativeIndicesTest) {
119     Document doc(LoadTestDocument(kSekretNoPassword), false);
120     std::shared_ptr<Page> page = doc.GetPage(0);
121 
122     page->NotifyInvalidRect(pdfClient::IntRect(-100, 100, 200, 200));
123     page->NotifyInvalidRect(pdfClient::IntRect(400, -100, 500, 200));
124     page->NotifyInvalidRect(pdfClient::IntRect(100, 400, -200, 500));
125     page->NotifyInvalidRect(pdfClient::IntRect(400, 400, 500, -500));
126     EXPECT_FALSE(page->HasInvalidRect());
127 }
128 
129 /**
130  * Try calling NotifyInvalidRect with empty rectangles. No error should be
131  * thrown. Confirm all rectangles have been ignored by the page.
132  */
TEST(Test,NotifyInvalidRectEmptyRectanglesTest)133 TEST(Test, NotifyInvalidRectEmptyRectanglesTest) {
134     Document doc(LoadTestDocument(kSekretNoPassword), false);
135     std::shared_ptr<Page> page = doc.GetPage(0);
136 
137     page->NotifyInvalidRect(pdfClient::IntRect(100, 200, 100, 500));
138     page->NotifyInvalidRect(pdfClient::IntRect(100, 400, 500, 400));
139     page->NotifyInvalidRect(pdfClient::Rectangle_i{100, 200, 0, 500});
140     page->NotifyInvalidRect(pdfClient::Rectangle_i{100, 400, 500, 0});
141     EXPECT_FALSE(page->HasInvalidRect());
142 }
143 
144 /**
145  * Test that calling ConsumeInvalidRect resets the rectangle in the Page.
146  */
TEST(Test,ConsumeInvalidRectResetsRectTest)147 TEST(Test, ConsumeInvalidRectResetsRectTest) {
148     Document doc(LoadTestDocument(kSekretNoPassword), false);
149     std::shared_ptr<Page> page = doc.GetPage(0);
150 
151     // doesn't have one
152     EXPECT_FALSE(page->HasInvalidRect());
153     page->NotifyInvalidRect(pdfClient::IntRect(100, 100, 200, 200));
154 
155     // now has one
156     page->NotifyInvalidRect(pdfClient::IntRect(100, 100, 200, 200));
157     EXPECT_TRUE(page->HasInvalidRect());
158 
159     // no longer has one
160     page->ConsumeInvalidRect();
161     EXPECT_FALSE(page->HasInvalidRect());
162 
163     // if we call Consume anyway we will receive empty rect
164     Rectangle_i expected = Rectangle_i{0, 0, 0, 0};
165     ASSERT_EQ(expected, page->ConsumeInvalidRect());
166 }
167 
168 }  // namespace