//===-- TestOptionValue.cpp -------- -------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "lldb/Interpreter/OptionValues.h" #include "gmock/gmock.h" #include "gtest/gtest.h" using namespace lldb_private; class Callback { public: virtual void Invoke() const {} void operator()() const { Invoke(); } protected: ~Callback() = default; }; class MockCallback final : public Callback { public: MOCK_CONST_METHOD0(Invoke, void()); }; // Test a single-value class. TEST(OptionValueString, DeepCopy) { OptionValueString str; str.SetValueFromString("ab"); MockCallback callback; str.SetValueChangedCallback([&callback] { callback(); }); EXPECT_CALL(callback, Invoke()); auto copy_sp = str.DeepCopy(nullptr); // Test that the base class data members are copied/set correctly. ASSERT_TRUE(copy_sp); ASSERT_EQ(copy_sp->GetParent().get(), nullptr); ASSERT_TRUE(copy_sp->OptionWasSet()); ASSERT_EQ(copy_sp->GetValueAs(), "ab"); // Trigger the callback. copy_sp->SetValueFromString("c", eVarSetOperationAppend); ASSERT_EQ(copy_sp->GetValueAs(), "abc"); } // Test an aggregate class. TEST(OptionValueArgs, DeepCopy) { OptionValueArgs args; args.SetValueFromString("A B"); MockCallback callback; args.SetValueChangedCallback([&callback] { callback(); }); EXPECT_CALL(callback, Invoke()); auto copy_sp = args.DeepCopy(nullptr); // Test that the base class data members are copied/set correctly. ASSERT_TRUE(copy_sp); ASSERT_EQ(copy_sp->GetParent(), nullptr); ASSERT_TRUE(copy_sp->OptionWasSet()); auto *args_copy_ptr = copy_sp->GetAsArgs(); ASSERT_EQ(args_copy_ptr->GetSize(), 2U); ASSERT_EQ((*args_copy_ptr)[0]->GetParent(), copy_sp); ASSERT_EQ((*args_copy_ptr)[0]->GetValueAs(), "A"); ASSERT_EQ((*args_copy_ptr)[1]->GetParent(), copy_sp); ASSERT_EQ((*args_copy_ptr)[1]->GetValueAs(), "B"); // Trigger the callback. copy_sp->SetValueFromString("C", eVarSetOperationAppend); ASSERT_TRUE(args_copy_ptr); ASSERT_EQ(args_copy_ptr->GetSize(), 3U); ASSERT_EQ((*args_copy_ptr)[2]->GetValueAs(), "C"); } class TestProperties : public OptionValueProperties { public: static std::shared_ptr CreateGlobal() { auto props_sp = std::make_shared(); const bool is_global = false; auto dict_sp = std::make_shared(1 << eTypeUInt64); props_sp->AppendProperty("dict", "", is_global, dict_sp); auto file_list_sp = std::make_shared(); props_sp->AppendProperty("file-list", "", is_global, file_list_sp); return props_sp; } void SetDictionaryChangedCallback(const MockCallback &callback) { SetValueChangedCallback(m_dict_index, [&callback] { callback(); }); } void SetFileListChangedCallback(const MockCallback &callback) { SetValueChangedCallback(m_file_list_index, [&callback] { callback(); }); } OptionValueDictionary *GetDictionary() { return GetPropertyAtIndexAsOptionValueDictionary(m_dict_index); } OptionValueFileSpecList *GetFileList() { return GetPropertyAtIndexAsOptionValueFileSpecList(m_file_list_index); } private: lldb::OptionValueSP Clone() const override { return std::make_shared(*this); } uint32_t m_dict_index = 0; uint32_t m_file_list_index = 1; }; // Test a user-defined propery class. TEST(TestProperties, DeepCopy) { auto props_sp = TestProperties::CreateGlobal(); props_sp->GetDictionary()->SetValueFromString("A=1 B=2"); props_sp->GetFileList()->SetValueFromString("path/to/file"); MockCallback callback; props_sp->SetDictionaryChangedCallback(callback); props_sp->SetFileListChangedCallback(callback); EXPECT_CALL(callback, Invoke()).Times(2); auto copy_sp = props_sp->DeepCopy(nullptr); // Test that the base class data members are copied/set correctly. ASSERT_TRUE(copy_sp); ASSERT_EQ(copy_sp->GetParent(), nullptr); // This cast is safe only if the class overrides Clone(). auto *props_copy_ptr = static_cast(copy_sp.get()); ASSERT_TRUE(props_copy_ptr); // Test the first child. auto dict_copy_ptr = props_copy_ptr->GetDictionary(); ASSERT_TRUE(dict_copy_ptr); ASSERT_EQ(dict_copy_ptr->GetParent(), copy_sp); ASSERT_TRUE(dict_copy_ptr->OptionWasSet()); ASSERT_EQ(dict_copy_ptr->GetNumValues(), 2U); auto value_ptr = dict_copy_ptr->GetValueForKey("A"); ASSERT_TRUE(value_ptr); ASSERT_EQ(value_ptr->GetParent().get(), dict_copy_ptr); ASSERT_EQ(value_ptr->GetValueAs(), 1U); value_ptr = dict_copy_ptr->GetValueForKey("B"); ASSERT_TRUE(value_ptr); ASSERT_EQ(value_ptr->GetParent().get(), dict_copy_ptr); ASSERT_EQ(value_ptr->GetValueAs(), 2U); // Test the second child. auto file_list_copy_ptr = props_copy_ptr->GetFileList(); ASSERT_TRUE(file_list_copy_ptr); ASSERT_EQ(file_list_copy_ptr->GetParent(), copy_sp); ASSERT_TRUE(file_list_copy_ptr->OptionWasSet()); auto file_list_copy = file_list_copy_ptr->GetCurrentValue(); ASSERT_EQ(file_list_copy.GetSize(), 1U); ASSERT_EQ(file_list_copy.GetFileSpecAtIndex(0), FileSpec("path/to/file")); // Trigger the callback first time. dict_copy_ptr->SetValueFromString("C=3", eVarSetOperationAppend); // Trigger the callback second time. file_list_copy_ptr->SetValueFromString("0 another/path", eVarSetOperationReplace); }