1 //===-- TestOptionValue.cpp -------- -------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "lldb/Interpreter/OptionValues.h" 10 #include "gmock/gmock.h" 11 #include "gtest/gtest.h" 12 13 using namespace lldb_private; 14 15 class Callback { 16 public: 17 virtual void Invoke() const {} 18 void operator()() const { Invoke(); } 19 protected: 20 ~Callback() = default; 21 }; 22 23 class MockCallback final : public Callback { 24 public: 25 MOCK_CONST_METHOD0(Invoke, void()); 26 }; 27 28 // Test a single-value class. 29 TEST(OptionValueString, DeepCopy) { 30 OptionValueString str; 31 str.SetValueFromString("ab"); 32 33 MockCallback callback; 34 str.SetValueChangedCallback([&callback] { callback(); }); 35 EXPECT_CALL(callback, Invoke()); 36 37 auto copy_sp = str.DeepCopy(nullptr); 38 39 // Test that the base class data members are copied/set correctly. 40 ASSERT_TRUE(copy_sp); 41 ASSERT_EQ(copy_sp->GetParent().get(), nullptr); 42 ASSERT_TRUE(copy_sp->OptionWasSet()); 43 ASSERT_EQ(copy_sp->GetStringValue(), "ab"); 44 45 // Trigger the callback. 46 copy_sp->SetValueFromString("c", eVarSetOperationAppend); 47 ASSERT_EQ(copy_sp->GetStringValue(), "abc"); 48 } 49 50 // Test an aggregate class. 51 TEST(OptionValueArgs, DeepCopy) { 52 OptionValueArgs args; 53 args.SetValueFromString("A B"); 54 55 MockCallback callback; 56 args.SetValueChangedCallback([&callback] { callback(); }); 57 EXPECT_CALL(callback, Invoke()); 58 59 auto copy_sp = args.DeepCopy(nullptr); 60 61 // Test that the base class data members are copied/set correctly. 62 ASSERT_TRUE(copy_sp); 63 ASSERT_EQ(copy_sp->GetParent(), nullptr); 64 ASSERT_TRUE(copy_sp->OptionWasSet()); 65 66 auto *args_copy_ptr = copy_sp->GetAsArgs(); 67 ASSERT_EQ(args_copy_ptr->GetSize(), 2U); 68 ASSERT_EQ((*args_copy_ptr)[0]->GetParent(), copy_sp); 69 ASSERT_EQ((*args_copy_ptr)[0]->GetStringValue(), "A"); 70 ASSERT_EQ((*args_copy_ptr)[1]->GetParent(), copy_sp); 71 ASSERT_EQ((*args_copy_ptr)[1]->GetStringValue(), "B"); 72 73 // Trigger the callback. 74 copy_sp->SetValueFromString("C", eVarSetOperationAppend); 75 ASSERT_TRUE(args_copy_ptr); 76 ASSERT_EQ(args_copy_ptr->GetSize(), 3U); 77 ASSERT_EQ((*args_copy_ptr)[2]->GetStringValue(), "C"); 78 } 79 80 class TestProperties : public OptionValueProperties { 81 public: 82 static std::shared_ptr<TestProperties> CreateGlobal() { 83 auto props_sp = std::make_shared<TestProperties>(); 84 const bool is_global = false; 85 86 auto dict_sp = std::make_shared<OptionValueDictionary>(1 << eTypeUInt64); 87 props_sp->AppendProperty(ConstString("dict"), ConstString(), is_global, 88 dict_sp); 89 90 auto file_list_sp = std::make_shared<OptionValueFileSpecList>(); 91 props_sp->AppendProperty(ConstString("file-list"), ConstString(), is_global, 92 file_list_sp); 93 return props_sp; 94 } 95 96 void SetDictionaryChangedCallback(const MockCallback &callback) { 97 SetValueChangedCallback(m_dict_index, [&callback] { callback(); }); 98 } 99 100 void SetFileListChangedCallback(const MockCallback &callback) { 101 SetValueChangedCallback(m_file_list_index, [&callback] { callback(); }); 102 } 103 104 OptionValueDictionary *GetDictionary() { 105 return GetPropertyAtIndexAsOptionValueDictionary(nullptr, m_dict_index); 106 } 107 108 OptionValueFileSpecList *GetFileList() { 109 return GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, true, 110 m_file_list_index); 111 } 112 113 private: 114 lldb::OptionValueSP Clone() const override { 115 return std::make_shared<TestProperties>(*this); 116 } 117 118 uint32_t m_dict_index = 0; 119 uint32_t m_file_list_index = 1; 120 }; 121 122 // Test a user-defined propery class. 123 TEST(TestProperties, DeepCopy) { 124 auto props_sp = TestProperties::CreateGlobal(); 125 props_sp->GetDictionary()->SetValueFromString("A=1 B=2"); 126 props_sp->GetFileList()->SetValueFromString("path/to/file"); 127 128 MockCallback callback; 129 props_sp->SetDictionaryChangedCallback(callback); 130 props_sp->SetFileListChangedCallback(callback); 131 EXPECT_CALL(callback, Invoke()).Times(2); 132 133 auto copy_sp = props_sp->DeepCopy(nullptr); 134 135 // Test that the base class data members are copied/set correctly. 136 ASSERT_TRUE(copy_sp); 137 ASSERT_EQ(copy_sp->GetParent(), nullptr); 138 139 // This cast is safe only if the class overrides Clone(). 140 auto *props_copy_ptr = static_cast<TestProperties *>(copy_sp.get()); 141 ASSERT_TRUE(props_copy_ptr); 142 143 // Test the first child. 144 auto dict_copy_ptr = props_copy_ptr->GetDictionary(); 145 ASSERT_TRUE(dict_copy_ptr); 146 ASSERT_EQ(dict_copy_ptr->GetParent(), copy_sp); 147 ASSERT_TRUE(dict_copy_ptr->OptionWasSet()); 148 ASSERT_EQ(dict_copy_ptr->GetNumValues(), 2U); 149 150 auto value_ptr = dict_copy_ptr->GetValueForKey(ConstString("A")); 151 ASSERT_TRUE(value_ptr); 152 ASSERT_EQ(value_ptr->GetParent().get(), dict_copy_ptr); 153 ASSERT_EQ(value_ptr->GetUInt64Value(), 1U); 154 155 value_ptr = dict_copy_ptr->GetValueForKey(ConstString("B")); 156 ASSERT_TRUE(value_ptr); 157 ASSERT_EQ(value_ptr->GetParent().get(), dict_copy_ptr); 158 ASSERT_EQ(value_ptr->GetUInt64Value(), 2U); 159 160 // Test the second child. 161 auto file_list_copy_ptr = props_copy_ptr->GetFileList(); 162 ASSERT_TRUE(file_list_copy_ptr); 163 ASSERT_EQ(file_list_copy_ptr->GetParent(), copy_sp); 164 ASSERT_TRUE(file_list_copy_ptr->OptionWasSet()); 165 166 auto file_list_copy = file_list_copy_ptr->GetCurrentValue(); 167 ASSERT_EQ(file_list_copy.GetSize(), 1U); 168 ASSERT_EQ(file_list_copy.GetFileSpecAtIndex(0), FileSpec("path/to/file")); 169 170 // Trigger the callback first time. 171 dict_copy_ptr->SetValueFromString("C=3", eVarSetOperationAppend); 172 173 // Trigger the callback second time. 174 file_list_copy_ptr->SetValueFromString("0 another/path", eVarSetOperationReplace); 175 } 176