summaryrefslogtreecommitdiff
path: root/core/fpdfapi/parser/cpdf_object_avail_unittest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/fpdfapi/parser/cpdf_object_avail_unittest.cpp')
-rw-r--r--core/fpdfapi/parser/cpdf_object_avail_unittest.cpp363
1 files changed, 363 insertions, 0 deletions
diff --git a/core/fpdfapi/parser/cpdf_object_avail_unittest.cpp b/core/fpdfapi/parser/cpdf_object_avail_unittest.cpp
new file mode 100644
index 0000000000..cb9ceac101
--- /dev/null
+++ b/core/fpdfapi/parser/cpdf_object_avail_unittest.cpp
@@ -0,0 +1,363 @@
+// Copyright 2017 PDFium 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 "core/fpdfapi/parser/cpdf_object_avail.h"
+
+#include <map>
+#include <utility>
+
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
+#include "core/fpdfapi/parser/cpdf_read_validator.h"
+#include "core/fpdfapi/parser/cpdf_reference.h"
+#include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fxcrt/fx_stream.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
+
+namespace {
+
+class InvalidReader : public IFX_SeekableReadStream {
+ public:
+ template <typename T, typename... Args>
+ friend CFX_RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+ // IFX_SeekableReadStream overrides:
+ bool ReadBlock(void* buffer, FX_FILESIZE offset, size_t size) override {
+ return false;
+ }
+ FX_FILESIZE GetSize() override { return 100; }
+
+ private:
+ InvalidReader() {}
+ ~InvalidReader() override {}
+};
+
+class TestReadValidator : public CPDF_ReadValidator {
+ public:
+ template <typename T, typename... Args>
+ friend CFX_RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+
+ void SimulateReadError() { ReadBlock(nullptr, 0, 1); }
+
+ protected:
+ TestReadValidator()
+ : CPDF_ReadValidator(pdfium::MakeRetain<InvalidReader>(), nullptr) {}
+ ~TestReadValidator() override {}
+};
+
+class TestHolder : public CPDF_IndirectObjectHolder {
+ public:
+ enum class ObjectState {
+ Unavailable,
+ Available,
+ };
+ TestHolder() : validator_(pdfium::MakeRetain<TestReadValidator>()) {}
+ ~TestHolder() override {}
+
+ // CPDF_IndirectObjectHolder overrides:
+ CPDF_Object* GetOrParseIndirectObject(uint32_t objnum) override {
+ auto it = objects_data_.find(objnum);
+ if (it == objects_data_.end())
+ return nullptr;
+
+ ObjectData& obj_data = it->second;
+ if (obj_data.state == ObjectState::Unavailable) {
+ validator_->SimulateReadError();
+ return nullptr;
+ }
+ return obj_data.object.get();
+ }
+
+ CFX_RetainPtr<CPDF_ReadValidator> GetValidator() { return validator_; }
+
+ void AddObject(uint32_t objnum,
+ std::unique_ptr<CPDF_Object> object,
+ ObjectState state) {
+ ObjectData object_data;
+ object_data.object = std::move(object);
+ object_data.state = state;
+ ASSERT(objects_data_.find(objnum) == objects_data_.end());
+ objects_data_[objnum] = std::move(object_data);
+ }
+
+ void SetObjectState(uint32_t objnum, ObjectState state) {
+ auto it = objects_data_.find(objnum);
+ ASSERT(it != objects_data_.end());
+ ObjectData& obj_data = it->second;
+ obj_data.state = state;
+ }
+
+ CPDF_Object* GetTestObject(uint32_t objnum) {
+ auto it = objects_data_.find(objnum);
+ if (it == objects_data_.end())
+ return nullptr;
+ return it->second.object.get();
+ }
+
+ private:
+ struct ObjectData {
+ std::unique_ptr<CPDF_Object> object;
+ ObjectState state = ObjectState::Unavailable;
+ };
+ std::map<uint32_t, ObjectData> objects_data_;
+ CFX_RetainPtr<TestReadValidator> validator_;
+};
+
+class CPDF_ObjectAvailFailOnExclude : public CPDF_ObjectAvail {
+ public:
+ using CPDF_ObjectAvail::CPDF_ObjectAvail;
+ ~CPDF_ObjectAvailFailOnExclude() override {}
+ bool ExcludeObject(const CPDF_Object* object) const override {
+ NOTREACHED();
+ return false;
+ }
+};
+
+class CPDF_ObjectAvailExcludeArray : public CPDF_ObjectAvail {
+ public:
+ using CPDF_ObjectAvail::CPDF_ObjectAvail;
+ ~CPDF_ObjectAvailExcludeArray() override {}
+ bool ExcludeObject(const CPDF_Object* object) const override {
+ return object->IsArray();
+ }
+};
+
+class CPDF_ObjectAvailExcludeTypeKey : public CPDF_ObjectAvail {
+ public:
+ using CPDF_ObjectAvail::CPDF_ObjectAvail;
+ ~CPDF_ObjectAvailExcludeTypeKey() override {}
+ bool ExcludeObject(const CPDF_Object* object) const override {
+ // The value of "Type" may be reference, and if it is not available, we can
+ // incorrect filter objects.
+ // In this case CPDF_ObjectAvail should wait availability of this item and
+ // call ExcludeObject again.
+ return object->IsDictionary() &&
+ object->GetDict()->GetStringFor("Type") == "Exclude me";
+ }
+};
+
+} // namespace
+
+TEST(CPDF_ObjectAvailTest, OneObject) {
+ TestHolder holder;
+ holder.AddObject(1, pdfium::MakeUnique<CPDF_String>(nullptr, "string", false),
+ TestHolder::ObjectState::Unavailable);
+ CPDF_ObjectAvail avail(holder.GetValidator().Get(), &holder, 1);
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
+ avail.CheckAvail());
+ holder.SetObjectState(1, TestHolder::ObjectState::Available);
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+}
+
+TEST(CPDF_ObjectAvailTest, OneReferencedObject) {
+ TestHolder holder;
+ holder.AddObject(1, pdfium::MakeUnique<CPDF_Reference>(&holder, 2),
+ TestHolder::ObjectState::Unavailable);
+ holder.AddObject(2, pdfium::MakeUnique<CPDF_String>(nullptr, "string", false),
+ TestHolder::ObjectState::Unavailable);
+ CPDF_ObjectAvail avail(holder.GetValidator().Get(), &holder, 1);
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
+ avail.CheckAvail());
+
+ holder.SetObjectState(1, TestHolder::ObjectState::Available);
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
+ avail.CheckAvail());
+
+ holder.SetObjectState(2, TestHolder::ObjectState::Available);
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+}
+
+TEST(CPDF_ObjectAvailTest, CycledReferences) {
+ TestHolder holder;
+ holder.AddObject(1, pdfium::MakeUnique<CPDF_Reference>(&holder, 2),
+ TestHolder::ObjectState::Unavailable);
+ holder.AddObject(2, pdfium::MakeUnique<CPDF_Reference>(&holder, 3),
+ TestHolder::ObjectState::Unavailable);
+ holder.AddObject(3, pdfium::MakeUnique<CPDF_Reference>(&holder, 1),
+ TestHolder::ObjectState::Unavailable);
+
+ CPDF_ObjectAvail avail(holder.GetValidator().Get(), &holder, 1);
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
+ avail.CheckAvail());
+
+ holder.SetObjectState(1, TestHolder::ObjectState::Available);
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
+ avail.CheckAvail());
+
+ holder.SetObjectState(2, TestHolder::ObjectState::Available);
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
+ avail.CheckAvail());
+
+ holder.SetObjectState(3, TestHolder::ObjectState::Available);
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+}
+
+TEST(CPDF_ObjectAvailTest, DoNotCheckParent) {
+ TestHolder holder;
+ holder.AddObject(1, pdfium::MakeUnique<CPDF_Dictionary>(),
+ TestHolder::ObjectState::Unavailable);
+ holder.AddObject(2, pdfium::MakeUnique<CPDF_Dictionary>(),
+ TestHolder::ObjectState::Unavailable);
+
+ holder.GetTestObject(2)->GetDict()->SetNewFor<CPDF_Reference>("Parent",
+ &holder, 1);
+
+ CPDF_ObjectAvail avail(holder.GetValidator().Get(), &holder, 2);
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
+ avail.CheckAvail());
+
+ holder.SetObjectState(2, TestHolder::ObjectState::Available);
+ // Object should be available in case when "Parent" object is unavailable.
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+}
+
+TEST(CPDF_ObjectAvailTest, Generic) {
+ TestHolder holder;
+ const uint32_t kDepth = 100;
+ for (uint32_t i = 1; i < kDepth; ++i) {
+ holder.AddObject(i, pdfium::MakeUnique<CPDF_Dictionary>(),
+ TestHolder::ObjectState::Unavailable);
+ // Add ref to next dictionary.
+ holder.GetTestObject(i)->GetDict()->SetNewFor<CPDF_Reference>(
+ "Child", &holder, i + 1);
+ }
+ // Add final object
+ holder.AddObject(kDepth, pdfium::MakeUnique<CPDF_Dictionary>(),
+ TestHolder::ObjectState::Unavailable);
+
+ CPDF_ObjectAvail avail(holder.GetValidator().Get(), &holder, 1);
+
+ for (uint32_t i = 1; i <= kDepth; ++i) {
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
+ avail.CheckAvail());
+ holder.SetObjectState(i, TestHolder::ObjectState::Available);
+ }
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+}
+
+TEST(CPDF_ObjectAvailTest, NotExcludeRoot) {
+ TestHolder holder;
+ holder.AddObject(1, pdfium::MakeUnique<CPDF_Dictionary>(),
+ TestHolder::ObjectState::Available);
+ CPDF_ObjectAvailFailOnExclude avail(holder.GetValidator().Get(), &holder, 1);
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+}
+
+TEST(CPDF_ObjectAvailTest, NotExcludeReferedRoot) {
+ TestHolder holder;
+ holder.AddObject(1, pdfium::MakeUnique<CPDF_Reference>(&holder, 2),
+ TestHolder::ObjectState::Available);
+ holder.AddObject(2, pdfium::MakeUnique<CPDF_Dictionary>(),
+ TestHolder::ObjectState::Available);
+ CPDF_ObjectAvailFailOnExclude avail(holder.GetValidator().Get(), &holder, 1);
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+}
+
+TEST(CPDF_ObjectAvailTest, Exclude) {
+ TestHolder holder;
+ holder.AddObject(1, pdfium::MakeUnique<CPDF_Dictionary>(),
+ TestHolder::ObjectState::Available);
+ holder.GetTestObject(1)->GetDict()->SetNewFor<CPDF_Reference>("ArrayRef",
+ &holder, 2);
+ holder.AddObject(2, pdfium::MakeUnique<CPDF_Array>(),
+ TestHolder::ObjectState::Available);
+ holder.GetTestObject(2)->AsArray()->AddNew<CPDF_Reference>(&holder, 2);
+
+ // Add string, which is refered by array item. It is should not be checked.
+ holder.AddObject(
+ 3,
+ pdfium::MakeUnique<CPDF_String>(nullptr, "Not available string", false),
+ TestHolder::ObjectState::Unavailable);
+ CPDF_ObjectAvailExcludeArray avail(holder.GetValidator().Get(), &holder, 1);
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+}
+
+TEST(CPDF_ObjectAvailTest, ReadErrorOnExclude) {
+ TestHolder holder;
+ holder.AddObject(1, pdfium::MakeUnique<CPDF_Dictionary>(),
+ TestHolder::ObjectState::Available);
+ holder.GetTestObject(1)->GetDict()->SetNewFor<CPDF_Reference>("DictRef",
+ &holder, 2);
+ holder.AddObject(2, pdfium::MakeUnique<CPDF_Dictionary>(),
+ TestHolder::ObjectState::Available);
+
+ holder.GetTestObject(2)->GetDict()->SetNewFor<CPDF_Reference>("Type", &holder,
+ 3);
+ // The value of "Type" key is not available at start
+ holder.AddObject(
+ 3, pdfium::MakeUnique<CPDF_String>(nullptr, "Exclude me", false),
+ TestHolder::ObjectState::Unavailable);
+
+ holder.GetTestObject(2)->GetDict()->SetNewFor<CPDF_Reference>("OtherData",
+ &holder, 4);
+ // Add string, which is refered by dictionary item. It is should not be
+ // checked, because the dictionary with it, should be skipped.
+ holder.AddObject(
+ 4,
+ pdfium::MakeUnique<CPDF_String>(nullptr, "Not available string", false),
+ TestHolder::ObjectState::Unavailable);
+
+ CPDF_ObjectAvailExcludeTypeKey avail(holder.GetValidator().Get(), &holder, 1);
+
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
+ avail.CheckAvail());
+
+ // Make "Type" value object available.
+ holder.SetObjectState(3, TestHolder::ObjectState::Available);
+
+ // Now object should be available, although the object '4' is not available,
+ // because it is in skipped dictionary.
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+}
+
+TEST(CPDF_ObjectAvailTest, IgnoreNotExistsObject) {
+ TestHolder holder;
+ holder.AddObject(1, pdfium::MakeUnique<CPDF_Dictionary>(),
+ TestHolder::ObjectState::Available);
+ holder.GetTestObject(1)->GetDict()->SetNewFor<CPDF_Reference>(
+ "NotExistsObjRef", &holder, 2);
+ CPDF_ObjectAvail avail(holder.GetValidator().Get(), &holder, 1);
+ // Now object should be available, although the object '2' is not exists. But
+ // all exists in file related data are checked.
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+}
+
+TEST(CPDF_ObjectAvailTest, CheckTwice) {
+ TestHolder holder;
+ holder.AddObject(1, pdfium::MakeUnique<CPDF_String>(nullptr, "string", false),
+ TestHolder::ObjectState::Unavailable);
+
+ CPDF_ObjectAvail avail(holder.GetValidator().Get(), &holder, 1);
+ EXPECT_EQ(avail.CheckAvail(), avail.CheckAvail());
+
+ holder.SetObjectState(1, TestHolder::ObjectState::Available);
+ EXPECT_EQ(avail.CheckAvail(), avail.CheckAvail());
+}
+
+TEST(CPDF_ObjectAvailTest, SelfReferedInlinedObject) {
+ TestHolder holder;
+ holder.AddObject(1, pdfium::MakeUnique<CPDF_Dictionary>(),
+ TestHolder::ObjectState::Available);
+
+ holder.GetTestObject(1)->GetDict()->SetNewFor<CPDF_Reference>("Data", &holder,
+ 2);
+ auto* root =
+ holder.GetTestObject(1)->GetDict()->SetNewFor<CPDF_Dictionary>("Dict");
+
+ root->SetNewFor<CPDF_Reference>("Self", &holder, 1);
+
+ holder.AddObject(2, pdfium::MakeUnique<CPDF_String>(nullptr, "Data", false),
+ TestHolder::ObjectState::Unavailable);
+
+ CPDF_ObjectAvail avail(holder.GetValidator().Get(), &holder, root);
+
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataNotAvailable,
+ avail.CheckAvail());
+
+ holder.SetObjectState(2, TestHolder::ObjectState::Available);
+
+ EXPECT_EQ(CPDF_DataAvail::DocAvailStatus::DataAvailable, avail.CheckAvail());
+}