// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/safe_browsing/preference_validation_delegate.h" #include #include #include "base/bind.h" #include "base/compiler_specific.h" #include "base/memory/scoped_vector.h" #include "base/values.h" #include "chrome/common/safe_browsing/csd.pb.h" #include "testing/gtest/include/gtest/gtest.h" // A basic test harness that creates a delegate instance for which it stores all // incidents. Tests can push data to the delegate and verify that the test // instance was provided with the expected data. class PreferenceValidationDelegateTest : public testing::Test { protected: typedef ScopedVector IncidentVector; PreferenceValidationDelegateTest() : kPrefPath_("atomic.pref"), null_value_(base::Value::CreateNullValue()) {} virtual void SetUp() OVERRIDE { testing::Test::SetUp(); invalid_keys_.push_back(std::string("one")); invalid_keys_.push_back(std::string("two")); instance_.reset(new safe_browsing::PreferenceValidationDelegate( base::Bind(&PreferenceValidationDelegateTest::AddIncident, base::Unretained(this)))); } void AddIncident( scoped_ptr data) { incidents_.push_back(data.release()); } static void ExpectValueStatesEquate( PrefHashStoreTransaction::ValueState store_state, safe_browsing:: ClientIncidentReport_IncidentData_TrackedPreferenceIncident_ValueState incident_state) { typedef safe_browsing:: ClientIncidentReport_IncidentData_TrackedPreferenceIncident TPIncident; switch (store_state) { case PrefHashStoreTransaction::CLEARED: EXPECT_EQ(TPIncident::CLEARED, incident_state); break; case PrefHashStoreTransaction::WEAK_LEGACY: EXPECT_EQ(TPIncident::WEAK_LEGACY, incident_state); break; case PrefHashStoreTransaction::CHANGED: EXPECT_EQ(TPIncident::CHANGED, incident_state); break; case PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE: EXPECT_EQ(TPIncident::UNTRUSTED_UNKNOWN_VALUE, incident_state); break; default: FAIL() << "unexpected store state"; break; } } static void ExpectKeysEquate( const std::vector& store_keys, const google::protobuf::RepeatedPtrField& incident_keys) { ASSERT_EQ(store_keys.size(), static_cast(incident_keys.size())); for (int i = 0; i < incident_keys.size(); ++i) { EXPECT_EQ(store_keys[i], incident_keys.Get(i)); } } const std::string kPrefPath_; IncidentVector incidents_; scoped_ptr null_value_; base::DictionaryValue dict_value_; std::vector invalid_keys_; scoped_ptr instance_; }; // Tests that a NULL value results in an incident with no value. TEST_F(PreferenceValidationDelegateTest, NullValue) { instance_->OnAtomicPreferenceValidation(kPrefPath_, NULL, PrefHashStoreTransaction::CLEARED, TrackedPreferenceHelper::DONT_RESET); safe_browsing::ClientIncidentReport_IncidentData* incident = incidents_.back(); EXPECT_FALSE(incident->tracked_preference().has_atomic_value()); EXPECT_EQ( safe_browsing:: ClientIncidentReport_IncidentData_TrackedPreferenceIncident::CLEARED, incident->tracked_preference().value_state()); } // Tests that all supported value types can be stringified into an incident. The // parameters for the test are the type of value to test and the expected value // string. class PreferenceValidationDelegateValues : public PreferenceValidationDelegateTest, public testing::WithParamInterface< std::tr1::tuple > { protected: virtual void SetUp() OVERRIDE { PreferenceValidationDelegateTest::SetUp(); value_type_ = std::tr1::get<0>(GetParam()); expected_value_ = std::tr1::get<1>(GetParam()); } static scoped_ptr MakeValue(base::Value::Type value_type) { using base::Value; switch (value_type) { case Value::TYPE_NULL: return make_scoped_ptr(Value::CreateNullValue()); case Value::TYPE_BOOLEAN: return scoped_ptr(new base::FundamentalValue(false)); case Value::TYPE_INTEGER: return scoped_ptr(new base::FundamentalValue(47)); case Value::TYPE_DOUBLE: return scoped_ptr(new base::FundamentalValue(0.47)); case Value::TYPE_STRING: return scoped_ptr(new base::StringValue("i have a spleen")); case Value::TYPE_DICTIONARY: { scoped_ptr value(new base::DictionaryValue()); value->SetInteger("twenty-two", 22); value->SetInteger("forty-seven", 47); return value.PassAs(); } case Value::TYPE_LIST: { scoped_ptr value(new base::ListValue()); value->AppendInteger(22); value->AppendInteger(47); return value.PassAs(); } default: ADD_FAILURE() << "unsupported value type " << value_type; } return scoped_ptr(); } base::Value::Type value_type_; const char* expected_value_; }; TEST_P(PreferenceValidationDelegateValues, Value) { instance_->OnAtomicPreferenceValidation(kPrefPath_, MakeValue(value_type_).get(), PrefHashStoreTransaction::CLEARED, TrackedPreferenceHelper::DONT_RESET); ASSERT_EQ(1U, incidents_.size()); safe_browsing::ClientIncidentReport_IncidentData* incident = incidents_.back(); EXPECT_EQ(std::string(expected_value_), incident->tracked_preference().atomic_value()); } INSTANTIATE_TEST_CASE_P( Values, PreferenceValidationDelegateValues, testing::Values( std::tr1::make_tuple(base::Value::TYPE_NULL, "null"), std::tr1::make_tuple(base::Value::TYPE_BOOLEAN, "false"), std::tr1::make_tuple(base::Value::TYPE_INTEGER, "47"), std::tr1::make_tuple(base::Value::TYPE_DOUBLE, "0.47"), std::tr1::make_tuple(base::Value::TYPE_STRING, "i have a spleen"), std::tr1::make_tuple(base::Value::TYPE_DICTIONARY, "{\"forty-seven\":47,\"twenty-two\":22}"), std::tr1::make_tuple(base::Value::TYPE_LIST, "[22,47]"))); // Tests that no incidents are reported for relevant combinations of ValueState. class PreferenceValidationDelegateNoIncident : public PreferenceValidationDelegateTest, public testing::WithParamInterface { protected: virtual void SetUp() OVERRIDE { PreferenceValidationDelegateTest::SetUp(); value_state_ = GetParam(); } PrefHashStoreTransaction::ValueState value_state_; }; TEST_P(PreferenceValidationDelegateNoIncident, Atomic) { instance_->OnAtomicPreferenceValidation(kPrefPath_, null_value_.get(), value_state_, TrackedPreferenceHelper::DONT_RESET); EXPECT_EQ(0U, incidents_.size()); } TEST_P(PreferenceValidationDelegateNoIncident, Split) { instance_->OnSplitPreferenceValidation(kPrefPath_, &dict_value_, invalid_keys_, value_state_, TrackedPreferenceHelper::DONT_RESET); EXPECT_EQ(0U, incidents_.size()); } INSTANTIATE_TEST_CASE_P( NoIncident, PreferenceValidationDelegateNoIncident, testing::Values(PrefHashStoreTransaction::UNCHANGED, PrefHashStoreTransaction::SECURE_LEGACY, PrefHashStoreTransaction::TRUSTED_UNKNOWN_VALUE)); // Tests that incidents are reported for relevant combinations of ValueState and // ResetAction. class PreferenceValidationDelegateWithIncident : public PreferenceValidationDelegateTest, public testing::WithParamInterface< std::tr1::tuple > { protected: virtual void SetUp() OVERRIDE { PreferenceValidationDelegateTest::SetUp(); value_state_ = std::tr1::get<0>(GetParam()); reset_action_ = std::tr1::get<1>(GetParam()); } PrefHashStoreTransaction::ValueState value_state_; TrackedPreferenceHelper::ResetAction reset_action_; }; TEST_P(PreferenceValidationDelegateWithIncident, Atomic) { instance_->OnAtomicPreferenceValidation( kPrefPath_, null_value_.get(), value_state_, reset_action_); ASSERT_EQ(1U, incidents_.size()); safe_browsing::ClientIncidentReport_IncidentData* incident = incidents_.back(); EXPECT_TRUE(incident->has_tracked_preference()); const safe_browsing:: ClientIncidentReport_IncidentData_TrackedPreferenceIncident& tp_incident = incident->tracked_preference(); EXPECT_EQ(kPrefPath_, tp_incident.path()); EXPECT_EQ(0, tp_incident.split_key_size()); EXPECT_TRUE(tp_incident.has_atomic_value()); EXPECT_EQ(std::string("null"), tp_incident.atomic_value()); EXPECT_TRUE(tp_incident.has_value_state()); ExpectValueStatesEquate(value_state_, tp_incident.value_state()); } TEST_P(PreferenceValidationDelegateWithIncident, Split) { instance_->OnSplitPreferenceValidation( kPrefPath_, &dict_value_, invalid_keys_, value_state_, reset_action_); ASSERT_EQ(1U, incidents_.size()); safe_browsing::ClientIncidentReport_IncidentData* incident = incidents_.back(); EXPECT_TRUE(incident->has_tracked_preference()); const safe_browsing:: ClientIncidentReport_IncidentData_TrackedPreferenceIncident& tp_incident = incident->tracked_preference(); EXPECT_EQ(kPrefPath_, tp_incident.path()); EXPECT_FALSE(tp_incident.has_atomic_value()); ExpectKeysEquate(invalid_keys_, tp_incident.split_key()); EXPECT_TRUE(tp_incident.has_value_state()); ExpectValueStatesEquate(value_state_, tp_incident.value_state()); } INSTANTIATE_TEST_CASE_P( WithIncident, PreferenceValidationDelegateWithIncident, testing::Combine( testing::Values(PrefHashStoreTransaction::CLEARED, PrefHashStoreTransaction::WEAK_LEGACY, PrefHashStoreTransaction::CHANGED, PrefHashStoreTransaction::UNTRUSTED_UNKNOWN_VALUE), testing::Values(TrackedPreferenceHelper::WANTED_RESET, TrackedPreferenceHelper::DO_RESET)));