1// Copyright 2014 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <brillo/dbus/exported_property_set.h>
6
7#include <string>
8#include <vector>
9
10#include <base/bind.h>
11#include <base/macros.h>
12#include <brillo/dbus/dbus_object.h>
13#include <brillo/dbus/dbus_object_test_helpers.h>
14#include <brillo/errors/error_codes.h>
15#include <dbus/message.h>
16#include <dbus/property.h>
17#include <dbus/object_path.h>
18#include <dbus/mock_bus.h>
19#include <dbus/mock_exported_object.h>
20#include <gmock/gmock.h>
21#include <gtest/gtest.h>
22
23using ::testing::AnyNumber;
24using ::testing::Return;
25using ::testing::Invoke;
26using ::testing::_;
27using ::testing::Unused;
28
29namespace brillo {
30
31namespace dbus_utils {
32
33namespace {
34
35const char kBoolPropName[] = "BoolProp";
36const char kUint8PropName[] = "Uint8Prop";
37const char kInt16PropName[] = "Int16Prop";
38const char kUint16PropName[] = "Uint16Prop";
39const char kInt32PropName[] = "Int32Prop";
40const char kUint32PropName[] = "Uint32Prop";
41const char kInt64PropName[] = "Int64Prop";
42const char kUint64PropName[] = "Uint64Prop";
43const char kDoublePropName[] = "DoubleProp";
44const char kStringPropName[] = "StringProp";
45const char kPathPropName[] = "PathProp";
46const char kStringListPropName[] = "StringListProp";
47const char kPathListPropName[] = "PathListProp";
48const char kUint8ListPropName[] = "Uint8ListProp";
49
50const char kTestInterface1[] = "org.chromium.TestInterface1";
51const char kTestInterface2[] = "org.chromium.TestInterface2";
52const char kTestInterface3[] = "org.chromium.TestInterface3";
53
54const std::string kTestString("lies");
55const dbus::ObjectPath kMethodsExportedOnPath(std::string("/export"));
56const dbus::ObjectPath kTestObjectPathInit(std::string("/path_init"));
57const dbus::ObjectPath kTestObjectPathUpdate(std::string("/path_update"));
58
59}  // namespace
60
61class ExportedPropertySetTest : public ::testing::Test {
62 public:
63  struct Properties {
64   public:
65    ExportedProperty<bool> bool_prop_;
66    ExportedProperty<uint8_t> uint8_prop_;
67    ExportedProperty<int16_t> int16_prop_;
68    ExportedProperty<uint16_t> uint16_prop_;
69    ExportedProperty<int32_t> int32_prop_;
70    ExportedProperty<uint32_t> uint32_prop_;
71    ExportedProperty<int64_t> int64_prop_;
72    ExportedProperty<uint64_t> uint64_prop_;
73    ExportedProperty<double> double_prop_;
74    ExportedProperty<std::string> string_prop_;
75    ExportedProperty<dbus::ObjectPath> path_prop_;
76    ExportedProperty<std::vector<std::string>> stringlist_prop_;
77    ExportedProperty<std::vector<dbus::ObjectPath>> pathlist_prop_;
78    ExportedProperty<std::vector<uint8_t>> uint8list_prop_;
79
80    Properties(scoped_refptr<dbus::Bus> bus, const dbus::ObjectPath& path)
81        : dbus_object_(nullptr, bus, path) {
82      // The empty string is not a valid value for an ObjectPath.
83      path_prop_.SetValue(kTestObjectPathInit);
84      DBusInterface* itf1 = dbus_object_.AddOrGetInterface(kTestInterface1);
85      itf1->AddProperty(kBoolPropName, &bool_prop_);
86      itf1->AddProperty(kUint8PropName, &uint8_prop_);
87      itf1->AddProperty(kInt16PropName, &int16_prop_);
88      // I chose this weird grouping because N=2 is about all the permutations
89      // of GetAll that I want to anticipate.
90      DBusInterface* itf2 = dbus_object_.AddOrGetInterface(kTestInterface2);
91      itf2->AddProperty(kUint16PropName, &uint16_prop_);
92      itf2->AddProperty(kInt32PropName, &int32_prop_);
93      DBusInterface* itf3 = dbus_object_.AddOrGetInterface(kTestInterface3);
94      itf3->AddProperty(kUint32PropName, &uint32_prop_);
95      itf3->AddProperty(kInt64PropName, &int64_prop_);
96      itf3->AddProperty(kUint64PropName, &uint64_prop_);
97      itf3->AddProperty(kDoublePropName, &double_prop_);
98      itf3->AddProperty(kStringPropName, &string_prop_);
99      itf3->AddProperty(kPathPropName, &path_prop_);
100      itf3->AddProperty(kStringListPropName, &stringlist_prop_);
101      itf3->AddProperty(kPathListPropName, &pathlist_prop_);
102      itf3->AddProperty(kUint8ListPropName, &uint8list_prop_);
103      dbus_object_.RegisterAsync(
104          AsyncEventSequencer::GetDefaultCompletionAction());
105    }
106    virtual ~Properties() {}
107
108    DBusObject dbus_object_;
109  };
110
111  void SetUp() override {
112    dbus::Bus::Options options;
113    options.bus_type = dbus::Bus::SYSTEM;
114    bus_ = new dbus::MockBus(options);
115    // By default, don't worry about threading assertions.
116    EXPECT_CALL(*bus_, AssertOnOriginThread()).Times(AnyNumber());
117    EXPECT_CALL(*bus_, AssertOnDBusThread()).Times(AnyNumber());
118    // Use a mock exported object.
119    mock_exported_object_ =
120        new dbus::MockExportedObject(bus_.get(), kMethodsExportedOnPath);
121    EXPECT_CALL(*bus_, GetExportedObject(kMethodsExportedOnPath))
122        .Times(1).WillOnce(Return(mock_exported_object_.get()));
123
124    EXPECT_CALL(*mock_exported_object_,
125                ExportMethod(dbus::kPropertiesInterface, _, _, _)).Times(3);
126    p_.reset(new Properties(bus_, kMethodsExportedOnPath));
127  }
128
129  void TearDown() override {
130    EXPECT_CALL(*mock_exported_object_, Unregister()).Times(1);
131  }
132
133  void AssertMethodReturnsError(dbus::MethodCall* method_call) {
134    method_call->SetSerial(123);
135    auto response = testing::CallMethod(p_->dbus_object_, method_call);
136    ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
137  }
138
139  std::unique_ptr<dbus::Response> GetPropertyOnInterface(
140      const std::string& interface_name,
141      const std::string& property_name) {
142    dbus::MethodCall method_call(dbus::kPropertiesInterface,
143                                 dbus::kPropertiesGet);
144    method_call.SetSerial(123);
145    dbus::MessageWriter writer(&method_call);
146    writer.AppendString(interface_name);
147    writer.AppendString(property_name);
148    return testing::CallMethod(p_->dbus_object_, &method_call);
149  }
150
151  std::unique_ptr<dbus::Response> SetPropertyOnInterface(
152      const std::string& interface_name,
153      const std::string& property_name,
154      const brillo::Any& value) {
155    dbus::MethodCall method_call(dbus::kPropertiesInterface,
156                                 dbus::kPropertiesSet);
157    method_call.SetSerial(123);
158    dbus::MessageWriter writer(&method_call);
159    writer.AppendString(interface_name);
160    writer.AppendString(property_name);
161    dbus_utils::AppendValueToWriter(&writer, value);
162    return testing::CallMethod(p_->dbus_object_, &method_call);
163  }
164
165  std::unique_ptr<dbus::Response> last_response_;
166  scoped_refptr<dbus::MockBus> bus_;
167  scoped_refptr<dbus::MockExportedObject> mock_exported_object_;
168  std::unique_ptr<Properties> p_;
169};
170
171template<typename T>
172class PropertyValidatorObserver {
173 public:
174  PropertyValidatorObserver()
175      : validate_property_callback_(
176            base::Bind(&PropertyValidatorObserver::ValidateProperty,
177                       base::Unretained(this))) {}
178  virtual ~PropertyValidatorObserver() {}
179
180  MOCK_METHOD2_T(ValidateProperty,
181                 bool(brillo::ErrorPtr* error, const T& value));
182
183  const base::Callback<bool(brillo::ErrorPtr*, const T&)>&
184  validate_property_callback() const {
185    return validate_property_callback_;
186  }
187
188 private:
189  base::Callback<bool(brillo::ErrorPtr*, const T&)>
190      validate_property_callback_;
191
192  DISALLOW_COPY_AND_ASSIGN(PropertyValidatorObserver);
193};
194
195TEST_F(ExportedPropertySetTest, UpdateNotifications) {
196  EXPECT_CALL(*mock_exported_object_, SendSignal(_)).Times(14);
197  p_->bool_prop_.SetValue(true);
198  p_->uint8_prop_.SetValue(1);
199  p_->int16_prop_.SetValue(1);
200  p_->uint16_prop_.SetValue(1);
201  p_->int32_prop_.SetValue(1);
202  p_->uint32_prop_.SetValue(1);
203  p_->int64_prop_.SetValue(1);
204  p_->uint64_prop_.SetValue(1);
205  p_->double_prop_.SetValue(1.0);
206  p_->string_prop_.SetValue(kTestString);
207  p_->path_prop_.SetValue(kTestObjectPathUpdate);
208  p_->stringlist_prop_.SetValue({kTestString});
209  p_->pathlist_prop_.SetValue({kTestObjectPathUpdate});
210  p_->uint8list_prop_.SetValue({1});
211}
212
213TEST_F(ExportedPropertySetTest, UpdateToSameValue) {
214  EXPECT_CALL(*mock_exported_object_, SendSignal(_)).Times(1);
215  p_->bool_prop_.SetValue(true);
216  p_->bool_prop_.SetValue(true);
217}
218
219TEST_F(ExportedPropertySetTest, GetAllNoArgs) {
220  dbus::MethodCall method_call(dbus::kPropertiesInterface,
221                               dbus::kPropertiesGetAll);
222  AssertMethodReturnsError(&method_call);
223}
224
225TEST_F(ExportedPropertySetTest, GetAllInvalidInterface) {
226  dbus::MethodCall method_call(dbus::kPropertiesInterface,
227                               dbus::kPropertiesGetAll);
228  method_call.SetSerial(123);
229  dbus::MessageWriter writer(&method_call);
230  writer.AppendString("org.chromium.BadInterface");
231  auto response = testing::CallMethod(p_->dbus_object_, &method_call);
232  dbus::MessageReader response_reader(response.get());
233  dbus::MessageReader dict_reader(nullptr);
234  ASSERT_TRUE(response_reader.PopArray(&dict_reader));
235  // The response should just be a an empty array, since there are no properties
236  // on this interface.  The spec doesn't say much about error conditions here,
237  // so I'm going to assume this is a valid implementation.
238  ASSERT_FALSE(dict_reader.HasMoreData());
239  ASSERT_FALSE(response_reader.HasMoreData());
240}
241
242TEST_F(ExportedPropertySetTest, GetAllExtraArgs) {
243  dbus::MethodCall method_call(dbus::kPropertiesInterface,
244                               dbus::kPropertiesGetAll);
245  dbus::MessageWriter writer(&method_call);
246  writer.AppendString(kTestInterface1);
247  writer.AppendString(kTestInterface1);
248  AssertMethodReturnsError(&method_call);
249}
250
251TEST_F(ExportedPropertySetTest, GetAllCorrectness) {
252  dbus::MethodCall method_call(dbus::kPropertiesInterface,
253                               dbus::kPropertiesGetAll);
254  method_call.SetSerial(123);
255  dbus::MessageWriter writer(&method_call);
256  writer.AppendString(kTestInterface2);
257  auto response = testing::CallMethod(p_->dbus_object_, &method_call);
258  dbus::MessageReader response_reader(response.get());
259  dbus::MessageReader dict_reader(nullptr);
260  dbus::MessageReader entry_reader(nullptr);
261  ASSERT_TRUE(response_reader.PopArray(&dict_reader));
262  ASSERT_TRUE(dict_reader.PopDictEntry(&entry_reader));
263  std::string property_name;
264  ASSERT_TRUE(entry_reader.PopString(&property_name));
265  uint16_t value16;
266  int32_t value32;
267  if (property_name.compare(kUint16PropName) == 0) {
268    ASSERT_TRUE(entry_reader.PopVariantOfUint16(&value16));
269    ASSERT_FALSE(entry_reader.HasMoreData());
270    ASSERT_TRUE(dict_reader.PopDictEntry(&entry_reader));
271    ASSERT_TRUE(entry_reader.PopString(&property_name));
272    ASSERT_EQ(property_name.compare(kInt32PropName), 0);
273    ASSERT_TRUE(entry_reader.PopVariantOfInt32(&value32));
274  } else {
275    ASSERT_EQ(property_name.compare(kInt32PropName), 0);
276    ASSERT_TRUE(entry_reader.PopVariantOfInt32(&value32));
277    ASSERT_FALSE(entry_reader.HasMoreData());
278    ASSERT_TRUE(dict_reader.PopDictEntry(&entry_reader));
279    ASSERT_TRUE(entry_reader.PopString(&property_name));
280    ASSERT_EQ(property_name.compare(kUint16PropName), 0);
281    ASSERT_TRUE(entry_reader.PopVariantOfUint16(&value16));
282  }
283  ASSERT_FALSE(entry_reader.HasMoreData());
284  ASSERT_FALSE(dict_reader.HasMoreData());
285  ASSERT_FALSE(response_reader.HasMoreData());
286}
287
288TEST_F(ExportedPropertySetTest, GetNoArgs) {
289  dbus::MethodCall method_call(dbus::kPropertiesInterface,
290                               dbus::kPropertiesGet);
291  AssertMethodReturnsError(&method_call);
292}
293
294TEST_F(ExportedPropertySetTest, GetInvalidInterface) {
295  dbus::MethodCall method_call(dbus::kPropertiesInterface,
296                               dbus::kPropertiesGet);
297  dbus::MessageWriter writer(&method_call);
298  writer.AppendString("org.chromium.BadInterface");
299  writer.AppendString(kInt16PropName);
300  AssertMethodReturnsError(&method_call);
301}
302
303TEST_F(ExportedPropertySetTest, GetBadPropertyName) {
304  dbus::MethodCall method_call(dbus::kPropertiesInterface,
305                               dbus::kPropertiesGet);
306  dbus::MessageWriter writer(&method_call);
307  writer.AppendString(kTestInterface1);
308  writer.AppendString("IAmNotAProperty");
309  AssertMethodReturnsError(&method_call);
310}
311
312TEST_F(ExportedPropertySetTest, GetPropIfMismatch) {
313  dbus::MethodCall method_call(dbus::kPropertiesInterface,
314                               dbus::kPropertiesGet);
315  dbus::MessageWriter writer(&method_call);
316  writer.AppendString(kTestInterface1);
317  writer.AppendString(kStringPropName);
318  AssertMethodReturnsError(&method_call);
319}
320
321TEST_F(ExportedPropertySetTest, GetNoPropertyName) {
322  dbus::MethodCall method_call(dbus::kPropertiesInterface,
323                               dbus::kPropertiesGet);
324  dbus::MessageWriter writer(&method_call);
325  writer.AppendString(kTestInterface1);
326  AssertMethodReturnsError(&method_call);
327}
328
329TEST_F(ExportedPropertySetTest, GetExtraArgs) {
330  dbus::MethodCall method_call(dbus::kPropertiesInterface,
331                               dbus::kPropertiesGet);
332  dbus::MessageWriter writer(&method_call);
333  writer.AppendString(kTestInterface1);
334  writer.AppendString(kBoolPropName);
335  writer.AppendString("Extra param");
336  AssertMethodReturnsError(&method_call);
337}
338
339TEST_F(ExportedPropertySetTest, GetWorksWithBool) {
340  auto response = GetPropertyOnInterface(kTestInterface1, kBoolPropName);
341  dbus::MessageReader reader(response.get());
342  bool value;
343  ASSERT_TRUE(reader.PopVariantOfBool(&value));
344  ASSERT_FALSE(reader.HasMoreData());
345}
346
347TEST_F(ExportedPropertySetTest, GetWorksWithUint8) {
348  auto response = GetPropertyOnInterface(kTestInterface1, kUint8PropName);
349  dbus::MessageReader reader(response.get());
350  uint8_t value;
351  ASSERT_TRUE(reader.PopVariantOfByte(&value));
352  ASSERT_FALSE(reader.HasMoreData());
353}
354
355TEST_F(ExportedPropertySetTest, GetWorksWithInt16) {
356  auto response = GetPropertyOnInterface(kTestInterface1, kInt16PropName);
357  dbus::MessageReader reader(response.get());
358  int16_t value;
359  ASSERT_TRUE(reader.PopVariantOfInt16(&value));
360  ASSERT_FALSE(reader.HasMoreData());
361}
362
363TEST_F(ExportedPropertySetTest, GetWorksWithUint16) {
364  auto response = GetPropertyOnInterface(kTestInterface2, kUint16PropName);
365  dbus::MessageReader reader(response.get());
366  uint16_t value;
367  ASSERT_TRUE(reader.PopVariantOfUint16(&value));
368  ASSERT_FALSE(reader.HasMoreData());
369}
370
371TEST_F(ExportedPropertySetTest, GetWorksWithInt32) {
372  auto response = GetPropertyOnInterface(kTestInterface2, kInt32PropName);
373  dbus::MessageReader reader(response.get());
374  int32_t value;
375  ASSERT_TRUE(reader.PopVariantOfInt32(&value));
376  ASSERT_FALSE(reader.HasMoreData());
377}
378
379TEST_F(ExportedPropertySetTest, GetWorksWithUint32) {
380  auto response = GetPropertyOnInterface(kTestInterface3, kUint32PropName);
381  dbus::MessageReader reader(response.get());
382  uint32_t value;
383  ASSERT_TRUE(reader.PopVariantOfUint32(&value));
384  ASSERT_FALSE(reader.HasMoreData());
385}
386
387TEST_F(ExportedPropertySetTest, GetWorksWithInt64) {
388  auto response = GetPropertyOnInterface(kTestInterface3, kInt64PropName);
389  dbus::MessageReader reader(response.get());
390  int64_t value;
391  ASSERT_TRUE(reader.PopVariantOfInt64(&value));
392  ASSERT_FALSE(reader.HasMoreData());
393}
394
395TEST_F(ExportedPropertySetTest, GetWorksWithUint64) {
396  auto response = GetPropertyOnInterface(kTestInterface3, kUint64PropName);
397  dbus::MessageReader reader(response.get());
398  uint64_t value;
399  ASSERT_TRUE(reader.PopVariantOfUint64(&value));
400  ASSERT_FALSE(reader.HasMoreData());
401}
402
403TEST_F(ExportedPropertySetTest, GetWorksWithDouble) {
404  auto response = GetPropertyOnInterface(kTestInterface3, kDoublePropName);
405  dbus::MessageReader reader(response.get());
406  double value;
407  ASSERT_TRUE(reader.PopVariantOfDouble(&value));
408  ASSERT_FALSE(reader.HasMoreData());
409}
410
411TEST_F(ExportedPropertySetTest, GetWorksWithString) {
412  auto response = GetPropertyOnInterface(kTestInterface3, kStringPropName);
413  dbus::MessageReader reader(response.get());
414  std::string value;
415  ASSERT_TRUE(reader.PopVariantOfString(&value));
416  ASSERT_FALSE(reader.HasMoreData());
417}
418
419TEST_F(ExportedPropertySetTest, GetWorksWithPath) {
420  auto response = GetPropertyOnInterface(kTestInterface3, kPathPropName);
421  dbus::MessageReader reader(response.get());
422  dbus::ObjectPath value;
423  ASSERT_TRUE(reader.PopVariantOfObjectPath(&value));
424  ASSERT_FALSE(reader.HasMoreData());
425}
426
427TEST_F(ExportedPropertySetTest, GetWorksWithStringList) {
428  auto response = GetPropertyOnInterface(kTestInterface3, kStringListPropName);
429  dbus::MessageReader reader(response.get());
430  dbus::MessageReader variant_reader(nullptr);
431  std::vector<std::string> value;
432  ASSERT_TRUE(reader.PopVariant(&variant_reader));
433  ASSERT_TRUE(variant_reader.PopArrayOfStrings(&value));
434  ASSERT_FALSE(variant_reader.HasMoreData());
435  ASSERT_FALSE(reader.HasMoreData());
436}
437
438TEST_F(ExportedPropertySetTest, GetWorksWithPathList) {
439  auto response = GetPropertyOnInterface(kTestInterface3, kPathListPropName);
440  dbus::MessageReader reader(response.get());
441  dbus::MessageReader variant_reader(nullptr);
442  std::vector<dbus::ObjectPath> value;
443  ASSERT_TRUE(reader.PopVariant(&variant_reader));
444  ASSERT_TRUE(variant_reader.PopArrayOfObjectPaths(&value));
445  ASSERT_FALSE(variant_reader.HasMoreData());
446  ASSERT_FALSE(reader.HasMoreData());
447}
448
449TEST_F(ExportedPropertySetTest, GetWorksWithUint8List) {
450  auto response = GetPropertyOnInterface(kTestInterface3, kPathListPropName);
451  dbus::MessageReader reader(response.get());
452  dbus::MessageReader variant_reader(nullptr);
453  const uint8_t* buffer;
454  size_t buffer_len;
455  ASSERT_TRUE(reader.PopVariant(&variant_reader));
456  // |buffer| remains under the control of the MessageReader.
457  ASSERT_TRUE(variant_reader.PopArrayOfBytes(&buffer, &buffer_len));
458  ASSERT_FALSE(variant_reader.HasMoreData());
459  ASSERT_FALSE(reader.HasMoreData());
460}
461
462TEST_F(ExportedPropertySetTest, SetInvalidInterface) {
463  auto response = SetPropertyOnInterface(
464      "BadInterfaceName", kStringPropName, brillo::Any(kTestString));
465  ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
466  ASSERT_EQ(DBUS_ERROR_UNKNOWN_INTERFACE, response->GetErrorName());
467}
468
469TEST_F(ExportedPropertySetTest, SetBadPropertyName) {
470  auto response = SetPropertyOnInterface(
471      kTestInterface3, "IAmNotAProperty", brillo::Any(kTestString));
472  ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
473  ASSERT_EQ(DBUS_ERROR_UNKNOWN_PROPERTY, response->GetErrorName());
474}
475
476TEST_F(ExportedPropertySetTest, SetFailsWithReadOnlyProperty) {
477  auto response = SetPropertyOnInterface(
478      kTestInterface3, kStringPropName, brillo::Any(kTestString));
479  ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
480  ASSERT_EQ(DBUS_ERROR_PROPERTY_READ_ONLY, response->GetErrorName());
481}
482
483TEST_F(ExportedPropertySetTest, SetFailsWithMismatchedValueType) {
484  p_->string_prop_.SetAccessMode(ExportedPropertyBase::Access::kReadWrite);
485  auto response = SetPropertyOnInterface(
486      kTestInterface3, kStringPropName, brillo::Any(true));
487  ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
488  ASSERT_EQ(DBUS_ERROR_INVALID_ARGS, response->GetErrorName());
489}
490
491namespace {
492
493bool SetInvalidProperty(brillo::ErrorPtr* error, Unused) {
494  brillo::Error::AddTo(error, FROM_HERE, errors::dbus::kDomain,
495                       DBUS_ERROR_INVALID_ARGS, "Invalid value");
496  return false;
497}
498
499}  // namespace
500
501TEST_F(ExportedPropertySetTest, SetFailsWithValidator) {
502  PropertyValidatorObserver<std::string> property_validator;
503  p_->string_prop_.SetAccessMode(ExportedPropertyBase::Access::kReadWrite);
504  p_->string_prop_.SetValidator(
505      property_validator.validate_property_callback());
506
507  brillo::ErrorPtr error = brillo::Error::Create(
508      FROM_HERE, errors::dbus::kDomain, DBUS_ERROR_INVALID_ARGS, "");
509  EXPECT_CALL(property_validator, ValidateProperty(_, kTestString))
510      .WillOnce(Invoke(SetInvalidProperty));
511  auto response = SetPropertyOnInterface(
512      kTestInterface3, kStringPropName, brillo::Any(kTestString));
513  ASSERT_EQ(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
514  ASSERT_EQ(DBUS_ERROR_INVALID_ARGS, response->GetErrorName());
515}
516
517TEST_F(ExportedPropertySetTest, SetWorksWithValidator) {
518  PropertyValidatorObserver<std::string> property_validator;
519  p_->string_prop_.SetAccessMode(ExportedPropertyBase::Access::kReadWrite);
520  p_->string_prop_.SetValidator(
521      property_validator.validate_property_callback());
522
523  EXPECT_CALL(property_validator, ValidateProperty(_, kTestString))
524      .WillOnce(Return(true));
525  auto response = SetPropertyOnInterface(
526      kTestInterface3, kStringPropName, brillo::Any(kTestString));
527  ASSERT_NE(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
528  ASSERT_EQ(kTestString, p_->string_prop_.value());
529}
530
531TEST_F(ExportedPropertySetTest, SetWorksWithSameValue) {
532  PropertyValidatorObserver<std::string> property_validator;
533  p_->string_prop_.SetAccessMode(ExportedPropertyBase::Access::kReadWrite);
534  p_->string_prop_.SetValidator(
535      property_validator.validate_property_callback());
536  EXPECT_CALL(*mock_exported_object_, SendSignal(_)).Times(1);
537  p_->string_prop_.SetValue(kTestString);
538
539  // No need to validate the value if it is the same as the current one.
540  EXPECT_CALL(property_validator, ValidateProperty(_, _)).Times(0);
541  auto response = SetPropertyOnInterface(
542      kTestInterface3, kStringPropName, brillo::Any(kTestString));
543  ASSERT_NE(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
544  ASSERT_EQ(kTestString, p_->string_prop_.value());
545}
546
547TEST_F(ExportedPropertySetTest, SetWorksWithoutValidator) {
548  p_->string_prop_.SetAccessMode(ExportedPropertyBase::Access::kReadWrite);
549  auto response = SetPropertyOnInterface(
550      kTestInterface3, kStringPropName, brillo::Any(kTestString));
551  ASSERT_NE(dbus::Message::MESSAGE_ERROR, response->GetMessageType());
552  ASSERT_EQ(kTestString, p_->string_prop_.value());
553}
554
555namespace {
556
557void VerifySignal(dbus::Signal* signal) {
558  ASSERT_NE(signal, nullptr);
559  std::string interface_name;
560  std::string property_name;
561  uint8_t value;
562  dbus::MessageReader reader(signal);
563  dbus::MessageReader array_reader(signal);
564  dbus::MessageReader dict_reader(signal);
565  ASSERT_TRUE(reader.PopString(&interface_name));
566  ASSERT_TRUE(reader.PopArray(&array_reader));
567  ASSERT_TRUE(array_reader.PopDictEntry(&dict_reader));
568  ASSERT_TRUE(dict_reader.PopString(&property_name));
569  ASSERT_TRUE(dict_reader.PopVariantOfByte(&value));
570  ASSERT_FALSE(dict_reader.HasMoreData());
571  ASSERT_FALSE(array_reader.HasMoreData());
572  ASSERT_TRUE(reader.HasMoreData());
573  // Read the (empty) list of invalidated property names.
574  ASSERT_TRUE(reader.PopArray(&array_reader));
575  ASSERT_FALSE(array_reader.HasMoreData());
576  ASSERT_FALSE(reader.HasMoreData());
577  ASSERT_EQ(value, 57);
578  ASSERT_EQ(property_name, std::string(kUint8PropName));
579  ASSERT_EQ(interface_name, std::string(kTestInterface1));
580}
581
582}  // namespace
583
584TEST_F(ExportedPropertySetTest, SignalsAreParsable) {
585  EXPECT_CALL(*mock_exported_object_, SendSignal(_))
586      .Times(1).WillOnce(Invoke(&VerifySignal));
587  p_->uint8_prop_.SetValue(57);
588}
589
590}  // namespace dbus_utils
591
592}  // namespace brillo
593