普通文本  |  1629行  |  51.04 KB

//
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include "shill/cellular/mobile_operator_info.h"

#include <fstream>
#include <map>
#include <ostream>
#include <set>
#include <vector>

#include <base/files/file_util.h>
#include <base/macros.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include "shill/cellular/mobile_operator_info_impl.h"
#include "shill/logging.h"
#include "shill/test_event_dispatcher.h"

// These files contain binary protobuf definitions used by the following tests
// inside the namespace ::mobile_operator_db
#define IN_MOBILE_OPERATOR_INFO_UNITTEST_CC
#include "shill/mobile_operator_db/test_protos/data_test.h"
#include "shill/mobile_operator_db/test_protos/init_test_empty_db_init.h"
#include "shill/mobile_operator_db/test_protos/init_test_multiple_db_init_1.h"
#include "shill/mobile_operator_db/test_protos/init_test_multiple_db_init_2.h"
#include "shill/mobile_operator_db/test_protos/init_test_successful_init.h"
#include "shill/mobile_operator_db/test_protos/main_test.h"
#undef IN_MOBILE_OPERATOR_INFO_UNITTEST_CC

using base::FilePath;
using shill::mobile_operator_db::MobileOperatorDB;
using std::map;
using std::ofstream;
using std::set;
using std::string;
using std::vector;
using testing::Mock;
using testing::Test;
using testing::Values;
using testing::WithParamInterface;

// The tests run from the fixture |MobileOperatorInfoMainTest| and
// |MobileOperatorDataTest| can be run in two modes:
//   - strict event checking: We check that an event is raised for each update
//     to the state of the object.
//   - non-strict event checking: We check that a single event is raised as a
//     result of many updates to the object.
// The first case corresponds to a very aggressive event loop, that dispatches
// events as soon as they are posted; the second one corresponds to an
// over-crowded event loop that only dispatches events just before we verify
// that events were raised.
//
// We use ::testing::WithParamInterface to templatize the test fixtures to do
// string/non-strict event checking. When writing test cases using these
// fixtures, use the |Update*|, |ExpectEventCount|, |VerifyEventCount| functions
// provided by the fixture, and write the test as if event checking is strict.
//
// For |MobileOperatorObserverTest|, only the strict event checking case makes
// sense, so we only instantiate that.
namespace shill {

namespace {

enum EventCheckingPolicy {
  kEventCheckingPolicyStrict,
  kEventCheckingPolicyNonStrict
};

}  // namespace

class MockMobileOperatorInfoObserver : public MobileOperatorInfo::Observer {
 public:
  MockMobileOperatorInfoObserver() {}
  virtual ~MockMobileOperatorInfoObserver() {}

  MOCK_METHOD0(OnOperatorChanged, void());
};

class MobileOperatorInfoInitTest : public Test {
 public:
  MobileOperatorInfoInitTest()
      : operator_info_(new MobileOperatorInfo(&dispatcher_, "Operator")),
        operator_info_impl_(operator_info_->impl()) {}

  void TearDown() override {
    for (const auto& tmp_db_path : tmp_db_paths_) {
      base::DeleteFile(tmp_db_path, false);
    }
  }

 protected:
  void AddDatabase(const unsigned char database_data[], size_t num_elems) {
    FilePath tmp_db_path;
    CHECK(base::CreateTemporaryFile(&tmp_db_path));
    tmp_db_paths_.push_back(tmp_db_path);

    ofstream tmp_db(tmp_db_path.value(), ofstream::binary);
    for (size_t i = 0; i < num_elems; ++i) {
      tmp_db << database_data[i];
    }
    tmp_db.close();
    operator_info_->AddDatabasePath(tmp_db_path);
  }

  void AssertDatabaseEmpty() {
    EXPECT_EQ(0, operator_info_impl_->database()->mno_size());
    EXPECT_EQ(0, operator_info_impl_->database()->imvno_size());
  }

  const MobileOperatorDB* GetDatabase() {
    return operator_info_impl_->database();
  }

  EventDispatcherForTest dispatcher_;
  vector<FilePath> tmp_db_paths_;
  std::unique_ptr<MobileOperatorInfo> operator_info_;
  // Owned by |operator_info_| and tied to its life cycle.
  MobileOperatorInfoImpl* operator_info_impl_;

