blob: 0fb2607b3dab750a61d46cc36701b1d41298f3ff [file] [log] [blame]
// Copyright 2017 The PDFium Authors
// 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_indirect_object_holder.h"
#include "core/fpdfapi/parser/cpdf_array.h"
#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_null.h"
#include "core/fxcrt/check.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
class MockIndirectObjectHolder final : public CPDF_IndirectObjectHolder {
public:
MockIndirectObjectHolder() = default;
~MockIndirectObjectHolder() override = default;
MOCK_METHOD1(ParseIndirectObject, RetainPtr<CPDF_Object>(uint32_t objnum));
};
} // namespace
TEST(IndirectObjectHolderTest, RecursiveParseOfSameObject) {
MockIndirectObjectHolder mock_holder;
// ParseIndirectObject should not be called again on recursively same object
// parse request.
EXPECT_CALL(mock_holder, ParseIndirectObject(::testing::_))
.WillOnce(::testing::WithArg<0>(::testing::Invoke(
[&mock_holder](uint32_t objnum) -> RetainPtr<CPDF_Object> {
RetainPtr<const CPDF_Object> same_parse =
mock_holder.GetOrParseIndirectObject(objnum);
CHECK(!same_parse);
return pdfium::MakeRetain<CPDF_Null>();
})));
EXPECT_TRUE(mock_holder.GetOrParseIndirectObject(1000));
}
TEST(IndirectObjectHolderTest, GetObjectMethods) {
static constexpr uint32_t kObjNum = 1000;
MockIndirectObjectHolder mock_holder;
EXPECT_CALL(mock_holder, ParseIndirectObject(::testing::_)).Times(0);
EXPECT_FALSE(mock_holder.GetIndirectObject(kObjNum));
::testing::Mock::VerifyAndClearExpectations(&mock_holder);
EXPECT_CALL(mock_holder, ParseIndirectObject(::testing::_))
.WillOnce(::testing::WithArg<0>(
::testing::Invoke([](uint32_t objnum) -> RetainPtr<CPDF_Object> {
return pdfium::MakeRetain<CPDF_Null>();
})));
EXPECT_TRUE(mock_holder.GetOrParseIndirectObject(kObjNum));
::testing::Mock::VerifyAndClearExpectations(&mock_holder);
EXPECT_CALL(mock_holder, ParseIndirectObject(::testing::_)).Times(0);
ASSERT_TRUE(mock_holder.GetIndirectObject(kObjNum));
::testing::Mock::VerifyAndClearExpectations(&mock_holder);
EXPECT_EQ(kObjNum, mock_holder.GetIndirectObject(kObjNum)->GetObjNum());
}
TEST(IndirectObjectHolderTest, ParseInvalidObjNum) {
MockIndirectObjectHolder mock_holder;
EXPECT_CALL(mock_holder, ParseIndirectObject(::testing::_)).Times(0);
EXPECT_FALSE(
mock_holder.GetOrParseIndirectObject(CPDF_Object::kInvalidObjNum));
}
TEST(IndirectObjectHolderTest, ReplaceObjectWithInvalidObjNum) {
MockIndirectObjectHolder mock_holder;
EXPECT_CALL(mock_holder, ParseIndirectObject(::testing::_)).Times(0);
EXPECT_FALSE(mock_holder.ReplaceIndirectObjectIfHigherGeneration(
CPDF_Object::kInvalidObjNum, pdfium::MakeRetain<CPDF_Null>()));
}
TEST(IndirectObjectHolderTest, TemplateNewMethods) {
MockIndirectObjectHolder mock_holder;
auto pDict = mock_holder.NewIndirect<CPDF_Dictionary>();
auto pArray = mock_holder.NewIndirect<CPDF_Array>();
mock_holder.DeleteIndirectObject(pDict->GetObjNum());
mock_holder.DeleteIndirectObject(pArray->GetObjNum());
// No longer UAF since NewIndirect<> returns retained objects.
EXPECT_TRUE(pDict->IsDictionary());
EXPECT_TRUE(pArray->IsArray());
}