1// Copyright 2014 The Chromium 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 "chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.h" 6 7#include <string> 8#include <vector> 9 10#include "base/bind.h" 11#include "base/compiler_specific.h" 12#include "base/memory/scoped_vector.h" 13#include "base/values.h" 14#include "chrome/common/safe_browsing/csd.pb.h" 15#include "testing/gtest/include/gtest/gtest.h" 16 17// A basic test harness that creates a delegate instance for which it stores all 18// incidents. Tests can push data to the delegate and verify that the test 19// instance was provided with the expected data. 20class PreferenceValidationDelegateTest : public testing::Test { 21 protected: 22 typedef ScopedVector<safe_browsing::ClientIncidentReport_IncidentData> 23 IncidentVector; 24 25 PreferenceValidationDelegateTest() 26 : kPrefPath_("atomic.pref"), 27 null_value_(base::Value::CreateNullValue()) {} 28 29 virtual void SetUp() OVERRIDE { 30 testing::Test::SetUp(); 31 invalid_keys_.push_back(std::string("one")); 32 invalid_keys_.push_back(std::string("two")); 33 instance_.reset(new safe_browsing::PreferenceValidationDelegate( 34 base::Bind(&PreferenceValidationDelegateTest::AddIncident, 35 base::Unretained(this)))); 36 } 37 38 void AddIncident( 39 scoped_ptr<safe_browsing::ClientIncidentReport_IncidentData> data) { 40 incidents_.push_back(data.release()); 41 } 42 43 static void ExpectValueStatesEquate( 44 PrefHashStoreTransaction::ValueState store_state, 45 safe_browsing:: 46 ClientIncidentReport_IncidentData_TrackedPreferenceIncident_ValueState 47 incident_state) { 48 typedef safe_browsing:: 49 ClientIncidentReport_IncidentData_TrackedPreferenceIncident TPIncident; 50 switch (store_state) { 51 case PrefHashStoreTransaction::CLEARED: 52 EXPECT_EQ(TPIncident::CLEARED, incident_state); 53 break; 54 case PrefHashStoreTransaction::CHANGED: 55 EXPECT_EQ(TPIncident::CHANGED, incident_state); 56 break; 57 case PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE: 58 EXPECT_EQ(TPIncident::UNTRUSTED_UNKNOWN_VALUE, incident_state); 59 break; 60 default: 61 FAIL() << "unexpected store state"; 62 break; 63 } 64 } 65 66 static void ExpectKeysEquate( 67 const std::vector<std::string>& store_keys, 68 const google::protobuf::RepeatedPtrField<std::string>& incident_keys) { 69 ASSERT_EQ(store_keys.size(), static_cast<size_t>(incident_keys.size())); 70 for (int i = 0; i < incident_keys.size(); ++i) { 71 EXPECT_EQ(store_keys[i], incident_keys.Get(i)); 72 } 73 } 74 75 const std::string kPrefPath_; 76 IncidentVector incidents_; 77 scoped_ptr<base::Value> null_value_; 78 base::DictionaryValue dict_value_; 79 std::vector<std::string> invalid_keys_; 80 scoped_ptr<TrackedPreferenceValidationDelegate> instance_; 81}; 82 83// Tests that a NULL value results in an incident with no value. 84TEST_F(PreferenceValidationDelegateTest, NullValue) { 85 instance_->OnAtomicPreferenceValidation(kPrefPath_, 86 NULL, 87 PrefHashStoreTransaction::CLEARED, 88 TrackedPreferenceHelper::DONT_RESET); 89 safe_browsing::ClientIncidentReport_IncidentData* incident = 90 incidents_.back(); 91 EXPECT_FALSE(incident->tracked_preference().has_atomic_value()); 92 EXPECT_EQ( 93 safe_browsing:: 94 ClientIncidentReport_IncidentData_TrackedPreferenceIncident::CLEARED, 95 incident->tracked_preference().value_state()); 96} 97 98// Tests that all supported value types can be stringified into an incident. The 99// parameters for the test are the type of value to test and the expected value 100// string. 101class PreferenceValidationDelegateValues 102 : public PreferenceValidationDelegateTest, 103 public testing::WithParamInterface< 104 std::tr1::tuple<base::Value::Type, const char*> > { 105 protected: 106 virtual void SetUp() OVERRIDE { 107 PreferenceValidationDelegateTest::SetUp(); 108 value_type_ = std::tr1::get<0>(GetParam()); 109 expected_value_ = std::tr1::get<1>(GetParam()); 110 } 111 112 static scoped_ptr<base::Value> MakeValue(base::Value::Type value_type) { 113 using base::Value; 114 switch (value_type) { 115 case Value::TYPE_NULL: 116 return make_scoped_ptr(Value::CreateNullValue()); 117 case Value::TYPE_BOOLEAN: 118 return scoped_ptr<Value>(new base::FundamentalValue(false)); 119 case Value::TYPE_INTEGER: 120 return scoped_ptr<Value>(new base::FundamentalValue(47)); 121 case Value::TYPE_DOUBLE: 122 return scoped_ptr<Value>(new base::FundamentalValue(0.47)); 123 case Value::TYPE_STRING: 124 return scoped_ptr<Value>(new base::StringValue("i have a spleen")); 125 case Value::TYPE_DICTIONARY: { 126 scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue()); 127 value->SetInteger("twenty-two", 22); 128 value->SetInteger("forty-seven", 47); 129 return value.PassAs<Value>(); 130 } 131 case Value::TYPE_LIST: { 132 scoped_ptr<base::ListValue> value(new base::ListValue()); 133 value->AppendInteger(22); 134 value->AppendInteger(47); 135 return value.PassAs<Value>(); 136 } 137 default: 138 ADD_FAILURE() << "unsupported value type " << value_type; 139 } 140 return scoped_ptr<Value>(); 141 } 142 143 base::Value::Type value_type_; 144 const char* expected_value_; 145}; 146 147TEST_P(PreferenceValidationDelegateValues, Value) { 148 instance_->OnAtomicPreferenceValidation(kPrefPath_, 149 MakeValue(value_type_).get(), 150 PrefHashStoreTransaction::CLEARED, 151 TrackedPreferenceHelper::DONT_RESET); 152 ASSERT_EQ(1U, incidents_.size()); 153 safe_browsing::ClientIncidentReport_IncidentData* incident = 154 incidents_.back(); 155 EXPECT_EQ(std::string(expected_value_), 156 incident->tracked_preference().atomic_value()); 157} 158 159INSTANTIATE_TEST_CASE_P( 160 Values, 161 PreferenceValidationDelegateValues, 162 // On Android, make_tuple(..., "null") doesn't compile due to the error: 163 // testing/gtest/include/gtest/internal/gtest-tuple.h:246:48: 164 // error: array used as initializer 165 testing::Values( 166 std::tr1::make_tuple(base::Value::TYPE_NULL, 167 const_cast<char*>("null")), 168 std::tr1::make_tuple(base::Value::TYPE_BOOLEAN, 169 const_cast<char*>("false")), 170 std::tr1::make_tuple(base::Value::TYPE_INTEGER, 171 const_cast<char*>("47")), 172 std::tr1::make_tuple(base::Value::TYPE_DOUBLE, 173 const_cast<char*>("0.47")), 174 std::tr1::make_tuple(base::Value::TYPE_STRING, 175 const_cast<char*>("i have a spleen")), 176 std::tr1::make_tuple(base::Value::TYPE_DICTIONARY, 177 const_cast<char*>("{\"forty-seven\":47,\"twenty-two\":22}")), 178 std::tr1::make_tuple(base::Value::TYPE_LIST, 179 const_cast<char*>("[22,47]")))); 180 181// Tests that no incidents are reported for relevant combinations of ValueState. 182class PreferenceValidationDelegateNoIncident 183 : public PreferenceValidationDelegateTest, 184 public testing::WithParamInterface<PrefHashStoreTransaction::ValueState> { 185 protected: 186 virtual void SetUp() OVERRIDE { 187 PreferenceValidationDelegateTest::SetUp(); 188 value_state_ = GetParam(); 189 } 190 191 PrefHashStoreTransaction::ValueState value_state_; 192}; 193 194TEST_P(PreferenceValidationDelegateNoIncident, Atomic) { 195 instance_->OnAtomicPreferenceValidation(kPrefPath_, 196 null_value_.get(), 197 value_state_, 198 TrackedPreferenceHelper::DONT_RESET); 199 EXPECT_EQ(0U, incidents_.size()); 200} 201 202TEST_P(PreferenceValidationDelegateNoIncident, Split) { 203 instance_->OnSplitPreferenceValidation(kPrefPath_, 204 &dict_value_, 205 invalid_keys_, 206 value_state_, 207 TrackedPreferenceHelper::DONT_RESET); 208 EXPECT_EQ(0U, incidents_.size()); 209} 210 211INSTANTIATE_TEST_CASE_P( 212 NoIncident, 213 PreferenceValidationDelegateNoIncident, 214 testing::Values(PrefHashStoreTransaction::UNCHANGED, 215 PrefHashStoreTransaction::SECURE_LEGACY, 216 PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE)); 217 218// Tests that incidents are reported for relevant combinations of ValueState and 219// ResetAction. 220class PreferenceValidationDelegateWithIncident 221 : public PreferenceValidationDelegateTest, 222 public testing::WithParamInterface< 223 std::tr1::tuple<PrefHashStoreTransaction::ValueState, 224 TrackedPreferenceHelper::ResetAction> > { 225 protected: 226 virtual void SetUp() OVERRIDE { 227 PreferenceValidationDelegateTest::SetUp(); 228 value_state_ = std::tr1::get<0>(GetParam()); 229 reset_action_ = std::tr1::get<1>(GetParam()); 230 } 231 232 PrefHashStoreTransaction::ValueState value_state_; 233 TrackedPreferenceHelper::ResetAction reset_action_; 234}; 235 236TEST_P(PreferenceValidationDelegateWithIncident, Atomic) { 237 instance_->OnAtomicPreferenceValidation( 238 kPrefPath_, null_value_.get(), value_state_, reset_action_); 239 ASSERT_EQ(1U, incidents_.size()); 240 safe_browsing::ClientIncidentReport_IncidentData* incident = 241 incidents_.back(); 242 EXPECT_TRUE(incident->has_tracked_preference()); 243 const safe_browsing:: 244 ClientIncidentReport_IncidentData_TrackedPreferenceIncident& tp_incident = 245 incident->tracked_preference(); 246 EXPECT_EQ(kPrefPath_, tp_incident.path()); 247 EXPECT_EQ(0, tp_incident.split_key_size()); 248 EXPECT_TRUE(tp_incident.has_atomic_value()); 249 EXPECT_EQ(std::string("null"), tp_incident.atomic_value()); 250 EXPECT_TRUE(tp_incident.has_value_state()); 251 ExpectValueStatesEquate(value_state_, tp_incident.value_state()); 252} 253 254TEST_P(PreferenceValidationDelegateWithIncident, Split) { 255 instance_->OnSplitPreferenceValidation( 256 kPrefPath_, &dict_value_, invalid_keys_, value_state_, reset_action_); 257 ASSERT_EQ(1U, incidents_.size()); 258 safe_browsing::ClientIncidentReport_IncidentData* incident = 259 incidents_.back(); 260 EXPECT_TRUE(incident->has_tracked_preference()); 261 const safe_browsing:: 262 ClientIncidentReport_IncidentData_TrackedPreferenceIncident& tp_incident = 263 incident->tracked_preference(); 264 EXPECT_EQ(kPrefPath_, tp_incident.path()); 265 EXPECT_FALSE(tp_incident.has_atomic_value()); 266 ExpectKeysEquate(invalid_keys_, tp_incident.split_key()); 267 EXPECT_TRUE(tp_incident.has_value_state()); 268 ExpectValueStatesEquate(value_state_, tp_incident.value_state()); 269} 270 271INSTANTIATE_TEST_CASE_P( 272 WithIncident, 273 PreferenceValidationDelegateWithIncident, 274 testing::Combine( 275 testing::Values(PrefHashStoreTransaction::CLEARED, 276 PrefHashStoreTransaction::CHANGED, 277 PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE), 278 testing::Values(TrackedPreferenceHelper::WANTED_RESET, 279 TrackedPreferenceHelper::DO_RESET))); 280