 private:
  DISALLOW_COPY_AND_ASSIGN(MobileOperatorInfoInitTest);
};

TEST_F(MobileOperatorInfoInitTest, FailedInitNoPath) {
  // - Initialize object with no database paths set
  // - Verify that initialization fails.
  operator_info_->ClearDatabasePaths();
  EXPECT_FALSE(operator_info_->Init());
  AssertDatabaseEmpty();
}

TEST_F(MobileOperatorInfoInitTest, FailedInitBadPath) {
  // - Initialize object with non-existent path.
  // - Verify that initialization fails.
  const FilePath database_path("nonexistent.pbf");
  operator_info_->ClearDatabasePaths();
  operator_info_->AddDatabasePath(database_path);
  EXPECT_FALSE(operator_info_->Init());
  AssertDatabaseEmpty();
}

TEST_F(MobileOperatorInfoInitTest, FailedInitBadDatabase) {
  // - Initialize object with malformed database.
  // - Verify that initialization fails.
  // TODO(pprabhu): It's hard to get a malformed database in binary format.
}

TEST_F(MobileOperatorInfoInitTest, EmptyDBInit) {
  // - Initialize the object with a database file that is empty.
  // - Verify that initialization succeeds, and that the database is empty.
  operator_info_->ClearDatabasePaths();
  // Can't use arraysize on empty array.
  AddDatabase(mobile_operator_db::init_test_empty_db_init, 0);
  EXPECT_TRUE(operator_info_->Init());
  AssertDatabaseEmpty();
}

TEST_F(MobileOperatorInfoInitTest, SuccessfulInit) {
  operator_info_->ClearDatabasePaths();
  AddDatabase(mobile_operator_db::init_test_successful_init,
              arraysize(mobile_operator_db::init_test_successful_init));
  EXPECT_TRUE(operator_info_->Init());
  EXPECT_GT(GetDatabase()->mno_size(), 0);
  EXPECT_GT(GetDatabase()->imvno_size(), 0);
}

TEST_F(MobileOperatorInfoInitTest, MultipleDBInit) {
  // - Initialize the object with two database files.
  // - Verify that intialization succeeds, and both databases are loaded.
  operator_info_->ClearDatabasePaths();
  AddDatabase(mobile_operator_db::init_test_multiple_db_init_1,
              arraysize(mobile_operator_db::init_test_multiple_db_init_1));
  AddDatabase(mobile_operator_db::init_test_multiple_db_init_2,
              arraysize(mobile_operator_db::init_test_multiple_db_init_2));
  operator_info_->Init();
  EXPECT_GT(GetDatabase()->mno_size(), 0);
  EXPECT_GT(GetDatabase()->imvno_size(), 0);
}

TEST_F(MobileOperatorInfoInitTest, InitWithObserver) {
  // - Add an Observer.
  // - Initialize the object with empty database file.
  // - Verify innitialization succeeds.
  MockMobileOperatorInfoObserver dumb_observer;

  operator_info_->ClearDatabasePaths();
  // Can't use arraysize with empty array.
  AddDatabase(mobile_operator_db::init_test_empty_db_init, 0);
  operator_info_->AddObserver(&dumb_observer);
  EXPECT_TRUE(operator_info_->Init());
}

class MobileOperatorInfoMainTest
    : public MobileOperatorInfoInitTest,
      public WithParamInterface<EventCheckingPolicy> {
 public:
  MobileOperatorInfoMainTest()
      : MobileOperatorInfoInitTest(),
        event_checking_policy_(GetParam()) {}

  virtual void SetUp() {
    operator_info_->ClearDatabasePaths();
    AddDatabase(mobile_operator_db::main_test,
                arraysize(mobile_operator_db::main_test));
    operator_info_->Init();
    operator_info_->AddObserver(&observer_);
  }

 protected:
  // ///////////////////////////////////////////////////////////////////////////
  // Helper functions.
  void VerifyMNOWithUUID(const string& uuid) {
    EXPECT_TRUE(operator_info_->IsMobileNetworkOperatorKnown());
    EXPECT_FALSE(operator_info_->IsMobileVirtualNetworkOperatorKnown());
    EXPECT_EQ(uuid, operator_info_->uuid());
  }

  void VerifyMVNOWithUUID(const string& uuid) {
    EXPECT_TRUE(operator_info_->IsMobileNetworkOperatorKnown());
    EXPECT_TRUE(operator_info_->IsMobileVirtualNetworkOperatorKnown());
    EXPECT_EQ(uuid, operator_info_->uuid());
  }

  void VerifyNoMatch() {
    EXPECT_FALSE(operator_info_->IsMobileNetworkOperatorKnown());
    EXPECT_FALSE(operator_info_->IsMobileVirtualNetworkOperatorKnown());
    EXPECT_EQ("", operator_info_->uuid());
  }

  void ExpectEventCount(int count) {
    // In case we're running in the non-strict event checking mode, we only
    // expect one overall event to be raised for all the updates.
    if (event_checking_policy_ == kEventCheckingPolicyNonStrict) {
      count = (count > 0) ? 1 : 0;
    }
    EXPECT_CALL(observer_, OnOperatorChanged()).Times(count);
  }

  void VerifyEventCount() {
    dispatcher_.DispatchPendingEvents();
    Mock::VerifyAndClearExpectations(&observer_);
  }

  void ResetOperatorInfo() {
    operator_info_->Reset();
    // Eat up any events caused by |Reset|.
    dispatcher_.DispatchPendingEvents();
    VerifyNoMatch();
  }

  // Use these wrappers to send updates to |operator_info_|. These wrappers
  // optionally run the dispatcher if we want strict checking of the number of
  // events raised.
  void UpdateMCCMNC(const std::string& mccmnc) {
    operator_info_->UpdateMCCMNC(mccmnc);
    DispatchPendingEventsIfStrict();
  }

  void UpdateSID(const std::string& sid) {
    operator_info_->UpdateSID(sid);
    DispatchPendingEventsIfStrict();
  }

  void UpdateIMSI(const std::string& imsi) {
    operator_info_->UpdateIMSI(imsi);
    DispatchPendingEventsIfStrict();
  }

  void UpdateICCID(const std::string& iccid) {
    operator_info_->UpdateICCID(iccid);
    DispatchPendingEventsIfStrict();
  }

  void UpdateNID(const std::string& nid) {
    operator_info_->UpdateNID(nid);
    DispatchPendingEventsIfStrict();
  }

  void UpdateOperatorName(const std::string& operator_name) {
    operator_info_->UpdateOperatorName(operator_name);
    DispatchPendingEventsIfStrict();
  }

  void UpdateOnlinePortal(const std::string& url,
                          const std::string& method,
                          const std::string& post_data) {
    operator_info_->UpdateOnlinePortal(url, method, post_data);
    DispatchPendingEventsIfStrict();
  }

  void DispatchPendingEventsIfStrict() {
    if (event_checking_policy_ == kEventCheckingPolicyStrict) {
      dispatcher_.DispatchPendingEvents();
    }
  }

  // ///////////////////////////////////////////////////////////////////////////
  // Data.
  MockMobileOperatorInfoObserver observer_;
  const EventCheckingPolicy event_checking_policy_;

 private:
  DISALLOW_COPY_AND_ASSIGN(MobileOperatorInfoMainTest);
};

TEST_P(MobileOperatorInfoMainTest, InitialConditions) {
  // - Initialize a new object.
  // - Verify that all initial values of properties are reasonable.
  EXPECT_FALSE(operator_info_->IsMobileNetworkOperatorKnown());
  EXPECT_FALSE(operator_info_->IsMobileVirtualNetworkOperatorKnown());
  EXPECT_TRUE(operator_info_->uuid().empty());
  EXPECT_TRUE(operator_info_->operator_name().empty());
  EXPECT_TRUE(operator_info_->country().empty());
  EXPECT_TRUE(operator_info_->mccmnc().empty());
  EXPECT_TRUE(operator_info_->sid().empty());
  EXPECT_TRUE(operator_info_->nid().empty());
  EXPECT_TRUE(operator_info_->mccmnc_list().empty());
  EXPECT_TRUE(operator_info_->sid_list().empty());
  EXPECT_TRUE(operator_info_->operator_name_list().empty());
  EXPECT_TRUE(operator_info_->apn_list().empty());
  EXPECT_TRUE(operator_info_->olp_list().empty());
  EXPECT_TRUE(operator_info_->activation_code().empty());
  EXPECT_FALSE(operator_info_->requires_roaming());
}

TEST_P(MobileOperatorInfoMainTest, MNOByMCCMNC) {
  // message: Has an MNO with no MVNO.
  // match by: MCCMNC.
  // verify: Observer event, uuid.

  ExpectEventCount(0);
  UpdateMCCMNC("101999");  // No match.
  VerifyEventCount();
  VerifyNoMatch();

  ExpectEventCount(1);
  UpdateMCCMNC("101001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid101");

  ExpectEventCount(1);
  UpdateMCCMNC("101999");
  VerifyEventCount();
  VerifyNoMatch();
}

TEST_P(MobileOperatorInfoMainTest, MNOByMCCMNCMultipleMCCMNCOptions) {
  // message: Has an MNO with no MCCMNC.
  // match by: One of the MCCMNCs of the multiple ones in the MNO.
  // verify: Observer event, uuid.
  ExpectEventCount(1);
  UpdateMCCMNC("102002");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid102");
}

TEST_P(MobileOperatorInfoMainTest, MNOByMCCMNCMultipleMNOOptions) {
  // message: Two messages with the same MCCMNC.
  // match by: Both MNOs matched, one is earmarked.
  // verify: The earmarked MNO is picked.
  ExpectEventCount(1);
  UpdateMCCMNC("124001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid124002");
}

TEST_P(MobileOperatorInfoMainTest, MNOByOperatorName) {
  // message: Has an MNO with no MVNO.
  // match by: OperatorName.
  // verify: Observer event, uuid.
  ExpectEventCount(0);
  UpdateOperatorName("name103999");  // No match.
  VerifyEventCount();
  VerifyNoMatch();

  ExpectEventCount(1);
  UpdateOperatorName("name103");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid103");

  ExpectEventCount(1);
  UpdateOperatorName("name103999");  // No match.
  VerifyEventCount();
  VerifyNoMatch();
}

TEST_P(MobileOperatorInfoMainTest, MNOByOperatorNameMultipleMNOOptions) {
  // message: Two messages with the same operator name.
  // match by: Both MNOs matched, one is earmarked.
  // verify: The earmarked MNO is picked.
  ExpectEventCount(1);
  UpdateOperatorName("name125001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid125002");
}

TEST_P(MobileOperatorInfoMainTest, MNOByOperatorNameAggressiveMatch) {
  // These network operators match by name but only after normalizing the names.
  // Both the name from the database and the name provided to
  // |UpdateOperatoName| must be normalized for this test to pass.
  ExpectEventCount(1);
  UpdateOperatorName("name126001 casedoesnotmatch");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid126001");

  ResetOperatorInfo();
  ExpectEventCount(1);
  UpdateOperatorName("name126002 CaseStillDoesNotMatch");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid126002");

  ResetOperatorInfo();
  ExpectEventCount(1);
  UpdateOperatorName("name126003GiveMeMoreSpace");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid126003");

  ResetOperatorInfo();
  ExpectEventCount(1);
  UpdateOperatorName("name126004  Too  Much   Air Here");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid126004");

  ResetOperatorInfo();
  ExpectEventCount(1);
  UpdateOperatorName("näméwithNon-Äσ¢ii");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid126005");
}

TEST_P(MobileOperatorInfoMainTest, MNOByOperatorNameWithLang) {
  // message: Has an MNO with no MVNO.
  // match by: OperatorName.
  // verify: Observer event, fields.
  ExpectEventCount(1);
  UpdateOperatorName("name105");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid105");
}

TEST_P(MobileOperatorInfoMainTest, MNOByOperatorNameMultipleNameOptions) {
  // message: Has an MNO with no MVNO.
  // match by: OperatorName, one of the multiple present in the MNO.
  // verify: Observer event, fields.
  ExpectEventCount(1);
  UpdateOperatorName("name104002");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid104");
}

TEST_P(MobileOperatorInfoMainTest, MNOByMCCMNCAndOperatorName) {
  // message: Has MNOs with no MVNO.
  // match by: MCCMNC finds two candidates (first one is chosen), Name narrows
  //           down to one.
  // verify: Observer event, fields.
  // This is merely a MCCMNC update.
  ExpectEventCount(1);
  UpdateMCCMNC("106001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid106001");

  ExpectEventCount(1);
  UpdateOperatorName("name106002");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid106002");

  ResetOperatorInfo();
  // Try updates in reverse order.
  ExpectEventCount(1);
  UpdateOperatorName("name106001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid106001");
}

TEST_P(MobileOperatorInfoMainTest, MNOByOperatorNameAndMCCMNC) {
  // message: Has MNOs with no MVNO.
  // match by: OperatorName finds two (first one is chosen), MCCMNC narrows down
  //           to one.
  // verify: Observer event, fields.
  // This is merely an OperatorName update.
  ExpectEventCount(1);
  UpdateOperatorName("name107");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid107001");

  ExpectEventCount(1);
  UpdateMCCMNC("107002");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid107002");

  ResetOperatorInfo();
  // Try updates in reverse order.
  ExpectEventCount(1);
  UpdateMCCMNC("107001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid107001");
}

TEST_P(MobileOperatorInfoMainTest, MNOByMCCMNCOverridesOperatorName) {
  // message: Has MNOs with no MVNO.
  // match by: First MCCMNC finds one. Then, OperatorName matches another.
  // verify: MCCMNC match prevails. No change on OperatorName update.
  ExpectEventCount(1);
  UpdateMCCMNC("108001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid108001");

  // An event is sent for the updated OperatorName.
  ExpectEventCount(1);
  UpdateOperatorName("name108002");  // Does not match.
  VerifyEventCount();
  VerifyMNOWithUUID("uuid108001");
  // OperatorName from the database is given preference over the user supplied
  // one.
  EXPECT_EQ("name108001", operator_info_->operator_name());

  ResetOperatorInfo();
  // message: Same as above.
  // match by: First OperatorName finds one, then MCCMNC overrides it.
  // verify: Two events, MCCMNC one overriding the OperatorName one.
  ExpectEventCount(1);
  UpdateOperatorName("name108001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid108001");

  ExpectEventCount(1);
  UpdateMCCMNC("108002");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid108002");
  EXPECT_EQ("name108002", operator_info_->operator_name());

  // message: Same as above.
  // match by: First a *wrong* MCCMNC update, followed by the correct Name
  // update.
  // verify: No MNO, since MCCMNC is given precedence.
  ResetOperatorInfo();
  ExpectEventCount(0);
  UpdateMCCMNC("108999");  // Does not match.
  UpdateOperatorName("name108001");
  VerifyEventCount();
  VerifyNoMatch();
}

TEST_P(MobileOperatorInfoMainTest, MNOByIMSI) {
  // message: Has MNO with no MVNO.
  // match by: MCCMNC part of IMSI of length 5 / 6.
  ExpectEventCount(0);
  UpdateIMSI("109");  // Too short.
  VerifyEventCount();
  VerifyNoMatch();

  ExpectEventCount(0);
  UpdateIMSI("109995432154321");  // No match.
  VerifyEventCount();
  VerifyNoMatch();

  ResetOperatorInfo();
  // Short MCCMNC match.
  ExpectEventCount(1);
  UpdateIMSI("109015432154321");  // First 5 digits match.
  VerifyEventCount();
  VerifyMNOWithUUID("uuid10901");

  ResetOperatorInfo();
  // Long MCCMNC match.
  ExpectEventCount(1);
  UpdateIMSI("10900215432154321");  // First 6 digits match.
  VerifyEventCount();
  VerifyMNOWithUUID("uuid109002");
}

TEST_P(MobileOperatorInfoMainTest, MNOByMCCMNCOverridesIMSI) {
  // message: Has MNOs with no MVNO.
  // match by: One matches MCCMNC, then one matches a different MCCMNC substring
  //    of IMSI
  // verify: Observer event for the first match, all fields. Second Update
  // ignored.
  ExpectEventCount(1);
  UpdateMCCMNC("110001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid110001");

  // MNO remains unchanged on a mismatched IMSI update.
  ExpectEventCount(0);
  UpdateIMSI("1100025432154321");  // First 6 digits match.
  VerifyEventCount();
  VerifyMNOWithUUID("uuid110001");

  // MNO remains uncnaged on an invalid IMSI update.
  ExpectEventCount(0);
  UpdateIMSI("1100035432154321");  // Prefix does not match.
  VerifyEventCount();
  VerifyMNOWithUUID("uuid110001");

  ExpectEventCount(0);
  UpdateIMSI("110");  // Too small.
  VerifyEventCount();
  VerifyMNOWithUUID("uuid110001");

  ResetOperatorInfo();
  // Same as above, but this time, match with IMSI, followed by a contradictory
  // MCCMNC update. The second update should override the first one.
  ExpectEventCount(1);
  UpdateIMSI("1100025432154321");  // First 6 digits match.
  VerifyEventCount();
  VerifyMNOWithUUID("uuid110002");

  ExpectEventCount(1);
  UpdateMCCMNC("110001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid110001");
}

TEST_P(MobileOperatorInfoMainTest, MNOUchangedBySecondaryUpdates) {
  // This test verifies that only some updates affect the MNO.
  // message: Has MNOs with no MVNO.
  // match by: First matches the MCCMNC. Later, MNOs with a different MCCMNC
  //    matchs the given SID, NID, ICCID.
  // verify: Only one Observer event, on the first MCCMNC match.
  ExpectEventCount(1);
  UpdateMCCMNC("111001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid111001");

  ExpectEventCount(1);  // NID change event.
  UpdateNID("111202");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid111001");
}

TEST_P(MobileOperatorInfoMainTest, MVNODefaultMatch) {
  // message: MNO with one MVNO (no filter).
  // match by: MNO matches by MCCMNC.
  // verify: Observer event for MVNO match. Uuid match the MVNO.
  // second update: ICCID.
  // verify: No observer event, match remains unchanged.
  ExpectEventCount(1);
  UpdateMCCMNC("112001");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid112002");

  ExpectEventCount(0);
  UpdateICCID("112002");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid112002");
}

TEST_P(MobileOperatorInfoMainTest, MVNONameMatch) {
  // message: MNO with one MVNO (name filter).
  // match by: MNO matches by MCCMNC,
  //           MVNO fails to match by fist name update,
  //           then MVNO matches by name.
  // verify: Two Observer events: MNO followed by MVNO.
  ExpectEventCount(1);
  UpdateMCCMNC("113001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid113001");

  ExpectEventCount(1);
  UpdateOperatorName("name113999");  // No match.
  VerifyEventCount();
  VerifyMNOWithUUID("uuid113001");
  // Name from the database is given preference.
  EXPECT_EQ("name113001", operator_info_->operator_name());

  ExpectEventCount(1);
  UpdateOperatorName("name113002");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid113002");
  EXPECT_EQ("name113002", operator_info_->operator_name());
}

TEST_P(MobileOperatorInfoMainTest, MVNONameMalformedRegexMatch) {
  // message: MNO with one MVNO (name filter with a malformed regex).
  // match by: MNO matches by MCCMNC.
  //           MVNO does not match
  ExpectEventCount(2);
  UpdateMCCMNC("114001");
  UpdateOperatorName("name[");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid114001");
}

TEST_P(MobileOperatorInfoMainTest, MVNONameSubexpressionRegexMatch) {
  // message: MNO with one MVNO (name filter with simple regex).
  // match by: MNO matches by MCCMNC.
  //           MVNO does not match with a name whose subexpression matches the
  //           regex.
  ExpectEventCount(2);  // One event for just the name update.
  UpdateMCCMNC("115001");
  UpdateOperatorName("name115_ExtraCrud");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid115001");

  ResetOperatorInfo();
  ExpectEventCount(2);  // One event for just the name update.
  UpdateMCCMNC("115001");
  UpdateOperatorName("ExtraCrud_name115");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid115001");

  ResetOperatorInfo();
  ExpectEventCount(2);  // One event for just the name update.
  UpdateMCCMNC("115001");
  UpdateOperatorName("ExtraCrud_name115_ExtraCrud");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid115001");

  ResetOperatorInfo();
  ExpectEventCount(2);  // One event for just the name update.
  UpdateMCCMNC("115001");
  UpdateOperatorName("name_ExtraCrud_115");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid115001");

  ResetOperatorInfo();
  ExpectEventCount(2);
  UpdateMCCMNC("115001");
  UpdateOperatorName("name115");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid115002");
}

TEST_P(MobileOperatorInfoMainTest, MVNONameRegexMatch) {
  // message: MNO with one MVNO (name filter with non-trivial regex).
  // match by: MNO matches by MCCMNC.
  //           MVNO fails to match several times with different strings.
  //           MVNO matches several times with different values.

  // Make sure we're not taking the regex literally!
  ExpectEventCount(2);
  UpdateMCCMNC("116001");
  UpdateOperatorName("name[a-zA-Z_]*116[0-9]{0,3}");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid116001");

  ResetOperatorInfo();
  ExpectEventCount(2);
  UpdateMCCMNC("116001");
  UpdateOperatorName("name[a-zA-Z_]116[0-9]");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid116001");

  ResetOperatorInfo();
  ExpectEventCount(2);
  UpdateMCCMNC("116001");
  UpdateOperatorName("nameb*1167");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid116001");

  // Success!
  ResetOperatorInfo();
  ExpectEventCount(2);
  UpdateMCCMNC("116001");
  UpdateOperatorName("name116");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid116002");

  ResetOperatorInfo();
  ExpectEventCount(2);
  UpdateMCCMNC("116001");
  UpdateOperatorName("nameSomeWord116");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid116002");

  ResetOperatorInfo();
  ExpectEventCount(2);
  UpdateMCCMNC("116001");
  UpdateOperatorName("name116567");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid116002");
}

TEST_P(MobileOperatorInfoMainTest, MVNONameMatchMultipleFilters) {
  // message: MNO with one MVNO with two name filters.
  // match by: MNO matches by MCCMNC.
  //           MVNO first fails on the second filter alone.
  //           MVNO fails on the first filter alone.
  //           MVNO matches on both filters.
  ExpectEventCount(2);
  UpdateMCCMNC("117001");
  UpdateOperatorName("nameA_crud");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid117001");

  ResetOperatorInfo();
  ExpectEventCount(2);
  UpdateMCCMNC("117001");
  UpdateOperatorName("crud_nameB");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid117001");

  ResetOperatorInfo();
  ExpectEventCount(2);
  UpdateMCCMNC("117001");
  UpdateOperatorName("crud_crud");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid117001");

  ResetOperatorInfo();
  ExpectEventCount(2);
  UpdateMCCMNC("117001");
  UpdateOperatorName("nameA_nameB");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid117002");
}

TEST_P(MobileOperatorInfoMainTest, MVNOIMSIMatch) {
  // message: MNO with one MVNO (imsi filter).
  // match by: MNO matches by MCCMNC,
  //           MVNO fails to match by fist imsi update,
  //           then MVNO matches by imsi.
  // verify: Two Observer events: MNO followed by MVNO.
  ExpectEventCount(1);
  UpdateMCCMNC("118001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid118001");

  ExpectEventCount(0);
  UpdateIMSI("1180011234512345");  // No match.
  VerifyEventCount();
  VerifyMNOWithUUID("uuid118001");

  ExpectEventCount(1);
  UpdateIMSI("1180015432154321");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid118002");
}

TEST_P(MobileOperatorInfoMainTest, MVNOICCIDMatch) {
  // message: MNO with one MVNO (iccid filter).
  // match by: MNO matches by MCCMNC,
  //           MVNO fails to match by fist iccid update,
  //           then MVNO matches by iccid.
  // verify: Two Observer events: MNO followed by MVNO.
  ExpectEventCount(1);
  UpdateMCCMNC("119001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid119001");

  ExpectEventCount(0);
  UpdateICCID("119987654321");  // No match.
  VerifyEventCount();
  VerifyMNOWithUUID("uuid119001");

  ExpectEventCount(1);
  UpdateICCID("119123456789");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid119002");
}

TEST_P(MobileOperatorInfoMainTest, MVNOSIDMatch) {
  // message: MNO with one MVNO (sid filter).
  // match by: MNO matches by SID,
  //           MVNO fails to match by fist sid update,
  //           then MVNO matches by sid.
  // verify: Two Observer events: MNO followed by MVNO.
  ExpectEventCount(0);
  UpdateSID("120999");  // No match.
  VerifyEventCount();
  VerifyNoMatch();

  ExpectEventCount(1);
  UpdateSID("120001");  // Only MNO matches.
  VerifyEventCount();
  VerifyMNOWithUUID("uuid120001");
  EXPECT_EQ("120001", operator_info_->sid());

  ExpectEventCount(1);
  UpdateSID("120002");  // MVNO matches as well.
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid120002");
  EXPECT_EQ("120002", operator_info_->sid());
}

TEST_P(MobileOperatorInfoMainTest, MVNOAllMatch) {
  // message: MNO with following MVNOS:
  //   - one with no filter.
  //   - one with name filter.
  //   - one with imsi filter.
  //   - one with iccid filter.
  //   - one with name and iccid filter.
  // verify:
  //   - initial MCCMNC matches the default MVNO directly (not MNO)
  //   - match each of the MVNOs in turn.
  //   - give super set information that does not match any MVNO correctly,
  //     verify that the MNO matches.
  ExpectEventCount(1);
  UpdateMCCMNC("121001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid121001");

  ResetOperatorInfo();
  ExpectEventCount(2);
  UpdateMCCMNC("121001");
  UpdateOperatorName("name121003");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid121003");

  ResetOperatorInfo();
  ExpectEventCount(2);
  UpdateMCCMNC("121001");
  UpdateIMSI("1210045432154321");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid121004");

  ResetOperatorInfo();
  ExpectEventCount(2);
  UpdateMCCMNC("121001");
  UpdateICCID("121005123456789");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid121005");

  ResetOperatorInfo();
  ExpectEventCount(3);
  UpdateMCCMNC("121001");
  UpdateOperatorName("name121006");
  VerifyMNOWithUUID("uuid121001");
  UpdateICCID("121006123456789");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid121006");
}

TEST_P(MobileOperatorInfoMainTest, MVNOMatchAndMismatch) {
  // message: MNO with one MVNO with name filter.
  // match by: MNO matches by MCCMNC
  //           MVNO matches by name.
  //           Second name update causes the MVNO to not match again.
  ExpectEventCount(1);
  UpdateMCCMNC("113001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid113001");

  ExpectEventCount(1);
  UpdateOperatorName("name113002");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid113002");
  EXPECT_EQ("name113002", operator_info_->operator_name());

  ExpectEventCount(1);
  UpdateOperatorName("name113999");  // No match.
  VerifyEventCount();
  VerifyMNOWithUUID("uuid113001");
  // Name from database is given preference.
  EXPECT_EQ("name113001", operator_info_->operator_name());
}

TEST_P(MobileOperatorInfoMainTest, MVNOMatchAndReset) {
  // message: MVNO with name filter.
  // verify;
  //   - match MVNO by name.
  //   - Reset object, verify Observer event, and not match.
  //   - match MVNO by name again.
  ExpectEventCount(1);
  UpdateMCCMNC("113001");
  VerifyEventCount();
  ExpectEventCount(1);
  VerifyMNOWithUUID("uuid113001");
  UpdateOperatorName("name113002");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid113002");
  EXPECT_EQ("name113002", operator_info_->operator_name());

  ExpectEventCount(1);
  operator_info_->Reset();
  VerifyEventCount();
  VerifyNoMatch();

  ExpectEventCount(1);
  UpdateMCCMNC("113001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid113001");
  ExpectEventCount(1);
  UpdateOperatorName("name113002");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid113002");
  EXPECT_EQ("name113002", operator_info_->operator_name());
}

// Here, we rely on our knowledge about the implementation: The SID and MCCMNC
// updates follow the same code paths, and so we can get away with not testing
// all the scenarios we test above for MCCMNC. Instead, we only do basic testing
// to make sure that SID upates operator as MCCMNC updates do.
TEST_P(MobileOperatorInfoMainTest, MNOBySID) {
  // message: Has an MNO with no MVNO.
  // match by: SID.
  // verify: Observer event, uuid.

  ExpectEventCount(0);
  UpdateSID("1229");  // No match.
  VerifyEventCount();
  VerifyNoMatch();

  ExpectEventCount(1);
  UpdateSID("1221");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid1221");

  ExpectEventCount(1);
  UpdateSID("1229");  // No Match.
  VerifyEventCount();
  VerifyNoMatch();
}

TEST_P(MobileOperatorInfoMainTest, MNOByMCCMNCAndSID) {
  // message: Has an MNO with no MVNO.
  // match by: SID / MCCMNC alternately.
  // verify: Observer event, uuid.

  ExpectEventCount(0);
  UpdateMCCMNC("123999");  // NO match.
  UpdateSID("1239");  // No match.
  VerifyEventCount();
  VerifyNoMatch();

  ExpectEventCount(1);
  UpdateMCCMNC("123001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid123001");

  ExpectEventCount(1);
  operator_info_->Reset();
  VerifyEventCount();
  VerifyNoMatch();

  ExpectEventCount(1);
  UpdateSID("1232");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid1232");

  ExpectEventCount(1);
  operator_info_->Reset();
  VerifyEventCount();
  VerifyNoMatch();

  ExpectEventCount(1);
  UpdateMCCMNC("123001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid123001");
}

class MobileOperatorInfoDataTest : public MobileOperatorInfoMainTest {
 public:
  MobileOperatorInfoDataTest() : MobileOperatorInfoMainTest() {}

