Add unit test for CPDF_Action::GetType(). Add a comprehensive test for this method. Change-Id: I2ff6531af130ea1021b102b3be693349d4a52782 Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/79296 Reviewed-by: Hui Yingst <nigi@chromium.org> Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/core/fpdfdoc/BUILD.gn b/core/fpdfdoc/BUILD.gn index 4adbac2..7d26906 100644 --- a/core/fpdfdoc/BUILD.gn +++ b/core/fpdfdoc/BUILD.gn
@@ -92,6 +92,7 @@ pdfium_unittest_source_set("unittests") { sources = [ + "cpdf_action_unittest.cpp", "cpdf_annot_unittest.cpp", "cpdf_defaultappearance_unittest.cpp", "cpdf_dest_unittest.cpp",
diff --git a/core/fpdfdoc/cpdf_action_unittest.cpp b/core/fpdfdoc/cpdf_action_unittest.cpp new file mode 100644 index 0000000..35639a5 --- /dev/null +++ b/core/fpdfdoc/cpdf_action_unittest.cpp
@@ -0,0 +1,137 @@ +// Copyright 2021 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/fpdfdoc/cpdf_action.h" + +#include "core/fpdfapi/parser/cpdf_dictionary.h" +#include "core/fpdfapi/parser/cpdf_name.h" +#include "core/fpdfapi/parser/cpdf_string.h" +#include "core/fxcrt/retain_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +RetainPtr<CPDF_Dictionary> CreateActionDictWithType( + const ByteString& action_type) { + auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); + dict->SetNewFor<CPDF_Name>("Type", "Action"); + dict->SetNewFor<CPDF_Name>("S", action_type); + return dict; +} + +RetainPtr<CPDF_Dictionary> CreateActionDictWithoutType( + const ByteString& action_type) { + auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); + dict->SetNewFor<CPDF_Name>("S", action_type); + return dict; +} + +RetainPtr<CPDF_Dictionary> CreateActionDictWithInvalidType( + const ByteString& action_type) { + auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); + dict->SetNewFor<CPDF_Name>("Type", "Lights"); + dict->SetNewFor<CPDF_Name>("S", action_type); + return dict; +} + +RetainPtr<CPDF_Dictionary> CreateInvalidActionDictWithType( + const ByteString& action_type) { + auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); + dict->SetNewFor<CPDF_Name>("Type", "Action"); + dict->SetNewFor<CPDF_String>("S", action_type, /*is_hex=*/false); + return dict; +} + +RetainPtr<CPDF_Dictionary> CreateInvalidActionDictWithoutType( + const ByteString& action_type) { + auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); + dict->SetNewFor<CPDF_String>("S", action_type, /*is_hex=*/false); + return dict; +} + +} // namespace + +TEST(CPDFActionTest, GetType) { + static constexpr struct { + const char* action_type; + CPDF_Action::Type expected_type; + } kValidTestCases[] = { + {"GoTo", CPDF_Action::Type::kGoTo}, + {"GoToR", CPDF_Action::Type::kGoToR}, + {"GoToE", CPDF_Action::Type::kGoToE}, + {"Launch", CPDF_Action::Type::kLaunch}, + {"Thread", CPDF_Action::Type::kThread}, + {"URI", CPDF_Action::Type::kURI}, + {"Sound", CPDF_Action::Type::kSound}, + {"Movie", CPDF_Action::Type::kMovie}, + {"Hide", CPDF_Action::Type::kHide}, + {"Named", CPDF_Action::Type::kNamed}, + {"SubmitForm", CPDF_Action::Type::kSubmitForm}, + {"ResetForm", CPDF_Action::Type::kResetForm}, + {"ImportData", CPDF_Action::Type::kImportData}, + {"JavaScript", CPDF_Action::Type::kJavaScript}, + {"SetOCGState", CPDF_Action::Type::kSetOCGState}, + {"Rendition", CPDF_Action::Type::kRendition}, + {"Trans", CPDF_Action::Type::kTrans}, + {"GoTo3DView", CPDF_Action::Type::kGoTo3DView}, + }; + + // Test correctly constructed actions. + for (const auto& test_case : kValidTestCases) { + { + // Type is present. + RetainPtr<CPDF_Dictionary> dict = + CreateActionDictWithType(test_case.action_type); + CPDF_Action action(dict.Get()); + EXPECT_EQ(test_case.expected_type, action.GetType()); + } + { + // Type is optional, so omitting it is ok. + RetainPtr<CPDF_Dictionary> dict = + CreateActionDictWithoutType(test_case.action_type); + CPDF_Action action(dict.Get()); + EXPECT_EQ(test_case.expected_type, action.GetType()); + } + } + + // Test incorrectly constructed actions. + for (const auto& test_case : kValidTestCases) { + { + // Type is optional, but must be valid if present. + RetainPtr<CPDF_Dictionary> dict = + CreateActionDictWithInvalidType(test_case.action_type); + CPDF_Action action(dict.Get()); + EXPECT_EQ(CPDF_Action::Type::kUnknown, action.GetType()); + } + { + // The action type (/S) must be a name. + RetainPtr<CPDF_Dictionary> dict = + CreateInvalidActionDictWithType(test_case.action_type); + CPDF_Action action(dict.Get()); + // TODO(thestig): Should return `CPDF_Action::Type::kUnknown`. + EXPECT_EQ(test_case.expected_type, action.GetType()); + } + { + // The action type (/S) must be a name. + RetainPtr<CPDF_Dictionary> dict = + CreateInvalidActionDictWithoutType(test_case.action_type); + CPDF_Action action(dict.Get()); + // TODO(thestig): Should return `CPDF_Action::Type::kUnknown`. + EXPECT_EQ(test_case.expected_type, action.GetType()); + } + } + + static constexpr const char* kInvalidTestCases[] = { + "Camera", + "Javascript", + "Unknown", + }; + + // Test invalid actions. + for (const char* test_case : kInvalidTestCases) { + RetainPtr<CPDF_Dictionary> dict = CreateActionDictWithType(test_case); + CPDF_Action action(dict.Get()); + EXPECT_EQ(CPDF_Action::Type::kUnknown, action.GetType()); + } +}