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 "document.h"
18 
19 #include <android-base/file.h>
20 #include <android-base/logging.h>
21 #include <fcntl.h>
22 #include <gtest/gtest.h>
23 #include <stddef.h>
24 
25 #include <algorithm>
26 #include <cstring>
27 #include <memory>
28 #include <string>
29 #include <utility>
30 
31 #include "file.h"
32 #include "fpdfview.h"
33 #include "linux_fileops.h"
34 #include "logging.h"
35 #include "page.h"
36 
37 using pdfClient::Document;
38 using pdfClient::FileReader;
39 using pdfClient::LinuxFileOps;
40 using pdfClient::Page;
41 using std::string_view;
42 
43 namespace {
44 
45 #define LOG_TAG "pdf/apk/jni/pdfClient/document_test.cc"
46 
47 const std::string kTestdata = "testdata";
48 const std::string kSekretNoPassword = "sekret_no_password.pdf";
49 const std::string kSecretWithPassword = "sekret_password_banana.pdf";
50 const std::string kPassword = "banana";
51 
GetTestDataDir()52 std::string GetTestDataDir() {
53     return android::base::GetExecutableDirectory();
54 }
55 
GetTestFile(std::string filename)56 std::string GetTestFile(std::string filename) {
57     return GetTestDataDir() + "/" + kTestdata + "/" + filename;
58 }
59 
GetTempFile(std::string filename)60 std::string GetTempFile(std::string filename) {
61     return GetTestDataDir() + "/" + filename;
62 }
63 
LoadDocument(string_view path,const char * password=nullptr)64 std::unique_ptr<Document> LoadDocument(string_view path, const char* password = nullptr) {
65     LinuxFileOps::FDCloser fd(open(path.data(), O_RDONLY));
66     CHECK_GT(fd.get(), 0);
67     std::unique_ptr<Document> document;
68     CHECK_EQ(pdfClient::LOADED, Document::Load(std::make_unique<FileReader>(std::move(fd)), password,
69                                                /* closeFdOnFailure= */ true, &document))
70             << "could not load " << path << " with password " << (password ? password : "nullptr");
71     return document;
72 }
73 
compareDocuments(const std::shared_ptr<Page> page_orig,const std::shared_ptr<Page> page_copied)74 void compareDocuments(const std::shared_ptr<Page> page_orig,
75                       const std::shared_ptr<Page> page_copied) {
76     static constexpr int kMaxDimension = 1024;
77     CHECK_GT(page_orig->Width(), 0) << "0 page width";
78     CHECK_GT(page_orig->Height(), 0) << "0 page height";
79     const float scale_orig =
80             static_cast<float>(kMaxDimension) / std::max(page_orig->Width(), page_orig->Height());
81     size_t width_orig = static_cast<size_t>(page_orig->Width() * scale_orig);
82     size_t height_orig = static_cast<size_t>(page_orig->Height() * scale_orig);
83 
84     CHECK_GT(page_copied->Width(), 0) << "0 page width";
85     CHECK_GT(page_copied->Height(), 0) << "0 page height";
86     const float scale_copied = static_cast<float>(kMaxDimension) /
87                                std::max(page_copied->Width(), page_copied->Height());
88     size_t width_copied = static_cast<size_t>(page_copied->Width() * scale_copied);
89     size_t height_copied = static_cast<size_t>(page_copied->Height() * scale_copied);
90 
91     ASSERT_EQ(width_orig, width_copied);
92     ASSERT_EQ(height_orig, height_copied);
93     ASSERT_EQ(scale_orig, scale_copied);
94 }
95 
loadDocumentWithoutPassword(std::string fpath)96 void loadDocumentWithoutPassword(std::string fpath) {
97     // Expect to fail for lack of password.
98     LinuxFileOps::FDCloser in(open(fpath.c_str(), O_RDONLY));
99     ASSERT_GT(in.get(), 0);
100     std::unique_ptr<Document> should_fail;
101     auto fr = std::make_unique<FileReader>(std::move(in));
102     CHECK_EQ(pdfClient::REQUIRES_PASSWORD,
103              Document::Load(std::move(fr), nullptr, /* closeFdOnFailure= */ true, &should_fail))
104             << "should not have been able to load copy of " << kSecretWithPassword
105             << " without password";
106 }
107 
TEST(Test,CloneWithoutEncryption)108 TEST(Test, CloneWithoutEncryption) {
109     std::unique_ptr<Document> doc =
110             LoadDocument(GetTestFile(kSecretWithPassword), kPassword.c_str());
111     std::string cloned_path = GetTempFile("cloned.pdf");
112     LinuxFileOps::FDCloser out(open(cloned_path.c_str(), O_RDWR | O_CREAT | O_APPEND, 0600));
113     ASSERT_GT(out.get(), 0);
114     ASSERT_TRUE(doc->CloneDocumentWithoutSecurity(std::move(out)));
115     std::unique_ptr<Document> cloned = LoadDocument(cloned_path);
116     compareDocuments(doc->GetPage(0), cloned->GetPage(0));
117 }
118 
TEST(Test,SaveAs)119 TEST(Test, SaveAs) {
120     std::unique_ptr<Document> doc_orig =
121             LoadDocument(GetTestFile(kSecretWithPassword), kPassword.c_str());
122     std::string copied_path = GetTempFile("copied.pdf");
123     LinuxFileOps::FDCloser out(open(copied_path.c_str(), O_RDWR | O_CREAT | O_APPEND, 0600));
124     ASSERT_GT(out.get(), 0);
125     ASSERT_TRUE(doc_orig->SaveAs(std::move(out)));
126     loadDocumentWithoutPassword(copied_path);
127     // Should load with same password.
128     std::unique_ptr<Document> copied = LoadDocument(copied_path, kPassword.c_str());
129     compareDocuments(doc_orig->GetPage(0), copied->GetPage(0));
130 }
131 
132 /*
133  * Tests the retention of std::shared_ptr<Page> as requested.
134  */
TEST(Test,GetPageTest)135 TEST(Test, GetPageTest) {
136     std::unique_ptr<Document> doc = LoadDocument(GetTestFile(kSekretNoPassword), nullptr);
137     // retain == false so should be a new copy each time
138     std::shared_ptr<Page> page_zero_copy_one = doc->GetPage(0);
139     std::shared_ptr<Page> page_zero_copy_two = doc->GetPage(0);
140     EXPECT_NE(page_zero_copy_one, page_zero_copy_two);
141 
142     // retain == true so should get the same ptr
143     std::shared_ptr<Page> page_zero_copy_three = doc->GetPage(0, true);
144     std::shared_ptr<Page> page_zero_copy_four = doc->GetPage(0, true);
145     EXPECT_EQ(page_zero_copy_three, page_zero_copy_four);
146 
147     // since it's already retained, shouldn't matter if we request with
148     // retain == false, should still get same one
149     std::shared_ptr<Page> page_zero_copy_five = doc->GetPage(0);
150     EXPECT_EQ(page_zero_copy_four, page_zero_copy_five);
151 }
152 
153 }  // namespace
154 
main(int argc,char ** argv)155 int main(int argc, char** argv) {
156     ::testing::InitGoogleTest(&argc, argv);
157     FPDF_InitLibrary();
158     int status = RUN_ALL_TESTS();
159     // Destroy the library to keep the memory leak checker happy.
160     FPDF_DestroyLibrary();
161     return status;
162 }
163