  // Same as MobileOperatorInfoMainTest, except that the database used is
  // different.
  virtual void SetUp() {
    operator_info_->ClearDatabasePaths();
    AddDatabase(mobile_operator_db::data_test,
                arraysize(mobile_operator_db::data_test));
    operator_info_->Init();
    operator_info_->AddObserver(&observer_);
  }

 protected:
  // This is a function that does a best effort verification of the information
  // that is obtained from the database by the MobileOperatorInfo object against
  // expectations stored in the form of data members in this class.
  // This is not a full proof check. In particular:
  //  - It is unspecified in some case which of the values from a list is
  //    exposed as a property. For example, at best, we can check that |sid| is
  //    non-empty.
  //  - It is not robust to "" as property values at times.
  void VerifyDatabaseData() {
    EXPECT_EQ(country_, operator_info_->country());
    EXPECT_EQ(requires_roaming_, operator_info_->requires_roaming());
    EXPECT_EQ(activation_code_, operator_info_->activation_code());

    EXPECT_EQ(mccmnc_list_.size(), operator_info_->mccmnc_list().size());
    set<string> mccmnc_set(operator_info_->mccmnc_list().begin(),
                           operator_info_->mccmnc_list().end());
    for (const auto& mccmnc : mccmnc_list_) {
      EXPECT_TRUE(mccmnc_set.find(mccmnc) != mccmnc_set.end());
    }
    if (mccmnc_list_.size() > 0) {
      // It is not specified which entry will be chosen, but mccmnc() must be
      // non empty.
      EXPECT_FALSE(operator_info_->mccmnc().empty());
    }

    VerifyNameListsMatch(operator_name_list_,
                         operator_info_->operator_name_list());

    // This comparison breaks if two apns have the same |apn| field.
    EXPECT_EQ(apn_list_.size(), operator_info_->apn_list().size());
    map<string, const MobileOperatorInfo::MobileAPN*> mobile_apns;
    for (const auto& apn_node : operator_info_->apn_list()) {
      mobile_apns[apn_node->apn] = apn_node;
    }
    for (const auto& apn_lhs : apn_list_) {
      ASSERT_TRUE(mobile_apns.find(apn_lhs->apn) != mobile_apns.end());
      const auto& apn_rhs = mobile_apns[apn_lhs->apn];
      // Only comparing apn, name, username, password.
      EXPECT_EQ(apn_lhs->apn, apn_rhs->apn);
      EXPECT_EQ(apn_lhs->username, apn_rhs->username);
      EXPECT_EQ(apn_lhs->password, apn_rhs->password);
      VerifyNameListsMatch(apn_lhs->operator_name_list,
                           apn_rhs->operator_name_list);
    }

    EXPECT_EQ(olp_list_.size(), operator_info_->olp_list().size());
    // This comparison breaks if two OLPs have the same |url|.
    map<string, MobileOperatorInfo::OnlinePortal> olps;
    for (const auto& olp : operator_info_->olp_list()) {
      olps[olp.url] = olp;
    }
    for (const auto& olp : olp_list_) {
      ASSERT_TRUE(olps.find(olp.url) != olps.end());
      const auto& olp_rhs = olps[olp.url];
      EXPECT_EQ(olp.method, olp_rhs.method);
      EXPECT_EQ(olp.post_data, olp_rhs.post_data);
    }

    EXPECT_EQ(sid_list_.size(), operator_info_->sid_list().size());
    set<string> sid_set(operator_info_->sid_list().begin(),
                        operator_info_->sid_list().end());
    for (const auto& sid : sid_list_) {
      EXPECT_TRUE(sid_set.find(sid) != sid_set.end());
    }
    if (sid_list_.size() > 0) {
      // It is not specified which entry will be chosen, but |sid()| must be
      // non-empty.
      EXPECT_FALSE(operator_info_->sid().empty());
    }
  }

