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:
Invoke() const17 virtual void Invoke() const {}
operator ()() const18 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.
TEST(OptionValueString,DeepCopy)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.
TEST(OptionValueArgs,DeepCopy)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:
CreateGlobal()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
SetDictionaryChangedCallback(const MockCallback & callback)96 void SetDictionaryChangedCallback(const MockCallback &callback) {
97 SetValueChangedCallback(m_dict_index, [&callback] { callback(); });
98 }
99
SetFileListChangedCallback(const MockCallback & callback)100 void SetFileListChangedCallback(const MockCallback &callback) {
101 SetValueChangedCallback(m_file_list_index, [&callback] { callback(); });
102 }
103
GetDictionary()104 OptionValueDictionary *GetDictionary() {
105 return GetPropertyAtIndexAsOptionValueDictionary(nullptr, m_dict_index);
106 }
107
GetFileList()108 OptionValueFileSpecList *GetFileList() {
109 return GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, true,
110 m_file_list_index);
111 }
112
113 private:
Clone() const114 lldb::OptionValueSP Clone() const {
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.
TEST(TestProperties,DeepCopy)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