Darin Petkov | b451d6e | 2012-04-23 11:56:41 +0200 | [diff] [blame] | 1 | // Copyright (c) 2012 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 "shill/vpn_driver.h" |
| 6 | |
| 7 | #include <base/stl_util.h> |
| 8 | #include <base/string_number_conversions.h> |
| 9 | #include <chromeos/dbus/service_constants.h> |
| 10 | #include <gmock/gmock.h> |
| 11 | #include <gtest/gtest.h> |
| 12 | |
Darin Petkov | 0e9735d | 2012-04-24 12:33:45 +0200 | [diff] [blame] | 13 | #include "shill/event_dispatcher.h" |
| 14 | #include "shill/glib.h" |
| 15 | #include "shill/mock_connection.h" |
| 16 | #include "shill/mock_device_info.h" |
| 17 | #include "shill/mock_glib.h" |
| 18 | #include "shill/mock_manager.h" |
| 19 | #include "shill/mock_metrics.h" |
| 20 | #include "shill/mock_service.h" |
Darin Petkov | b451d6e | 2012-04-23 11:56:41 +0200 | [diff] [blame] | 21 | #include "shill/mock_store.h" |
Darin Petkov | 0e9735d | 2012-04-24 12:33:45 +0200 | [diff] [blame] | 22 | #include "shill/nice_mock_control.h" |
Darin Petkov | b451d6e | 2012-04-23 11:56:41 +0200 | [diff] [blame] | 23 | #include "shill/property_store.h" |
| 24 | |
| 25 | using std::string; |
| 26 | using testing::_; |
| 27 | using testing::AnyNumber; |
Darin Petkov | 0e9735d | 2012-04-24 12:33:45 +0200 | [diff] [blame] | 28 | using testing::NiceMock; |
Darin Petkov | b451d6e | 2012-04-23 11:56:41 +0200 | [diff] [blame] | 29 | using testing::Return; |
| 30 | using testing::SetArgumentPointee; |
Darin Petkov | 0e9735d | 2012-04-24 12:33:45 +0200 | [diff] [blame] | 31 | using testing::StrictMock; |
Darin Petkov | b451d6e | 2012-04-23 11:56:41 +0200 | [diff] [blame] | 32 | using testing::Test; |
| 33 | |
| 34 | namespace shill { |
| 35 | |
| 36 | namespace { |
| 37 | const char kHostProperty[] = "VPN.Host"; |
| 38 | const char kOTPProperty[] = "VPN.OTP"; |
| 39 | const char kPasswordProperty[] = "VPN.Password"; |
| 40 | const char kPortProperty[] = "VPN.Port"; |
| 41 | } // namespace |
| 42 | |
| 43 | class VPNDriverUnderTest : public VPNDriver { |
| 44 | public: |
Darin Petkov | 0e9735d | 2012-04-24 12:33:45 +0200 | [diff] [blame] | 45 | VPNDriverUnderTest(Manager *manager); |
Darin Petkov | b451d6e | 2012-04-23 11:56:41 +0200 | [diff] [blame] | 46 | virtual ~VPNDriverUnderTest() {} |
| 47 | |
| 48 | // Inherited from VPNDriver. |
| 49 | MOCK_METHOD2(ClaimInterface, bool(const string &link_name, |
| 50 | int interface_index)); |
| 51 | MOCK_METHOD2(Connect, void(const VPNServiceRefPtr &service, Error *error)); |
| 52 | MOCK_METHOD0(Disconnect, void()); |
| 53 | MOCK_CONST_METHOD0(GetProviderType, string()); |
| 54 | |
| 55 | private: |
| 56 | static const Property kProperties[]; |
| 57 | |
| 58 | DISALLOW_COPY_AND_ASSIGN(VPNDriverUnderTest); |
| 59 | }; |
| 60 | |
| 61 | // static |
| 62 | const VPNDriverUnderTest::Property VPNDriverUnderTest::kProperties[] = { |
| 63 | { kHostProperty, 0 }, |
| 64 | { kOTPProperty, Property::kEphemeral | Property::kCrypted }, |
| 65 | { kPasswordProperty, Property::kCrypted }, |
| 66 | { kPortProperty, 0 }, |
| 67 | { flimflam::kProviderNameProperty, 0 }, |
| 68 | }; |
| 69 | |
Darin Petkov | 0e9735d | 2012-04-24 12:33:45 +0200 | [diff] [blame] | 70 | VPNDriverUnderTest::VPNDriverUnderTest(Manager *manager) |
| 71 | : VPNDriver(manager, kProperties, arraysize(kProperties)) {} |
Darin Petkov | b451d6e | 2012-04-23 11:56:41 +0200 | [diff] [blame] | 72 | |
| 73 | class VPNDriverTest : public Test { |
| 74 | public: |
Darin Petkov | 0e9735d | 2012-04-24 12:33:45 +0200 | [diff] [blame] | 75 | VPNDriverTest() |
| 76 | : device_info_(&control_, &dispatcher_, &metrics_, &manager_), |
| 77 | manager_(&control_, &dispatcher_, &metrics_, &glib_), |
| 78 | driver_(&manager_) {} |
Darin Petkov | b451d6e | 2012-04-23 11:56:41 +0200 | [diff] [blame] | 79 | |
| 80 | virtual ~VPNDriverTest() {} |
| 81 | |
| 82 | protected: |
| 83 | void SetArg(const string &arg, const string &value) { |
| 84 | driver_.args()->SetString(arg, value); |
| 85 | } |
| 86 | |
| 87 | KeyValueStore *GetArgs() { return driver_.args(); } |
| 88 | |
| 89 | bool FindStringPropertyInStore(const PropertyStore &store, |
| 90 | const string &key, |
| 91 | string *value, |
| 92 | Error *error); |
| 93 | bool FindStringmapPropertyInStore(const PropertyStore &store, |
| 94 | const string &key, |
| 95 | Stringmap *value, |
| 96 | Error *error); |
| 97 | |
Darin Petkov | 0e9735d | 2012-04-24 12:33:45 +0200 | [diff] [blame] | 98 | NiceMockControl control_; |
| 99 | NiceMock<MockDeviceInfo> device_info_; |
| 100 | EventDispatcher dispatcher_; |
| 101 | MockMetrics metrics_; |
| 102 | MockGLib glib_; |
| 103 | MockManager manager_; |
Darin Petkov | b451d6e | 2012-04-23 11:56:41 +0200 | [diff] [blame] | 104 | VPNDriverUnderTest driver_; |
| 105 | }; |
| 106 | |
| 107 | bool VPNDriverTest::FindStringPropertyInStore(const PropertyStore &store, |
| 108 | const string &key, |
| 109 | string *value, |
| 110 | Error *error) { |
| 111 | ReadablePropertyConstIterator<std::string> it = |
| 112 | store.GetStringPropertiesIter(); |
| 113 | for ( ; !it.AtEnd(); it.Advance()) { |
| 114 | if (it.Key() == key) { |
| 115 | *value = it.Value(error); |
| 116 | return error->IsSuccess(); |
| 117 | } |
| 118 | } |
| 119 | error->Populate(Error::kNotFound); |
| 120 | return false; |
| 121 | } |
| 122 | |
| 123 | bool VPNDriverTest::FindStringmapPropertyInStore(const PropertyStore &store, |
| 124 | const string &key, |
| 125 | Stringmap *value, |
| 126 | Error *error) { |
| 127 | ReadablePropertyConstIterator<Stringmap> it = |
| 128 | store.GetStringmapPropertiesIter(); |
| 129 | for ( ; !it.AtEnd(); it.Advance()) { |
| 130 | if (it.Key() == key) { |
| 131 | *value = it.Value(error); |
| 132 | return error->IsSuccess(); |
| 133 | } |
| 134 | } |
| 135 | error->Populate(Error::kNotFound); |
| 136 | return false; |
| 137 | } |
| 138 | |
| 139 | TEST_F(VPNDriverTest, Load) { |
| 140 | MockStore storage; |
| 141 | static const char kStorageID[] = "vpn_service_id"; |
| 142 | const string kPort = "1234"; |
| 143 | const string kPassword = "random-password"; |
| 144 | EXPECT_CALL(storage, GetString(kStorageID, _, _)) |
| 145 | .WillRepeatedly(Return(false)); |
| 146 | EXPECT_CALL(storage, GetString(kStorageID, kOTPProperty, _)).Times(0); |
| 147 | EXPECT_CALL(storage, GetCryptedString(kStorageID, kOTPProperty, _)).Times(0); |
| 148 | EXPECT_CALL(storage, GetString(kStorageID, kPortProperty, _)) |
| 149 | .WillOnce(DoAll(SetArgumentPointee<2>(kPort), Return(true))); |
| 150 | EXPECT_CALL(storage, GetCryptedString(kStorageID, kPasswordProperty, _)) |
| 151 | .WillOnce(DoAll(SetArgumentPointee<2>(kPassword), Return(true))); |
| 152 | EXPECT_TRUE(driver_.Load(&storage, kStorageID)); |
| 153 | EXPECT_EQ(kPort, GetArgs()->LookupString(kPortProperty, "")); |
| 154 | EXPECT_EQ(kPassword, GetArgs()->LookupString(kPasswordProperty, "")); |
| 155 | } |
| 156 | |
| 157 | TEST_F(VPNDriverTest, Store) { |
| 158 | const string kPort = "1234"; |
| 159 | const string kPassword = "foobar"; |
| 160 | SetArg(kPortProperty, kPort); |
| 161 | SetArg(kPasswordProperty, kPassword); |
| 162 | SetArg(kOTPProperty, "987654"); |
| 163 | MockStore storage; |
| 164 | static const char kStorageID[] = "vpn_service_id"; |
| 165 | EXPECT_CALL(storage, SetString(kStorageID, kPortProperty, kPort)) |
| 166 | .WillOnce(Return(true)); |
| 167 | EXPECT_CALL(storage, |
| 168 | SetCryptedString(kStorageID, kPasswordProperty, kPassword)) |
| 169 | .WillOnce(Return(true)); |
| 170 | EXPECT_CALL(storage, SetCryptedString(kStorageID, kOTPProperty, _)).Times(0); |
| 171 | EXPECT_CALL(storage, SetString(kStorageID, kOTPProperty, _)).Times(0); |
| 172 | EXPECT_CALL(storage, DeleteKey(kStorageID, _)).Times(AnyNumber()); |
| 173 | EXPECT_CALL(storage, DeleteKey(kStorageID, kHostProperty)).Times(1); |
| 174 | EXPECT_TRUE(driver_.Save(&storage, kStorageID)); |
| 175 | } |
| 176 | |
| 177 | TEST_F(VPNDriverTest, InitPropertyStore) { |
| 178 | // Figure out if the store is actually hooked up to the driver argument |
| 179 | // KeyValueStore. |
| 180 | PropertyStore store; |
| 181 | driver_.InitPropertyStore(&store); |
| 182 | |
| 183 | // An un-set property should not be readable. |
| 184 | { |
| 185 | Error error; |
| 186 | string string_property; |
| 187 | EXPECT_FALSE( |
| 188 | FindStringPropertyInStore( |
| 189 | store, kPortProperty, &string_property, &error)); |
| 190 | EXPECT_EQ(Error::kNotFound, error.type()); |
| 191 | } |
| 192 | |
| 193 | const string kPort = "1234"; |
| 194 | const string kPassword = "foobar"; |
| 195 | const string kProviderName = "boo"; |
| 196 | SetArg(kPortProperty, kPort); |
| 197 | SetArg(kPasswordProperty, kPassword); |
| 198 | SetArg(flimflam::kProviderNameProperty, kProviderName); |
| 199 | |
| 200 | // We should not be able to read a property out of the driver args using the |
| 201 | // key to the args directly. |
| 202 | { |
| 203 | Error error; |
| 204 | string string_property; |
| 205 | EXPECT_FALSE( |
| 206 | FindStringPropertyInStore( |
| 207 | store, kPortProperty, &string_property, &error)); |
| 208 | EXPECT_EQ(Error::kNotFound, error.type()); |
| 209 | } |
| 210 | |
| 211 | // We should instead be able to find it within the "Provider" stringmap. |
| 212 | { |
| 213 | Error error; |
| 214 | Stringmap provider_properties; |
| 215 | EXPECT_TRUE( |
| 216 | FindStringmapPropertyInStore( |
| 217 | store, flimflam::kProviderProperty, &provider_properties, &error)); |
| 218 | EXPECT_TRUE(ContainsKey(provider_properties, kPortProperty)); |
| 219 | EXPECT_EQ(kPort, provider_properties[kPortProperty]); |
| 220 | } |
| 221 | |
| 222 | // Properties that start with the prefix "Provider." should be mapped to the |
| 223 | // name in the Properties dict with the prefix removed. |
| 224 | { |
| 225 | Error error; |
| 226 | Stringmap provider_properties; |
| 227 | EXPECT_TRUE( |
| 228 | FindStringmapPropertyInStore( |
| 229 | store, flimflam::kProviderProperty, &provider_properties, &error)); |
| 230 | EXPECT_TRUE(ContainsKey(provider_properties, flimflam::kNameProperty)); |
| 231 | EXPECT_EQ(kProviderName, provider_properties[flimflam::kNameProperty]); |
| 232 | } |
| 233 | |
| 234 | // If we clear this property, we should no longer be able to find it. |
| 235 | { |
| 236 | Error error; |
| 237 | EXPECT_TRUE(store.ClearProperty(kPortProperty, &error)); |
| 238 | EXPECT_TRUE(error.IsSuccess()); |
| 239 | string string_property; |
| 240 | EXPECT_FALSE( |
| 241 | FindStringPropertyInStore( |
| 242 | store, kPortProperty, &string_property, &error)); |
| 243 | EXPECT_EQ(Error::kNotFound, error.type()); |
| 244 | } |
| 245 | |
| 246 | // A second attempt to clear this property should return an error. |
| 247 | { |
| 248 | Error error; |
| 249 | EXPECT_FALSE(store.ClearProperty(kPortProperty, &error)); |
| 250 | EXPECT_EQ(Error::kNotFound, error.type()); |
| 251 | } |
| 252 | |
| 253 | // These ones should be write-only. |
| 254 | static const char * const kWriteOnly[] = { |
| 255 | kPasswordProperty, |
| 256 | kOTPProperty |
| 257 | }; |
| 258 | |
| 259 | for (size_t i = 0; i < arraysize(kWriteOnly); i++) { |
| 260 | Error error; |
| 261 | string string_property; |
| 262 | EXPECT_FALSE( |
| 263 | FindStringPropertyInStore( |
| 264 | store, kWriteOnly[i], &string_property, &error)) << kWriteOnly[i]; |
| 265 | // We get NotFound here instead of PermissionDenied here due to the |
| 266 | // implementation of ReadablePropertyConstIterator: it shields us from |
| 267 | // store members for which Value() would have returned an error. |
| 268 | EXPECT_EQ(Error::kNotFound, error.type()) << kWriteOnly[i]; |
| 269 | } |
| 270 | |
| 271 | // Write properties to the driver args using the PropertyStore interface. |
| 272 | for (size_t i = 0; i < arraysize(kWriteOnly); i++) { |
| 273 | string value = "some-value-" + base::UintToString(i); |
| 274 | Error error; |
| 275 | EXPECT_TRUE(store.SetStringProperty(kWriteOnly[i], value, &error)) |
| 276 | << kWriteOnly[i]; |
| 277 | EXPECT_EQ(value, GetArgs()->GetString(kWriteOnly[i])) << kWriteOnly[i]; |
| 278 | } |
| 279 | } |
| 280 | |
Darin Petkov | b451d6e | 2012-04-23 11:56:41 +0200 | [diff] [blame] | 281 | } // namespace shill |