  // This function does some extra checks for the user data that can not be done
  // when data is obtained from the database.
  void VerifyUserData() {
    EXPECT_EQ(sid_, operator_info_->sid());
  }

  void VerifyNameListsMatch(
      const vector<MobileOperatorInfo::LocalizedName>& operator_name_list_lhs,
      const vector<MobileOperatorInfo::LocalizedName>& operator_name_list_rhs) {
    // This comparison breaks if two localized names have the same |name|.
    map<string, MobileOperatorInfo::LocalizedName> localized_names;
    for (const auto& localized_name : operator_name_list_rhs) {
      localized_names[localized_name.name] = localized_name;
    }
    for (const auto& localized_name : operator_name_list_lhs) {
      EXPECT_TRUE(localized_names.find(localized_name.name) !=
                  localized_names.end());
      EXPECT_EQ(localized_name.language,
                localized_names[localized_name.name].language);
    }
  }

  // Use this function to pre-popluate all the data members of this object with
  // values matching the MNO for the database in |data_test.prototxt|.
  void PopulateMNOData() {
    country_ = "us";
    requires_roaming_ = true;
    activation_code_ = "open sesame";

    mccmnc_list_.clear();
    mccmnc_list_.push_back("200001");
    mccmnc_list_.push_back("200002");
    mccmnc_list_.push_back("200003");

    operator_name_list_.clear();
    operator_name_list_.push_back({"name200001", "en"});
    operator_name_list_.push_back({"name200002", ""});

    apn_list_.clear();
    MobileOperatorInfo::MobileAPN* apn;
    apn = new MobileOperatorInfo::MobileAPN();
    apn->apn = "test@test.com";
    apn->username = "testuser";
    apn->password = "is_public_boohoohoo";
    apn->operator_name_list.push_back({"name200003", "hi"});
    apn_list_.push_back(apn);  // Takes ownership.

    olp_list_.clear();
    olp_list_.push_back({"some@random.com", "POST", "random_data"});

    sid_list_.clear();
    sid_list_.push_back("200123");
    sid_list_.push_back("200234");
    sid_list_.push_back("200345");
  }

