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