  // Use this function to pre-populate all the data members of this object with
  // values matching the MVNO for the database in |data_test.prototext|.
  void PopulateMVNOData() {
    country_ = "ca";
    requires_roaming_ = false;
    activation_code_ = "khul ja sim sim";

    mccmnc_list_.clear();
    mccmnc_list_.push_back("200001");
    mccmnc_list_.push_back("200102");

    operator_name_list_.clear();
    operator_name_list_.push_back({"name200101", "en"});
    operator_name_list_.push_back({"name200102", ""});

    apn_list_.clear();
    MobileOperatorInfo::MobileAPN* apn;
    apn = new MobileOperatorInfo::MobileAPN();
    apn->apn = "test2@test.com";
    apn->username = "testuser2";
    apn->password = "is_public_boohoohoo_too";
    apn_list_.push_back(apn);  // Takes ownership.

    olp_list_.clear();
    olp_list_.push_back({"someother@random.com", "GET", ""});

    sid_list_.clear();
    sid_list_.push_back("200345");
  }

  // Data to be verified against the database.
  string country_;
  bool requires_roaming_;
  string activation_code_;
  vector<string> mccmnc_list_;
  vector<MobileOperatorInfo::LocalizedName> operator_name_list_;
  ScopedVector<MobileOperatorInfo::MobileAPN> apn_list_;
  vector<MobileOperatorInfo::OnlinePortal> olp_list_;
  vector<string> sid_list_;

  // Extra data to be verified only against user updates.
  string sid_;

 private:
  DISALLOW_COPY_AND_ASSIGN(MobileOperatorInfoDataTest);
};


TEST_P(MobileOperatorInfoDataTest, MNODetailedInformation) {
  // message: MNO with all the information filled in.
  // match by: MNO matches by MCCMNC
  // verify: All information is correctly loaded.
  ExpectEventCount(1);
  UpdateMCCMNC("200001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid200001");

  PopulateMNOData();
  VerifyDatabaseData();
}

TEST_P(MobileOperatorInfoDataTest, MVNOInheritsInformation) {
  // message: MVNO with name filter.
  // verify: All the missing fields are carried over to the MVNO from MNO.
  ExpectEventCount(2);
  UpdateMCCMNC("200001");
  UpdateOperatorName("name200201");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid200201");

  PopulateMNOData();
  VerifyDatabaseData();
}

TEST_P(MobileOperatorInfoDataTest, MVNOOverridesInformation) {
  // match by: MNO matches by MCCMNC, MVNO by name.
  // verify: All information is correctly loaded.
  //         The MVNO in this case overrides the information provided by MNO.
  ExpectEventCount(2);
  UpdateMCCMNC("200001");
  UpdateOperatorName("name200101");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid200101");

  PopulateMVNOData();
  VerifyDatabaseData();
}

TEST_P(MobileOperatorInfoDataTest, NoUpdatesBeforeMNOMatch) {
  // message: MVNO.
  // - do not match MNO with mccmnc/name
  // - on different updates, verify no events.
  ExpectEventCount(0);
  UpdateMCCMNC("200999");  // No match.
  UpdateOperatorName("name200001");  // matches MNO
  UpdateOperatorName("name200101");  // matches MVNO filter.
  UpdateSID("200999");  // No match.
  VerifyEventCount();
  VerifyNoMatch();
}

TEST_P(MobileOperatorInfoDataTest, UserUpdatesOverrideMVNO) {
  // - match MVNO.
  // - send updates to properties and verify events are raised and values of
  //   updated properties override the ones provided by the database.
  string imsi {"2009991234512345"};
  string iccid {"200999123456789"};
  string olp_url {"url@url.com"};
  string olp_method {"POST"};
  string olp_post_data {"data"};

  // Determine MVNO.
  ExpectEventCount(2);
  UpdateMCCMNC("200001");
  UpdateOperatorName("name200101");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid200101");

  // Send updates.
  ExpectEventCount(1);
  UpdateOnlinePortal(olp_url, olp_method, olp_post_data);
  UpdateIMSI(imsi);
  // No event raised because imsi is not exposed.
  UpdateICCID(iccid);
  // No event raised because ICCID is not exposed.

  VerifyEventCount();

  // Update our expectations.
  PopulateMVNOData();
  olp_list_.push_back({olp_url, olp_method, olp_post_data});

  VerifyDatabaseData();
}

TEST_P(MobileOperatorInfoDataTest, CachedUserUpdatesOverrideMVNO) {
  // message: MVNO.
  // - First send updates that don't identify an MNO.
  // - Then identify an MNO and MVNO.
  // - verify that all the earlier updates are cached, and override the MVNO
  //   information.
  string imsi {"2009991234512345"};
  string iccid {"200999123456789"};
  string sid {"200999"};
  string olp_url {"url@url.com"};
  string olp_method {"POST"};
  string olp_post_data {"data"};

  // Send updates.
  ExpectEventCount(0);
  UpdateSID(sid);
  UpdateOnlinePortal(olp_url, olp_method, olp_post_data);
  UpdateIMSI(imsi);
  UpdateICCID(iccid);
  VerifyEventCount();

  // Determine MVNO.
  ExpectEventCount(2);
  UpdateMCCMNC("200001");
  UpdateOperatorName("name200101");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid200101");

  // Update our expectations.
  PopulateMVNOData();
  sid_ = sid;
  sid_list_.push_back(sid);
  olp_list_.push_back({olp_url, olp_method, olp_post_data});

  VerifyDatabaseData();
  VerifyUserData();
}

TEST_P(MobileOperatorInfoDataTest, RedundantUserUpdatesMVNO) {
  // - match MVNO.
  // - send redundant updates to properties.
  // - Verify no events, no updates to properties.

  // Identify MVNO.
  ExpectEventCount(2);
  UpdateMCCMNC("200001");
  UpdateOperatorName("name200101");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid200101");

  // Send redundant updates.
  // TODO(pprabhu)
  // |UpdateOnlinePortal| leads to an event because this is the first time this
  // value are set *by the user*. Although the values from the database were the
  // same, we did not use those values for filters.  It would be ideal to not
  // raise these redundant events (since no public information about the object
  // changed), but I haven't invested in doing so yet.
  ExpectEventCount(1);
  UpdateOperatorName(operator_info_->operator_name());
  UpdateOnlinePortal("someother@random.com", "GET", "");
  VerifyEventCount();
  PopulateMVNOData();
  VerifyDatabaseData();
}

TEST_P(MobileOperatorInfoDataTest, RedundantCachedUpdatesMVNO) {
  // message: MVNO.
  // - First send updates that don't identify MVNO, but match the data.
  // - Then idenityf an MNO and MVNO.
  // - verify that redundant information occurs only once.

  // Send redundant updates.
  ExpectEventCount(2);
  UpdateSID(operator_info_->sid());
  UpdateOperatorName(operator_info_->operator_name());
  UpdateOnlinePortal("someother@random.com", "GET", "");

  // Identify MVNO.
  UpdateMCCMNC("200001");
  UpdateOperatorName("name200101");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid200101");

  PopulateMVNOData();
  VerifyDatabaseData();
}

TEST_P(MobileOperatorInfoDataTest, ResetClearsInformation) {
  // Repeatedly reset the object and check M[V]NO identification and data.
  ExpectEventCount(2);
  UpdateMCCMNC("200001");
  UpdateOperatorName("name200201");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid200201");
  PopulateMNOData();
  VerifyDatabaseData();

  ExpectEventCount(1);
  operator_info_->Reset();
  VerifyEventCount();
  VerifyNoMatch();

  ExpectEventCount(2);
  UpdateMCCMNC("200001");
  UpdateOperatorName("name200101");
  VerifyEventCount();
  VerifyMVNOWithUUID("uuid200101");
  PopulateMVNOData();
  VerifyDatabaseData();

  ExpectEventCount(1);
  operator_info_->Reset();
  VerifyEventCount();
  VerifyNoMatch();

  ExpectEventCount(1);
  UpdateMCCMNC("200001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid200001");
  PopulateMNOData();
  VerifyDatabaseData();
}

TEST_P(MobileOperatorInfoDataTest, FilteredOLP) {
  // We only check basic filter matching, using the fact that the regex matching
  // code is shared with the MVNO filtering, and is already well tested.
  // (1) None of the filters match.
  ExpectEventCount(1);
  UpdateMCCMNC("200001");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid200001");

  ASSERT_EQ(1, operator_info_->olp_list().size());
  // Just check that the filtered OLPs are not in the list.
  EXPECT_NE("olp@mccmnc", operator_info_->olp_list()[0].url);
  EXPECT_NE("olp@sid", operator_info_->olp_list()[0].url);

  // (2) MCCMNC filter matches.
  ExpectEventCount(1);
  operator_info_->Reset();
  VerifyEventCount();
  VerifyNoMatch();

  ExpectEventCount(1);
  UpdateMCCMNC("200003");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid200001");

  ASSERT_EQ(2, operator_info_->olp_list().size());
  EXPECT_NE("olp@sid", operator_info_->olp_list()[0].url);
  bool found_olp_by_mccmnc = false;
  for (const auto& olp : operator_info_->olp_list()) {
    found_olp_by_mccmnc |= ("olp@mccmnc" == olp.url);
  }
  EXPECT_TRUE(found_olp_by_mccmnc);

  // (3) SID filter matches.
  ExpectEventCount(1);
  operator_info_->Reset();
  VerifyEventCount();
  VerifyNoMatch();

  ExpectEventCount(1);
  UpdateSID("200345");
  VerifyEventCount();
  VerifyMNOWithUUID("uuid200001");

  ASSERT_EQ(2, operator_info_->olp_list().size());
  EXPECT_NE("olp@mccmnc", operator_info_->olp_list()[0].url);
  bool found_olp_by_sid = false;
  for (const auto& olp : operator_info_->olp_list()) {
    found_olp_by_sid |= ("olp@sid" == olp.url);
  }
  EXPECT_TRUE(found_olp_by_sid);
}

class MobileOperatorInfoObserverTest : public MobileOperatorInfoMainTest {
 public:
  MobileOperatorInfoObserverTest() : MobileOperatorInfoMainTest() {}

  // Same as |MobileOperatorInfoMainTest::SetUp|, except that we don't add a
  // default observer.
  virtual void SetUp() {
    operator_info_->ClearDatabasePaths();
    AddDatabase(mobile_operator_db::data_test,
                arraysize(mobile_operator_db::data_test));
    operator_info_->Init();
  }

 protected:
  // ///////////////////////////////////////////////////////////////////////////
  // Data.
  MockMobileOperatorInfoObserver second_observer_;

 private:
  DISALLOW_COPY_AND_ASSIGN(MobileOperatorInfoObserverTest);
};

TEST_P(MobileOperatorInfoObserverTest, NoObserver) {
  // - Don't add any observers, and then cause an MVNO update to occur.
  // - Verify no crash.
  UpdateMCCMNC("200001");
  UpdateOperatorName("name200101");
}

TEST_P(MobileOperatorInfoObserverTest, MultipleObservers) {
  // - Add two observers, and then cause an MVNO update to occur.
  // - Verify both observers are notified.
  operator_info_->AddObserver(&observer_);
  operator_info_->AddObserver(&second_observer_);

  EXPECT_CALL(observer_, OnOperatorChanged()).Times(2);
  EXPECT_CALL(second_observer_, OnOperatorChanged()).Times(2);
  UpdateMCCMNC("200001");
  UpdateOperatorName("name200101");
  VerifyMVNOWithUUID("uuid200101");

  dispatcher_.DispatchPendingEvents();
}

TEST_P(MobileOperatorInfoObserverTest, LateObserver) {
  // - Add one observer, and verify it gets an MVNO update.
  operator_info_->AddObserver(&observer_);

  EXPECT_CALL(observer_, OnOperatorChanged()).Times(2);
  EXPECT_CALL(second_observer_, OnOperatorChanged()).Times(0);
  UpdateMCCMNC("200001");
  UpdateOperatorName("name200101");
  VerifyMVNOWithUUID("uuid200101");
  dispatcher_.DispatchPendingEvents();
  Mock::VerifyAndClearExpectations(&observer_);
  Mock::VerifyAndClearExpectations(&second_observer_);

  EXPECT_CALL(observer_, OnOperatorChanged()).Times(1);
  EXPECT_CALL(second_observer_, OnOperatorChanged()).Times(0);
  operator_info_->Reset();
  VerifyNoMatch();
  dispatcher_.DispatchPendingEvents();
  Mock::VerifyAndClearExpectations(&observer_);
  Mock::VerifyAndClearExpectations(&second_observer_);

  // - Add another observer, verify both get an MVNO update.
  operator_info_->AddObserver(&second_observer_);

  EXPECT_CALL(observer_, OnOperatorChanged()).Times(2);
  EXPECT_CALL(second_observer_, OnOperatorChanged()).Times(2);
  UpdateMCCMNC("200001");
  UpdateOperatorName("name200101");
  VerifyMVNOWithUUID("uuid200101");
  dispatcher_.DispatchPendingEvents();
  Mock::VerifyAndClearExpectations(&observer_);
  Mock::VerifyAndClearExpectations(&second_observer_);

  EXPECT_CALL(observer_, OnOperatorChanged()).Times(1);
  EXPECT_CALL(second_observer_, OnOperatorChanged()).Times(1);
  operator_info_->Reset();
  VerifyNoMatch();
  dispatcher_.DispatchPendingEvents();
  Mock::VerifyAndClearExpectations(&observer_);
  Mock::VerifyAndClearExpectations(&second_observer_);

  // - Remove an observer, verify it no longer gets updates.
  operator_info_->RemoveObserver(&observer_);

  EXPECT_CALL(observer_, OnOperatorChanged()).Times(0);
  EXPECT_CALL(second_observer_, OnOperatorChanged()).Times(2);
  UpdateMCCMNC("200001");
  UpdateOperatorName("name200101");
  VerifyMVNOWithUUID("uuid200101");
  dispatcher_.DispatchPendingEvents();
  Mock::VerifyAndClearExpectations(&observer_);
  Mock::VerifyAndClearExpectations(&second_observer_);

  EXPECT_CALL(observer_, OnOperatorChanged()).Times(0);
  EXPECT_CALL(second_observer_, OnOperatorChanged()).Times(1);
  operator_info_->Reset();
  VerifyNoMatch();
  dispatcher_.DispatchPendingEvents();
  Mock::VerifyAndClearExpectations(&observer_);
  Mock::VerifyAndClearExpectations(&second_observer_);
}

INSTANTIATE_TEST_CASE_P(MobileOperatorInfoMainTestInstance,
                        MobileOperatorInfoMainTest,
                        Values(kEventCheckingPolicyStrict,
                               kEventCheckingPolicyNonStrict));
INSTANTIATE_TEST_CASE_P(MobileOperatorInfoDataTestInstance,
                        MobileOperatorInfoDataTest,
                        Values(kEventCheckingPolicyStrict,
                               kEventCheckingPolicyNonStrict));
// It only makes sense to do strict checking here.
INSTANTIATE_TEST_CASE_P(MobileOperatorInfoObserverTestInstance,
                        MobileOperatorInfoObserverTest,
                        Values(kEventCheckingPolicyStrict));
}  // namespace shill