text:
namespace {

// Helps in setting the current network type and id.
class TestNetworkQualityEstimator : public net::NetworkQualityEstimator {
 public:
  TestNetworkQualityEstimator(
      const std::map<std::string, std::string>& variation_params,
      scoped_ptr<net::ExternalEstimateProvider> external_estimate_provider)
      : NetworkQualityEstimator(std::move(external_estimate_provider),
                                variation_params,
                                true,
                                true) {
    // Set up embedded test server.
    embedded_test_server_.ServeFilesFromDirectory(
        base::FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest")));
    EXPECT_TRUE(embedded_test_server_.Start());
    embedded_test_server_.RegisterRequestHandler(base::Bind(
        &TestNetworkQualityEstimator::HandleRequest, base::Unretained(this)));
  }

  explicit TestNetworkQualityEstimator(
      const std::map<std::string, std::string>& variation_params)
      : TestNetworkQualityEstimator(
            variation_params,
            scoped_ptr<net::ExternalEstimateProvider>()) {}

  ~TestNetworkQualityEstimator() override {}

  // Overrides the current network type and id.
  // Notifies network quality estimator of change in connection.
  void SimulateNetworkChangeTo(net::NetworkChangeNotifier::ConnectionType type,
                               std::string network_id) {
    current_network_type_ = type;
    current_network_id_ = network_id;
    OnConnectionTypeChanged(type);
  }

  // Called by embedded server when a HTTP request is received.
  scoped_ptr<net::test_server::HttpResponse> HandleRequest(
      const net::test_server::HttpRequest& request) {
    scoped_ptr<net::test_server::BasicHttpResponse> http_response(
        new net::test_server::BasicHttpResponse());
    http_response->set_code(net::HTTP_OK);
    http_response->set_content("hello");
    http_response->set_content_type("text/plain");
    return std::move(http_response);
  }

  // Returns a GURL hosted at embedded test server.
  const GURL GetEchoURL() const {
    return embedded_test_server_.GetURL("/echo.html");
  }

  using NetworkQualityEstimator::ReadCachedNetworkQualityEstimate;
  using NetworkQualityEstimator::OnConnectionTypeChanged;

 private:
  // NetworkQualityEstimator implementation that returns the overridden network
  // id (instead of invoking platform APIs).
  NetworkQualityEstimator::NetworkID GetCurrentNetworkID() const override {
    return NetworkQualityEstimator::NetworkID(current_network_type_,
                                              current_network_id_);
  }

  net::NetworkChangeNotifier::ConnectionType current_network_type_;
  std::string current_network_id_;

  // Embedded server used for testing.
  net::EmbeddedTestServer embedded_test_server_;

  DISALLOW_COPY_AND_ASSIGN(TestNetworkQualityEstimator);
};

class TestRTTObserver : public net::NetworkQualityEstimator::RTTObserver {
 public:
  struct Observation {
    Observation(int32_t ms,
                const base::TimeTicks& ts,
                net::NetworkQualityEstimator::ObservationSource src)
        : rtt_ms(ms), timestamp(ts), source(src) {}
    int32_t rtt_ms;
    base::TimeTicks timestamp;
    net::NetworkQualityEstimator::ObservationSource source;
  };

  std::vector<Observation>& observations() { return observations_; }

  // RttObserver implementation:
  void OnRTTObservation(
      int32_t rtt_ms,
      const base::TimeTicks& timestamp,
      net::NetworkQualityEstimator::ObservationSource source) override {
    observations_.push_back(Observation(rtt_ms, timestamp, source));
  }

 private:
  std::vector<Observation> observations_;
};

class TestThroughputObserver
    : public net::NetworkQualityEstimator::ThroughputObserver {
 public:
  struct Observation {
    Observation(int32_t kbps,
                const base::TimeTicks& ts,
                net::NetworkQualityEstimator::ObservationSource src)
        : throughput_kbps(kbps), timestamp(ts), source(src) {}
    int32_t throughput_kbps;
    base::TimeTicks timestamp;
    net::NetworkQualityEstimator::ObservationSource source;
  };

  std::vector<Observation>& observations() { return observations_; }

  // ThroughputObserver implementation:
  void OnThroughputObservation(
      int32_t throughput_kbps,
      const base::TimeTicks& timestamp,
      net::NetworkQualityEstimator::ObservationSource source) override {
    observations_.push_back(Observation(throughput_kbps, timestamp, source));
  }

 private:
  std::vector<Observation> observations_;
};

}  // namespace

namespace net {

TEST(NetworkQualityEstimatorTest, TestKbpsRTTUpdates) {
  base::HistogramTester histogram_tester;
  // Enable requests to local host to be used for network quality estimation.
  std::map<std::string, std::string> variation_params;
  TestNetworkQualityEstimator estimator(variation_params);

  EXPECT_EQ(NetworkQualityEstimator::InvalidRTT(),
            estimator.GetRTTEstimateInternal(base::TimeTicks(), 100));
  EXPECT_EQ(NetworkQualityEstimator::kInvalidThroughput,
            estimator.GetDownlinkThroughputKbpsEstimateInternal(
                base::TimeTicks(), 100));

  TestDelegate test_delegate;
  TestURLRequestContext context(true);
  context.set_network_quality_estimator(&estimator);
  context.Init();

  scoped_ptr<URLRequest> request(context.CreateRequest(
      estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
  request->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME);
  request->Start();
  base::RunLoop().Run();

  // Both RTT and downstream throughput should be updated.
  EXPECT_NE(NetworkQualityEstimator::InvalidRTT(),
            estimator.GetRTTEstimateInternal(base::TimeTicks(), 100));
  EXPECT_NE(NetworkQualityEstimator::kInvalidThroughput,
            estimator.GetDownlinkThroughputKbpsEstimateInternal(
                base::TimeTicks(), 100));

  base::TimeDelta rtt = NetworkQualityEstimator::InvalidRTT();
  int32_t kbps = NetworkQualityEstimator::kInvalidThroughput;
  EXPECT_TRUE(estimator.GetRTTEstimate(&rtt));
  EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
  EXPECT_NE(NetworkQualityEstimator::InvalidRTT(), rtt);
  EXPECT_NE(NetworkQualityEstimator::kInvalidThroughput, kbps);

  EXPECT_NEAR(
      rtt.InMilliseconds(),
      estimator.GetRTTEstimateInternal(base::TimeTicks(), 100).InMilliseconds(),
      1);

  // Check UMA histograms.
  histogram_tester.ExpectTotalCount("NQE.PeakKbps.Unknown", 0);
  histogram_tester.ExpectTotalCount("NQE.FastestRTT.Unknown", 0);

  histogram_tester.ExpectTotalCount("NQE.RatioEstimatedToActualRTT.Unknown", 0);

  scoped_ptr<URLRequest> request2(context.CreateRequest(
      estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
  request2->SetLoadFlags(request2->load_flags() | LOAD_MAIN_FRAME);
  request2->Start();
  base::RunLoop().Run();

  histogram_tester.ExpectTotalCount("NQE.RTTObservations.Unknown", 1);
  estimator.SimulateNetworkChangeTo(
      NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-1");
  histogram_tester.ExpectTotalCount("NQE.PeakKbps.Unknown", 1);
  histogram_tester.ExpectTotalCount("NQE.FastestRTT.Unknown", 1);

  histogram_tester.ExpectTotalCount("NQE.RatioMedianRTT.WiFi", 0);

  histogram_tester.ExpectTotalCount("NQE.RTT.Percentile0.Unknown", 1);
  histogram_tester.ExpectTotalCount("NQE.RTT.Percentile10.Unknown", 1);
  histogram_tester.ExpectTotalCount("NQE.RTT.Percentile50.Unknown", 1);
  histogram_tester.ExpectTotalCount("NQE.RTT.Percentile90.Unknown", 1);
  histogram_tester.ExpectTotalCount("NQE.RTT.Percentile100.Unknown", 1);

  EXPECT_EQ(NetworkQualityEstimator::InvalidRTT(),
            estimator.GetRTTEstimateInternal(base::TimeTicks(), 100));
  EXPECT_EQ(NetworkQualityEstimator::kInvalidThroughput,
            estimator.GetDownlinkThroughputKbpsEstimateInternal(
                base::TimeTicks(), 100));

  EXPECT_FALSE(estimator.GetRTTEstimate(&rtt));
  EXPECT_FALSE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
  EXPECT_EQ(NetworkQualityEstimator::InvalidRTT(), rtt);
  EXPECT_EQ(NetworkQualityEstimator::kInvalidThroughput, kbps);

  estimator.SimulateNetworkChangeTo(
      NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, std::string());
  histogram_tester.ExpectTotalCount("NQE.PeakKbps.Unknown", 1);
  histogram_tester.ExpectTotalCount("NQE.FastestRTT.Unknown", 1);

  EXPECT_EQ(NetworkQualityEstimator::InvalidRTT(),
            estimator.GetRTTEstimateInternal(base::TimeTicks(), 100));
  EXPECT_EQ(NetworkQualityEstimator::kInvalidThroughput,
            estimator.GetDownlinkThroughputKbpsEstimateInternal(
                base::TimeTicks(), 100));

  EXPECT_FALSE(estimator.GetRTTEstimate(&rtt));
  EXPECT_FALSE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
}

TEST(NetworkQualityEstimatorTest, StoreObservations) {
  std::map<std::string, std::string> variation_params;
  TestNetworkQualityEstimator estimator(variation_params);

  TestDelegate test_delegate;
  TestURLRequestContext context(true);
  context.set_network_quality_estimator(&estimator);
  context.Init();

  // Push 10 more observations than the maximum buffer size.
  for (size_t i = 0; i < estimator.kMaximumObservationsBufferSize + 10U; ++i) {
    scoped_ptr<URLRequest> request(context.CreateRequest(
        estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
    request->Start();
    base::RunLoop().Run();
  }

  EXPECT_EQ(static_cast<size_t>(
                NetworkQualityEstimator::kMaximumObservationsBufferSize),
            estimator.downstream_throughput_kbps_observations_.Size());
  EXPECT_EQ(static_cast<size_t>(
                NetworkQualityEstimator::kMaximumObservationsBufferSize),
            estimator.rtt_msec_observations_.Size());

  // Verify that the stored observations are cleared on network change.
  estimator.SimulateNetworkChangeTo(
      NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-2");
  EXPECT_EQ(0U, estimator.downstream_throughput_kbps_observations_.Size());
  EXPECT_EQ(0U, estimator.rtt_msec_observations_.Size());
}

// Verifies that the percentiles are correctly computed. All observations have
// the same timestamp. Kbps percentiles must be in decreasing order. RTT
// percentiles must be in increasing order.
TEST(NetworkQualityEstimatorTest, PercentileSameTimestamps) {
  std::map<std::string, std::string> variation_params;
  TestNetworkQualityEstimator estimator(variation_params);
  base::TimeTicks now = base::TimeTicks::Now();

  // Network quality should be unavailable when no observations are available.
  base::TimeDelta rtt;
  EXPECT_FALSE(estimator.GetRTTEstimate(&rtt));
  int32_t kbps;
  EXPECT_FALSE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));

  // Insert samples from {1,2,3,..., 100}. First insert odd samples, then even
  // samples. This helps in verifying that the order of samples does not matter.
  for (int i = 1; i <= 99; i += 2) {
    estimator.downstream_throughput_kbps_observations_.AddObservation(
        NetworkQualityEstimator::ThroughputObservation(
            i, now, NetworkQualityEstimator::URL_REQUEST));
    estimator.rtt_msec_observations_.AddObservation(
        NetworkQualityEstimator::RttObservation(
            base::TimeDelta::FromMilliseconds(i), now,
            NetworkQualityEstimator::URL_REQUEST));
    EXPECT_TRUE(estimator.GetRTTEstimate(&rtt));
    EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
  }

  for (int i = 2; i <= 100; i += 2) {
    estimator.downstream_throughput_kbps_observations_.AddObservation(
        NetworkQualityEstimator::ThroughputObservation(
            i, now, NetworkQualityEstimator::URL_REQUEST));
    estimator.rtt_msec_observations_.AddObservation(
        NetworkQualityEstimator::RttObservation(
            base::TimeDelta::FromMilliseconds(i), now,
            NetworkQualityEstimator::URL_REQUEST));
    EXPECT_TRUE(estimator.GetRTTEstimate(&rtt));
    EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
  }

  for (int i = 0; i <= 100; ++i) {
    // Checks if the difference between the two integers is less than 1. This is
    // required because computed percentiles may be slightly different from
    // what is expected due to floating point computation errors and integer
    // rounding off errors.
    EXPECT_NEAR(estimator.GetDownlinkThroughputKbpsEstimateInternal(
                    base::TimeTicks(), i),
                100 - i, 1);
    EXPECT_NEAR(
        estimator.GetRTTEstimateInternal(base::TimeTicks(), i).InMilliseconds(),
        i, 1);
  }

  EXPECT_TRUE(estimator.GetRTTEstimate(&rtt));
  EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
  // |network_quality| should be equal to the 50 percentile value.
  EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimateInternal(
                  base::TimeTicks(), 50) > 0);
  EXPECT_TRUE(estimator.GetRTTEstimateInternal(base::TimeTicks(), 50) !=
              NetworkQualityEstimator::InvalidRTT());
}

// Verifies that the percentiles are correctly computed. Observations have
// different timestamps with half the observations being very old and the rest
// of them being very recent. Percentiles should factor in recent observations
// much more heavily than older samples. Kbps percentiles must be in decreasing
// order. RTT percentiles must be in increasing order.
TEST(NetworkQualityEstimatorTest, PercentileDifferentTimestamps) {
  std::map<std::string, std::string> variation_params;
  TestNetworkQualityEstimator estimator(variation_params);
  base::TimeTicks now = base::TimeTicks::Now();
  base::TimeTicks very_old = base::TimeTicks::UnixEpoch();

  // First 50 samples have very old timestamp.
  for (int i = 1; i <= 50; ++i) {
    estimator.downstream_throughput_kbps_observations_.AddObservation(
        NetworkQualityEstimator::ThroughputObservation(
            i, very_old, NetworkQualityEstimator::URL_REQUEST));
    estimator.rtt_msec_observations_.AddObservation(
        NetworkQualityEstimator::RttObservation(
            base::TimeDelta::FromMilliseconds(i), very_old,
            NetworkQualityEstimator::URL_REQUEST));
  }

  // Next 50 (i.e., from 51 to 100) have recent timestamp.
  for (int i = 51; i <= 100; ++i) {
    estimator.downstream_throughput_kbps_observations_.AddObservation(
        NetworkQualityEstimator::ThroughputObservation(
            i, now, NetworkQualityEstimator::URL_REQUEST));
    estimator.rtt_msec_observations_.AddObservation(
        NetworkQualityEstimator::RttObservation(
            base::TimeDelta::FromMilliseconds(i), now,
            NetworkQualityEstimator::URL_REQUEST));
  }

  // Older samples have very little weight. So, all percentiles are >= 51
  // (lowest value among recent observations).
  for (int i = 1; i < 100; ++i) {
    // Checks if the difference between the two integers is less than 1. This is
    // required because computed percentiles may be slightly different from
    // what is expected due to floating point computation errors and integer
    // rounding off errors.
    EXPECT_NEAR(estimator.GetDownlinkThroughputKbpsEstimateInternal(
                    base::TimeTicks(), i),
                51 + 0.49 * (100 - i), 1);
    EXPECT_NEAR(
        estimator.GetRTTEstimateInternal(base::TimeTicks(), i).InMilliseconds(),
        51 + 0.49 * i, 1);
  }

  EXPECT_EQ(NetworkQualityEstimator::InvalidRTT(),
            estimator.GetRTTEstimateInternal(
                base::TimeTicks::Now() + base::TimeDelta::FromMinutes(10), 50));
  EXPECT_EQ(NetworkQualityEstimator::kInvalidThroughput,
            estimator.GetDownlinkThroughputKbpsEstimateInternal(
                base::TimeTicks::Now() + base::TimeDelta::FromMinutes(10), 50));
}

// This test notifies NetworkQualityEstimator of received data. Next,
// throughput and RTT percentiles are checked for correctness by doing simple
// verifications.
TEST(NetworkQualityEstimatorTest, ComputedPercentiles) {
  std::map<std::string, std::string> variation_params;
  TestNetworkQualityEstimator estimator(variation_params);

  EXPECT_EQ(NetworkQualityEstimator::InvalidRTT(),
            estimator.GetRTTEstimateInternal(base::TimeTicks(), 100));
  EXPECT_EQ(NetworkQualityEstimator::kInvalidThroughput,
            estimator.GetDownlinkThroughputKbpsEstimateInternal(
                base::TimeTicks(), 100));

  TestDelegate test_delegate;
  TestURLRequestContext context(true);
  context.set_network_quality_estimator(&estimator);
  context.Init();

  // Number of observations are more than the maximum buffer size.
  for (size_t i = 0; i < estimator.kMaximumObservationsBufferSize + 100U; ++i) {
    scoped_ptr<URLRequest> request(context.CreateRequest(
        estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
    request->Start();
    base::RunLoop().Run();
  }

  // Verify the percentiles through simple tests.
  for (int i = 0; i <= 100; ++i) {
    EXPECT_GT(estimator.GetDownlinkThroughputKbpsEstimateInternal(
                  base::TimeTicks(), i),
              0);
    EXPECT_LT(estimator.GetRTTEstimateInternal(base::TimeTicks(), i),
              base::TimeDelta::Max());

    if (i != 0) {
      // Throughput percentiles are in decreasing order.
      EXPECT_LE(estimator.GetDownlinkThroughputKbpsEstimateInternal(
                    base::TimeTicks(), i),
                estimator.GetDownlinkThroughputKbpsEstimateInternal(
                    base::TimeTicks(), i - 1));

      // RTT percentiles are in increasing order.
      EXPECT_GE(estimator.GetRTTEstimateInternal(base::TimeTicks(), i),
                estimator.GetRTTEstimateInternal(base::TimeTicks(), i - 1));
    }
  }
}

TEST(NetworkQualityEstimatorTest, ObtainOperatingParams) {
  std::map<std::string, std::string> variation_params;
  variation_params["Unknown.DefaultMedianKbps"] = "100";
  variation_params["WiFi.DefaultMedianKbps"] = "200";
  variation_params["2G.DefaultMedianKbps"] = "300";

  variation_params["Unknown.DefaultMedianRTTMsec"] = "1000";
  variation_params["WiFi.DefaultMedianRTTMsec"] = "2000";
  // Negative variation value should not be used.
  variation_params["2G.DefaultMedianRTTMsec"] = "-5";

  TestNetworkQualityEstimator estimator(variation_params);
  EXPECT_EQ(1U, estimator.downstream_throughput_kbps_observations_.Size());
  EXPECT_EQ(1U, estimator.rtt_msec_observations_.Size());

  base::TimeDelta rtt;
  EXPECT_TRUE(estimator.GetRTTEstimate(&rtt));
  int32_t kbps;
  EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));

  EXPECT_EQ(100, kbps);
  EXPECT_EQ(base::TimeDelta::FromMilliseconds(1000), rtt);
  auto throughput_iterator =
      estimator.downstream_throughput_kbps_observations_.observations_.begin();
  EXPECT_EQ(100, (*throughput_iterator).value);
  auto rtt_iterator = estimator.rtt_msec_observations_.observations_.begin();
  EXPECT_EQ(base::TimeDelta::FromMilliseconds(1000), (*rtt_iterator).value);

  // Simulate network change to Wi-Fi.
  estimator.SimulateNetworkChangeTo(
      NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-1");
  EXPECT_EQ(1U, estimator.downstream_throughput_kbps_observations_.Size());
  EXPECT_EQ(1U, estimator.rtt_msec_observations_.Size());

  EXPECT_TRUE(estimator.GetRTTEstimate(&rtt));
  EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
  EXPECT_EQ(200, kbps);
  EXPECT_EQ(base::TimeDelta::FromMilliseconds(2000), rtt);

  throughput_iterator =
      estimator.downstream_throughput_kbps_observations_.observations_.begin();
  EXPECT_EQ(200, (*throughput_iterator).value);
  rtt_iterator = estimator.rtt_msec_observations_.observations_.begin();
  EXPECT_EQ(base::TimeDelta::FromMilliseconds(2000), (*rtt_iterator).value);

  // Peak network quality should not be affected by the network quality
  // estimator field trial.
  EXPECT_EQ(NetworkQualityEstimator::InvalidRTT(),
            estimator.peak_network_quality_.rtt());
  EXPECT_EQ(NetworkQualityEstimator::kInvalidThroughput,
            estimator.peak_network_quality_.downstream_throughput_kbps());

  // Simulate network change to 2G. Only the Kbps default estimate should be
  // available.
  estimator.SimulateNetworkChangeTo(
      NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test-2");
  EXPECT_EQ(1U, estimator.downstream_throughput_kbps_observations_.Size());
  EXPECT_EQ(0U, estimator.rtt_msec_observations_.Size());

  EXPECT_FALSE(estimator.GetRTTEstimate(&rtt));
  EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));

  throughput_iterator =
      estimator.downstream_throughput_kbps_observations_.observations_.begin();
  EXPECT_EQ(300, (*throughput_iterator).value);

  // Simulate network change to 3G. Default estimates should be unavailable.
  estimator.SimulateNetworkChangeTo(
      NetworkChangeNotifier::ConnectionType::CONNECTION_3G, "test-3");

  EXPECT_FALSE(estimator.GetRTTEstimate(&rtt));
  EXPECT_FALSE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
  EXPECT_EQ(0U, estimator.downstream_throughput_kbps_observations_.Size());
  EXPECT_EQ(0U, estimator.rtt_msec_observations_.Size());
}

TEST(NetworkQualityEstimatorTest, HalfLifeParam) {
  // Verifies if |weight_multiplier_per_second_| is set to correct value for
  // various values of half life parameter.
  std::map<std::string, std::string> variation_params;
  {
    // Half life parameter is not set. Default value of
    // |weight_multiplier_per_second_| should be used.
    TestNetworkQualityEstimator estimator(variation_params);
    EXPECT_NEAR(0.988, estimator.downstream_throughput_kbps_observations_
                           .weight_multiplier_per_second_,
                0.001);
  }

  variation_params["HalfLifeSeconds"] = "-100";
  {
    // Half life parameter is set to a negative value.  Default value of
    // |weight_multiplier_per_second_| should be used.
    TestNetworkQualityEstimator estimator(variation_params);
    EXPECT_NEAR(0.988, estimator.downstream_throughput_kbps_observations_
                           .weight_multiplier_per_second_,
                0.001);
  }

  variation_params["HalfLifeSeconds"] = "0";
  {
    // Half life parameter is set to zero.  Default value of
    // |weight_multiplier_per_second_| should be used.
    TestNetworkQualityEstimator estimator(variation_params);
    EXPECT_NEAR(0.988, estimator.downstream_throughput_kbps_observations_
                           .weight_multiplier_per_second_,
                0.001);
  }

  variation_params["HalfLifeSeconds"] = "10";
  {
    // Half life parameter is set to a valid value.
    TestNetworkQualityEstimator estimator(variation_params);
    EXPECT_NEAR(0.933, estimator.downstream_throughput_kbps_observations_
                           .weight_multiplier_per_second_,
                0.001);
  }
}

// Test if the network estimates are cached when network change notification
// is invoked.
TEST(NetworkQualityEstimatorTest, TestCaching) {
  std::map<std::string, std::string> variation_params;
  TestNetworkQualityEstimator estimator(variation_params);
  size_t expected_cache_size = 0;
  EXPECT_EQ(expected_cache_size, estimator.cached_network_qualities_.size());

  // Cache entry will not be added for (NONE, "").
  estimator.downstream_throughput_kbps_observations_.AddObservation(
      NetworkQualityEstimator::ThroughputObservation(
          1, base::TimeTicks::Now(), NetworkQualityEstimator::URL_REQUEST));
  estimator.rtt_msec_observations_.AddObservation(
      NetworkQualityEstimator::RttObservation(
          base::TimeDelta::FromMilliseconds(1000), base::TimeTicks::Now(),
          NetworkQualityEstimator::URL_REQUEST));
  estimator.SimulateNetworkChangeTo(
      NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test-1");
  EXPECT_EQ(expected_cache_size, estimator.cached_network_qualities_.size());

  // Entry will be added for (2G, "test1").
  // Also, set the network quality for (2G, "test1") so that it is stored in
  // the cache.
  estimator.downstream_throughput_kbps_observations_.AddObservation(
      NetworkQualityEstimator::ThroughputObservation(
          1, base::TimeTicks::Now(), NetworkQualityEstimator::URL_REQUEST));
  estimator.rtt_msec_observations_.AddObservation(
      NetworkQualityEstimator::RttObservation(
          base::TimeDelta::FromMilliseconds(1000), base::TimeTicks::Now(),
          NetworkQualityEstimator::URL_REQUEST));

  estimator.SimulateNetworkChangeTo(
      NetworkChangeNotifier::ConnectionType::CONNECTION_3G, "test-1");
  ++expected_cache_size;
  EXPECT_EQ(expected_cache_size, estimator.cached_network_qualities_.size());

  // Entry will be added for (3G, "test1").
  // Also, set the network quality for (3G, "test1") so that it is stored in
  // the cache.
  estimator.downstream_throughput_kbps_observations_.AddObservation(
      NetworkQualityEstimator::ThroughputObservation(
          2, base::TimeTicks::Now(), NetworkQualityEstimator::URL_REQUEST));
  estimator.rtt_msec_observations_.AddObservation(
      NetworkQualityEstimator::RttObservation(
          base::TimeDelta::FromMilliseconds(500), base::TimeTicks::Now(),
          NetworkQualityEstimator::URL_REQUEST));
  estimator.SimulateNetworkChangeTo(
      NetworkChangeNotifier::ConnectionType::CONNECTION_3G, "test-2");
  ++expected_cache_size;
  EXPECT_EQ(expected_cache_size, estimator.cached_network_qualities_.size());

  // Entry will not be added for (3G, "test2").
  estimator.SimulateNetworkChangeTo(
      NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test-1");
  EXPECT_EQ(expected_cache_size, estimator.cached_network_qualities_.size());

  // Read the network quality for (2G, "test-1").
  EXPECT_TRUE(estimator.ReadCachedNetworkQualityEstimate());

  base::TimeDelta rtt;
  int32_t kbps;
  EXPECT_TRUE(estimator.GetRTTEstimate(&rtt));
  EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
  EXPECT_EQ(1, kbps);
  EXPECT_EQ(base::TimeDelta::FromMilliseconds(1000), rtt);
  // No new entry should be added for (2G, "test-1") since it already exists
  // in the cache.
  estimator.SimulateNetworkChangeTo(
      NetworkChangeNotifier::ConnectionType::CONNECTION_3G, "test-1");
  EXPECT_EQ(expected_cache_size, estimator.cached_network_qualities_.size());

  // Read the network quality for (3G, "test-1").
  EXPECT_TRUE(estimator.ReadCachedNetworkQualityEstimate());
  EXPECT_TRUE(estimator.GetRTTEstimate(&rtt));
  EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
  EXPECT_EQ(2, kbps);
  EXPECT_EQ(base::TimeDelta::FromMilliseconds(500), rtt);
  // No new entry should be added for (3G, "test1") since it already exists
  // in the cache.
  estimator.SimulateNetworkChangeTo(
      NetworkChangeNotifier::ConnectionType::CONNECTION_3G, "test-2");
  EXPECT_EQ(expected_cache_size, estimator.cached_network_qualities_.size());

  // Reading quality of (3G, "test-2") should return false.
  EXPECT_FALSE(estimator.ReadCachedNetworkQualityEstimate());

  // Reading quality of (2G, "test-3") should return false.
  estimator.SimulateNetworkChangeTo(
      NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test-3");
  EXPECT_FALSE(estimator.ReadCachedNetworkQualityEstimate());
}

// Tests if the cache size remains bounded. Also, ensure that the cache is
// LRU.
TEST(NetworkQualityEstimatorTest, TestLRUCacheMaximumSize) {
  std::map<std::string, std::string> variation_params;
  TestNetworkQualityEstimator estimator(variation_params);
  estimator.SimulateNetworkChangeTo(
      net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI,
      std::string());
  EXPECT_EQ(0U, estimator.cached_network_qualities_.size());

  // Add 100 more networks than the maximum size of the cache.
  size_t network_count =
      NetworkQualityEstimator::kMaximumNetworkQualityCacheSize + 100;

  base::TimeTicks update_time_of_network_100;
  for (size_t i = 0; i < network_count; ++i) {
    estimator.downstream_throughput_kbps_observations_.AddObservation(
        NetworkQualityEstimator::ThroughputObservation(
            2, base::TimeTicks::Now(), NetworkQualityEstimator::URL_REQUEST));
    estimator.rtt_msec_observations_.AddObservation(
        NetworkQualityEstimator::RttObservation(
            base::TimeDelta::FromMilliseconds(500), base::TimeTicks::Now(),
            NetworkQualityEstimator::URL_REQUEST));

    if (i == 100)
      update_time_of_network_100 = base::TimeTicks::Now();

    estimator.SimulateNetworkChangeTo(
        net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI,
        base::SizeTToString(i));
    if (i < NetworkQualityEstimator::kMaximumNetworkQualityCacheSize)
      EXPECT_EQ(i, estimator.cached_network_qualities_.size());
    EXPECT_LE(estimator.cached_network_qualities_.size(),
              static_cast<size_t>(
                  NetworkQualityEstimator::kMaximumNetworkQualityCacheSize));
  }
  // One more call so that the last network is also written to cache.
  estimator.downstream_throughput_kbps_observations_.AddObservation(
      NetworkQualityEstimator::ThroughputObservation(
          2, base::TimeTicks::Now(), NetworkQualityEstimator::URL_REQUEST));
  estimator.rtt_msec_observations_.AddObservation(
      NetworkQualityEstimator::RttObservation(
          base::TimeDelta::FromMilliseconds(500), base::TimeTicks::Now(),
          NetworkQualityEstimator::URL_REQUEST));
  estimator.SimulateNetworkChangeTo(
      net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI,
      base::SizeTToString(network_count - 1));
  EXPECT_EQ(static_cast<size_t>(
                NetworkQualityEstimator::kMaximumNetworkQualityCacheSize),
            estimator.cached_network_qualities_.size());

  // Test that the cache is LRU by examining its contents. Networks in cache
  // must all be newer than the 100th network.
  for (NetworkQualityEstimator::CachedNetworkQualities::iterator it =
           estimator.cached_network_qualities_.begin();
       it != estimator.cached_network_qualities_.end(); ++it) {
    EXPECT_GE((it->second).last_update_time_, update_time_of_network_100);
  }
}

TEST(NetworkQualityEstimatorTest, TestGetMedianRTTSince) {
  std::map<std::string, std::string> variation_params;
  TestNetworkQualityEstimator estimator(variation_params);
  base::TimeTicks now = base::TimeTicks::Now();
  base::TimeTicks old = now - base::TimeDelta::FromMilliseconds(1);
  ASSERT_NE(old, now);

  // First sample has very old timestamp.
  estimator.downstream_throughput_kbps_observations_.AddObservation(
      NetworkQualityEstimator::ThroughputObservation(
          1, old, NetworkQualityEstimator::URL_REQUEST));
  estimator.rtt_msec_observations_.AddObservation(
      NetworkQualityEstimator::RttObservation(
          base::TimeDelta::FromMilliseconds(1), old,
          NetworkQualityEstimator::URL_REQUEST));

  estimator.downstream_throughput_kbps_observations_.AddObservation(
      NetworkQualityEstimator::ThroughputObservation(
          100, now, NetworkQualityEstimator::URL_REQUEST));
  estimator.rtt_msec_observations_.AddObservation(
      NetworkQualityEstimator::RttObservation(
          base::TimeDelta::FromMilliseconds(100), now,
          NetworkQualityEstimator::URL_REQUEST));

  base::TimeDelta rtt;
  EXPECT_FALSE(estimator.GetRecentMedianRTT(
      now + base::TimeDelta::FromSeconds(10), &rtt));
  EXPECT_TRUE(estimator.GetRecentMedianRTT(now, &rtt));
  EXPECT_EQ(100, rtt.InMilliseconds());

  int32_t downstream_throughput_kbps;
  EXPECT_FALSE(estimator.GetRecentMedianDownlinkThroughputKbps(
      now + base::TimeDelta::FromSeconds(10), &downstream_throughput_kbps));
  EXPECT_TRUE(estimator.GetRecentMedianDownlinkThroughputKbps(
      now, &downstream_throughput_kbps));
  EXPECT_EQ(100, downstream_throughput_kbps);
}

// An external estimate provider that does not have a valid RTT or throughput
// estimate.
class InvalidExternalEstimateProvider : public ExternalEstimateProvider {
 public:
  InvalidExternalEstimateProvider() : get_rtt_count_(0) {}
  ~InvalidExternalEstimateProvider() override {}

  // ExternalEstimateProvider implementation:
  bool GetRTT(base::TimeDelta* rtt) const override {
    DCHECK(rtt);
    get_rtt_count_++;
    return false;
  }

  // ExternalEstimateProvider implementation:
  bool GetDownstreamThroughputKbps(
      int32_t* downstream_throughput_kbps) const override {
    DCHECK(downstream_throughput_kbps);
    return false;
  }

  // ExternalEstimateProvider implementation:
  bool GetUpstreamThroughputKbps(
      int32_t* upstream_throughput_kbps) const override {
    // NetworkQualityEstimator does not support upstream throughput.
    ADD_FAILURE();
    return false;
  }

  // ExternalEstimateProvider implementation:
  bool GetTimeSinceLastUpdate(
      base::TimeDelta* time_since_last_update) const override {
    *time_since_last_update = base::TimeDelta::FromMilliseconds(1);
    return true;
  }

  // ExternalEstimateProvider implementation:
  void SetUpdatedEstimateDelegate(UpdatedEstimateDelegate* delegate) override {}

  // ExternalEstimateProvider implementation:
  void Update() const override {}

  size_t get_rtt_count() const { return get_rtt_count_; }

 private:
  // Keeps track of number of times different functions were called.
  mutable size_t get_rtt_count_;

  DISALLOW_COPY_AND_ASSIGN(InvalidExternalEstimateProvider);
};

// Tests if the RTT value from external estimate provider is discarded if the
// external estimate provider is invalid.
TEST(NetworkQualityEstimatorTest, InvalidExternalEstimateProvider) {
  InvalidExternalEstimateProvider* invalid_external_estimate_provider =
      new InvalidExternalEstimateProvider();
  scoped_ptr<ExternalEstimateProvider> external_estimate_provider(
      invalid_external_estimate_provider);

  TestNetworkQualityEstimator estimator(std::map<std::string, std::string>(),
                                        std::move(external_estimate_provider));

  base::TimeDelta rtt;
  int32_t kbps;
  EXPECT_EQ(1U, invalid_external_estimate_provider->get_rtt_count());
  EXPECT_FALSE(estimator.GetRTTEstimate(&rtt));
  EXPECT_FALSE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
}

class TestExternalEstimateProvider : public ExternalEstimateProvider {
 public:
  TestExternalEstimateProvider(base::TimeDelta rtt,
                               int32_t downstream_throughput_kbps)
      : rtt_(rtt),
        downstream_throughput_kbps_(downstream_throughput_kbps),
        time_since_last_update_(base::TimeDelta::FromSeconds(1)),
        get_time_since_last_update_count_(0),
        get_rtt_count_(0),
        get_downstream_throughput_kbps_count_(0),
        update_count_(0) {}
  ~TestExternalEstimateProvider() override {}

  // ExternalEstimateProvider implementation:
  bool GetRTT(base::TimeDelta* rtt) const override {
    *rtt = rtt_;
    get_rtt_count_++;
    return true;
  }

  // ExternalEstimateProvider implementation:
  bool GetDownstreamThroughputKbps(
      int32_t* downstream_throughput_kbps) const override {
    *downstream_throughput_kbps = downstream_throughput_kbps_;
    get_downstream_throughput_kbps_count_++;
    return true;
  }

  // ExternalEstimateProvider implementation:
  bool GetUpstreamThroughputKbps(
      int32_t* upstream_throughput_kbps) const override {
    // NetworkQualityEstimator does not support upstream throughput.
    ADD_FAILURE();
    return false;
  }

  // ExternalEstimateProvider implementation:
  bool GetTimeSinceLastUpdate(
      base::TimeDelta* time_since_last_update) const override {
    *time_since_last_update = time_since_last_update_;
    get_time_since_last_update_count_++;
    return true;
  }

  // ExternalEstimateProvider implementation:
  void SetUpdatedEstimateDelegate(UpdatedEstimateDelegate* delegate) override {}

  // ExternalEstimateProvider implementation:
  void Update() const override { update_count_++; }

  void set_time_since_last_update(base::TimeDelta time_since_last_update) {
    time_since_last_update_ = time_since_last_update;
  }

  size_t get_time_since_last_update_count() const {
    return get_time_since_last_update_count_;
  }
  size_t get_rtt_count() const { return get_rtt_count_; }
  size_t get_downstream_throughput_kbps_count() const {
    return get_downstream_throughput_kbps_count_;
  }
  size_t update_count() const { return update_count_; }

 private:
  // RTT and downstream throughput estimates.
  const base::TimeDelta rtt_;
  const int32_t downstream_throughput_kbps_;

  base::TimeDelta time_since_last_update_;

  // Keeps track of number of times different functions were called.
  mutable size_t get_time_since_last_update_count_;
  mutable size_t get_rtt_count_;
  mutable size_t get_downstream_throughput_kbps_count_;
  mutable size_t update_count_;

  DISALLOW_COPY_AND_ASSIGN(TestExternalEstimateProvider);
};

// Tests if the external estimate provider is called in the constructor and
// on network change notification.
TEST(NetworkQualityEstimatorTest, TestExternalEstimateProvider) {
  TestExternalEstimateProvider* test_external_estimate_provider =
      new TestExternalEstimateProvider(base::TimeDelta::FromMilliseconds(1),
                                       100);
  scoped_ptr<ExternalEstimateProvider> external_estimate_provider(
      test_external_estimate_provider);
  std::map<std::string, std::string> variation_params;
  TestNetworkQualityEstimator estimator(variation_params,
                                        std::move(external_estimate_provider));

  base::TimeDelta rtt;
  int32_t kbps;
  EXPECT_TRUE(estimator.GetRTTEstimate(&rtt));
  EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));

  EXPECT_EQ(
      1U, test_external_estimate_provider->get_time_since_last_update_count());
  EXPECT_EQ(1U, test_external_estimate_provider->get_rtt_count());
  EXPECT_EQ(
      1U,
      test_external_estimate_provider->get_downstream_throughput_kbps_count());

  // Change network type to WiFi. Number of queries to External estimate
  // provider must increment.
  estimator.SimulateNetworkChangeTo(
      NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-1");
  EXPECT_TRUE(estimator.GetRTTEstimate(&rtt));
  EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
  EXPECT_EQ(
      2U, test_external_estimate_provider->get_time_since_last_update_count());
  EXPECT_EQ(2U, test_external_estimate_provider->get_rtt_count());
  EXPECT_EQ(
      2U,
      test_external_estimate_provider->get_downstream_throughput_kbps_count());

  // Change network type to 2G. Number of queries to External estimate provider
  // must increment.
  estimator.SimulateNetworkChangeTo(
      NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test-1");
  EXPECT_EQ(
      3U, test_external_estimate_provider->get_time_since_last_update_count());
  EXPECT_EQ(3U, test_external_estimate_provider->get_rtt_count());
  EXPECT_EQ(
      3U,
      test_external_estimate_provider->get_downstream_throughput_kbps_count());

  // Set the external estimate as old. Network Quality estimator should request
  // an update on connection type change.
  EXPECT_EQ(0U, test_external_estimate_provider->update_count());
  test_external_estimate_provider->set_time_since_last_update(
      base::TimeDelta::Max());

  estimator.SimulateNetworkChangeTo(
      NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test-2");
  EXPECT_EQ(
      4U, test_external_estimate_provider->get_time_since_last_update_count());
  EXPECT_EQ(3U, test_external_estimate_provider->get_rtt_count());
  EXPECT_EQ(
      3U,
      test_external_estimate_provider->get_downstream_throughput_kbps_count());
  EXPECT_EQ(1U, test_external_estimate_provider->update_count());

  // Estimates are unavailable because external estimate provider never
  // notifies network quality estimator of the updated estimates.
  EXPECT_FALSE(estimator.GetRTTEstimate(&rtt));
  EXPECT_FALSE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
}

// Tests if the estimate from the external estimate provider is merged with the
// observations collected from the HTTP requests.
TEST(NetworkQualityEstimatorTest, TestExternalEstimateProviderMergeEstimates) {
  const base::TimeDelta external_estimate_provider_rtt =
      base::TimeDelta::FromMilliseconds(1);
  const int32_t external_estimate_provider_downstream_throughput = 100;
  TestExternalEstimateProvider* test_external_estimate_provider =
      new TestExternalEstimateProvider(
          external_estimate_provider_rtt,
          external_estimate_provider_downstream_throughput);
  scoped_ptr<ExternalEstimateProvider> external_estimate_provider(
      test_external_estimate_provider);

  std::map<std::string, std::string> variation_params;
  TestNetworkQualityEstimator estimator(variation_params,
                                        std::move(external_estimate_provider));

  base::TimeDelta rtt;
  // Estimate provided by network quality estimator should match the estimate
  // provided by external estimate provider.
  EXPECT_TRUE(estimator.GetRTTEstimate(&rtt));
  EXPECT_EQ(external_estimate_provider_rtt, rtt);

  int32_t kbps;
  EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
  EXPECT_EQ(external_estimate_provider_downstream_throughput, kbps);

  EXPECT_EQ(1U, estimator.rtt_msec_observations_.Size());
  EXPECT_EQ(1U, estimator.downstream_throughput_kbps_observations_.Size());

  TestDelegate test_delegate;
  TestURLRequestContext context(true);
  context.set_network_quality_estimator(&estimator);
  context.Init();

  scoped_ptr<URLRequest> request(context.CreateRequest(
      estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
  request->Start();
  base::RunLoop().Run();

  EXPECT_EQ(2U, estimator.rtt_msec_observations_.Size());
  EXPECT_EQ(2U, estimator.downstream_throughput_kbps_observations_.Size());
}

TEST(NetworkQualityEstimatorTest, TestObservers) {
  TestRTTObserver rtt_observer;
  TestThroughputObserver throughput_observer;
  std::map<std::string, std::string> variation_params;
  TestNetworkQualityEstimator estimator(variation_params);
  estimator.AddRTTObserver(&rtt_observer);
  estimator.AddThroughputObserver(&throughput_observer);

  TestDelegate test_delegate;
  TestURLRequestContext context(true);
  context.set_network_quality_estimator(&estimator);
  context.Init();

  EXPECT_EQ(0U, rtt_observer.observations().size());
  EXPECT_EQ(0U, throughput_observer.observations().size());
  base::TimeTicks then = base::TimeTicks::Now();

  scoped_ptr<URLRequest> request(context.CreateRequest(
      estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
  request->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME);
  request->Start();
  base::RunLoop().Run();

  scoped_ptr<URLRequest> request2(context.CreateRequest(
      estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
  request2->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME);
  request2->Start();
  base::RunLoop().Run();

  // Both RTT and downstream throughput should be updated.
  EXPECT_NE(NetworkQualityEstimator::InvalidRTT(),
            estimator.GetRTTEstimateInternal(base::TimeTicks(), 100));
  EXPECT_NE(NetworkQualityEstimator::kInvalidThroughput,
            estimator.GetDownlinkThroughputKbpsEstimateInternal(
                base::TimeTicks(), 100));

  EXPECT_EQ(2U, rtt_observer.observations().size());
  EXPECT_EQ(2U, throughput_observer.observations().size());
  for (auto observation : rtt_observer.observations()) {
    EXPECT_LE(0, observation.rtt_ms);
    EXPECT_LE(0, (observation.timestamp - then).InMilliseconds());
    EXPECT_EQ(NetworkQualityEstimator::URL_REQUEST, observation.source);
  }
  for (auto observation : throughput_observer.observations()) {
    EXPECT_LE(0, observation.throughput_kbps);
    EXPECT_LE(0, (observation.timestamp - then).InMilliseconds());
    EXPECT_EQ(NetworkQualityEstimator::URL_REQUEST, observation.source);
  }

  // Verify that observations from TCP and QUIC are passed on to the observers.
  base::TimeDelta tcp_rtt(base::TimeDelta::FromMilliseconds(1));
  base::TimeDelta quic_rtt(base::TimeDelta::FromMilliseconds(2));

  scoped_ptr<SocketPerformanceWatcher> tcp_watcher =
      estimator.CreateSocketPerformanceWatcher(
          SocketPerformanceWatcherFactory::PROTOCOL_TCP);
  scoped_ptr<SocketPerformanceWatcher> quic_watcher =
      estimator.CreateSocketPerformanceWatcher(
          SocketPerformanceWatcherFactory::PROTOCOL_QUIC);
  tcp_watcher->OnUpdatedRTTAvailable(tcp_rtt);
  quic_watcher->OnUpdatedRTTAvailable(quic_rtt);

  EXPECT_EQ(4U, rtt_observer.observations().size());
  EXPECT_EQ(2U, throughput_observer.observations().size());

  EXPECT_EQ(tcp_rtt.InMilliseconds(), rtt_observer.observations().at(2).rtt_ms);
  EXPECT_EQ(quic_rtt.InMilliseconds(),
            rtt_observer.observations().at(3).rtt_ms);
}

}  // namespace net

redpajama_set_name: RedPajamaGithub


text:
Tag Archives: Sid Meier
New Game Weekend: Civilization & Advanced Civilization
Earlier in the week I posted about the elegantly quick board game Eight-Minute Empire: Legends I first had a chance to play last weekend at Metropolitan Wargamers in Brooklyn, NY. After a brief, engaging 20 minutes with Legends, I joined four other players in another game that was also new to me and a completely different experience — Advanced Civilization.
Civilization game board with Western expansion map to the left
Civilization by UK game designer Francis Tresham was published by Avalon Hill in 1981 and introduced a number of now-standard gaming elements found in many board and video games today. Ten years later, Advanced Civilization offered some refinements, additions and simplifications to the game. Playing with 2-8 players and running to 8, 10, 12 or more rivetingly-complex hours, Civilization has become one of the signature games of the past 40 years.
Advanced Civilization's innovative Archaeological Succession Table (AST)
At its core, Civilization is an area control game played in the Mediterranean region in an 8000-year period spanning the Stone Age through the Iron Age. Each player picks a civilization and places a single population marker in a territory in their color-coded beginning edge of the board. Territories each support varying numbers of population, indicated in each area. Population doubles each turn, so one marker grows to two. If the space cannot sustain more than the noted number of population markers, players must move and expand their population to new territories or "starve" population down to the maximum load for the territory. A population census grid on the Archaeology Succession Track (AST) notes population growth of each civilization and determines the order of play the following turn.
Moving six population markers to a space with a black square allows the settlement of a city, and twelve may settle a city in a blank space. Establishing cities allows the harvesting of goods the likes of Hides & Ochre, Salt & Timber or Gems & Dye. Drawn in turn order, the stacks of commodities cards also hide calamity cards which have adverse effect on a civilization through such things as Famine, Civil War, Flood or Volcano. Each round includes trading where players must keep certain untradeable calamities while trading away certain goods and other calamities in the quest to collect sets of cards which may then be turned into technologies. The technologies build upon each other through the building of a Technology Tree ("Tech Tree") which advances with additional abilities and protections for the civilization. Simple beginning technologies like Pottery, Mythology and Music aid early societies but lose importance over time. Philosophy, Democracy, Law, Trade Empire, Advanced Military and Golden Age technologies score the greatest points and advance a society through the Stone, Bronze and Iron Ages to eventual victory.
Advanced Civilization at Metropolitan Wargamers
The AST and Tech Tree become the major machines in propelling the game forward, and evolutions of these elements have been incorporated into countless board and video games since their introduction with Civilization. These two innovations make Civilization and other subsequent games like Clash of Cultures focused more largely on elements of managing resources and cultural advancement than traditional combat-focused area control games like Risk. Resource management and building based on collection of resources shows up in so many games, prominently so in the widely-popular Settlers of Catan. Somewhat controversially, the video and board game versions of the Sid Meir's Civilization franchise owes a great deal both in its mechanics to the Avalon Hill game which preceded it in theme and name.
Civilization is incredibly compelling and eerily historic in how it plays and forces the hand of history. Players who begin with small civilizations are forced into territorial expansion through population growth or risk starvation of their people. Once cities thrive, feeding those cities becomes another concern and reason for more expansion. Soon, players find themselves in a loop where development begets expansion begets development. Isolated societies have to begin moving farther afield over land of through developing shipbuilding and sailing technologies. Resources become important to build technologies to further enhance and protect their civilization. By building more cities, more valuable goods are obtained and more advanced technologies may be developed. With all players expanding their societies by mid-game, civilizations eventually must compete for room in the region. War becomes somewhat inevitable, but diplomacy and mutual-beneficial trade also gain special importance as the game moves on and civilization begin to settle into specific paths to winning the game.
Of the hundreds of games I've played, I now understand why Civilization holds a special place in the hearts of gamers. Committing to hours upon hours of play becomes a game mechanic unto itself as players of Civilization must maintain focused attention on mentally-exhausting detail. Yes, Civilization is a game, but a marvelously intriguing one to play in the context of our own world where population, space, resources and technologies play out with increasingly dire importance as human civilization marches toward our own eventual historical endgame.
Posted in New Game Weekend
Tagged Advanced Civilization, archaelogical succession table, area control, Avalon Hill, board games, Civilization, Clash of Cultures, Francis Tresham, Metropolitan Wargamers, Risk (game), Settlers of Catan, Sid Meier, tech tree, technology tree, video games, wargaming
New Game Weekend: Sid Meier's Civilization: The Board Game
Unlike myself, electronic games have been a constant presence with both my sons since they were born. Now aged 8 and 13, my boys have graduated up through a host of platforms and games with the Leapster, DS, PS2, Wii, PS3, online through sites like Steam and on the iPhone. Over the past year, their obsessive play over Minecraft has been gradually replaced with scores of hours logged with the wildy-popular Civilization V.
Developed more than 20 years ago by Sid Meier and Bruce Shelley, the Civilization franchise has evolved from a PC-only DOS game to occupy a variety of gaming platforms. The turn-based civilization-building mechanic and historical theme sets up an incredibly-engaging and educational universe for players to plan and play out a nearly-endless variety of paths in human advancement from the ancient era to the near future. The latest iteration, Civilization V, broke on the scene in 2010 with two expansions since. Worldwide, the game has sold millions of copies and has continuously won accolades from a couple generations gaming critics and devoted fans alike.
Even though my kids are devoted electronic gamers, they've also been raised with a healthy love for card, board and miniatures gaming. So, when my 8-year-old caught wind last week that a boardgame version of Civilization existed, I leapt at the chance to introduce the boys to the game. I've played a number of civilization-building games such as Clash of Cultures but hadn't yet played Civilization.
Now published by Fantasy Flight Games, Sid Meier's Civilization: The Board Game has been around since 2002 and can be found at your local Barnes and Noble for $60 or cheaper online. Like many games of this type, the box comes stuffed with hundreds of components plus more than 300 cards. Geared for 2-4 players ages 14 and up, Civilization takes 3+ hours to play. Fortunately, my boys have been exposed to enough games, both online and on the tabletop, that they've been able to quickly pick up on the basic play and strategy Civilization offers.
Civilization involves a march toward victory in one of four ways: Economic, Military, Cultural or Technological. Players begin by selecting a civilization such as the Americans, Egyptians, Romans or Russians. Each starting civilization carries its own strengths in play, making this first choice an important one in setting your strategy for the game. Based on the number of players, a map of hidden tiles is laid out and the market area is set up to the side of the board.
After set-up, the game progresses through a series of turns. In the Start Turn, a player may build a city or change their government type – an important choice which can greatly affect the path of a player's civilization. Next, the Trade Phase allows a player to reap trade points from the areas around their cities and offer trades in resources with other players.
In the City Management Phase, the player may build units or buildings, harvest resources or focus on developing their culture. Building units – Scouts or Armies – allows the player to explore and conquer areas of the board. Constructing buildings such as Markets, Temples or Baracks allows a variety of bonuses to your civilization. At a higher price, Wonders like The Oracle, The Hanging Gardens or Angkor Wat may be built. Finally, a player may pay to build their military prowess through a progression from simple archers and pikemen to tanks and aircraft. Aside from building, a player may harvest resources such as silk, iron, incense or wheat to be later spent to activate certain technologies already researched. A player may also choose to focus on culture, with gained cultural points moving the player up the culture track while also earning special cultural event cards played to change different courses of the game's play.
Next, in the Movement Phase a players may move their Scouts and/or Armies to explore, claim new territory or engage in combat with a barbarian village or another player. In the final Research Phase, players may spend their earned trade points to develop new technologies. The game's "technology pyramid" is built progressively by each player as they grow from simple technologies such as Metalurgy and Horseback Riding toward higher level advancements all the way up to Flight and Nuclear Energy.
The play adavances through the above turns until a player arrives at a victory condition. Cultural victory is earned by completing progress on the culture track. A military victory is won by conquering an opponent's capital city. Earning a technological win is done by completing the technology pyramid all the way up to Space Flight. Finally, an economic outcome is reached by collecting 15 gold coins earned through various actions your civilization makes throughout the game.
To summarize the game in a couple paragraphs is hard, as the variation of play in the game is boundless. This weekend, my younger son and I played through our first quick starter game on an eight-tile two player-map (pic below). Despite over twenty pages or rules and scores of playing pieces to manage, he picked up the game really fast. Along the way, there's a ton of reading I'm certain is not covered in the general elementary curriculum with stops along the way to have side conversations on the meanings of words such as "despotism" and "anarchy." At his age, my son's memory is quite amazing and our discussions of the finer points of the rules has continued even away from the game table.
In our second game (pic below), three of us played on a larger map. My older son, a pretty experienced gamer at this point, blew threw a ten minute intro to the rules and played as the Prussians with an aggressively militaristic strategy I've grown used to over the years. Using the Egyptians, my younger son focused on culture and building construction. For my own strategy, I played as the Romans with a healthy mix of the military and cultural expansionism for which their empire was known.
For parents and critics of them who decry the falling attention spans of today's kids, weekends like this past one show how with a bit of parental creativity all the stereotypes simply fall away. A game like Civilization — both online and off — offers so many opportunities to build vocabulary, learn history and develop some complex multi-layered management skills I see sorely lacking in many of the adults I encounter. Over hours of focused play, not only are my sons and I developing our civilizations but we're also developing each other — an investment in time together well-spent.
How Sid Meier became one of the most recognizable names in gaming (pcgamer.com)
The Father of Civilization: Profile of Sid Meier (games.slashdot.org)
Culture is king in Sid Meier's Civilization V: Brave New World (business.financialpost.com)
2K Announces Sid Meier's Civilization V: Brave New World Expansion Pack Now Available (virtual-strategy.com)
Posted in Downloading, New Game Weekend
Tagged board games, Civilization V, Clash of Cultures, Fantasy Flight Games, Leapster, PS2, PS3, Sid Meier, Steam, video games, wargaming, Wii

redpajama_set_name: RedPajamaCommonCrawl


text:
Award of Honor recipient for New Zealand, David Shields
Started hairdressing in New Plymouth 1995 – his second apprenticeship after having completed and working as a fully trained saddler. Served on the Committee of the Taranaki Hairdressing Association. Represented New Zealand at World Cup Hairdressing in Tokyo before moving…
Award of Honor receipient for Hong Kong, Joseph Ho Shui-Chung
Mr Joseph HO Shiu-Chung is the Managing Director of Zenith Cosmetics Trading Co Ltd. He was the immediate past Chairman of the Cosmetic & Perfumery Association of Hong Kong Ltd (CPAHK), a leading trade association in cosmetics and beauty industry…
Success stories of 3 graduates: Traineeship Scheme in Services Industries of VTC (source: MingPao Jump in June 2018)
(Chinese topic: 開「剪」事業第一步,參加美容及美髮業見習員訓練-興趣變事業) Key highlights After obtained ipsn qualifications, two of the graduates (Andy and Reiko) successfully joined the Working Holiday Scheme and took up short term employments as Junior Hairdresser and Junior Beautician in Melbourne, Australia respectively. Dicky's…
THE IPSN WELCOMES NEW DIRECTOR (AUSTRALIA)
The IPSN is honored to introduce the new Director (Australia) Dianne Miles. CIDESCO Australia was delighted to accept the invitation from The IPSN to become a member for the next 12 months. Founded in 1946 with its head office in…
On a recent trip to New Zealand the IPSN invited, Kodie North to present a speech on her experiences of being an apprentice hairdresser in Auckland, New Zealand. With Kodie's permission we have posted her speech below as it is…
New Zealand Off-Job Training 2019
Off-job training arrangements for hairdressing and barbering apprentices have been confirmed for 2019. Hairdressing For the following regions we have scheduled off-job training as day release classes: Northland, Auckland, Waikato, Taranaki, Bay of Plenty, Manawatu, Whanganui, Hawkes Bay, Wellington, Nelson,…
HITO Graduation 2018
There was applause, advice and two special awards at HITO's 2018 graduation ceremony. Held at the Cordis Hotel in Auckland, the ceremony celebrated the achievements of barbers, hairdressers, beauty therapists and nail technicians who completed qualifications this year. Leading hairdressers…
Celebrating NZ's Top Talent In Hair And Beauty
New Zealand's top hair and beauty professionals were celebrated for their dedication, creativity, training and business skills at this year's Industry Awards. The glamorous, annual event was held at the Cordis Hotel in Auckland, hosted by the New Zealand Association…
The IPSN Strategic Plan for 2018-2020
The IPSN Board and Technical Delegates developed a 2 year strategic plan. You can find a few exerts from this plan below. If you would like a full copy, please contact the Executive Director executivedirector@theipsn.net Background The International Professional…
Congratulations to the Award of Honor Recipients
Congratulations to the recipients who have just received the Award of Honor for services to their respected industries! You can see all of their achievements and bio's by clicking the link below http://www.theipsn.net/about/award-of-honour/ Juliana Yang – Hong Kong Ms….


text:
Clay Aiken May Run for Political Office
Amy Sciarretto
Amy Sciarretto Published: January 2, 2014
Astrid Stawiarz, Getty Images
From 'American Idol' to political office? Well, stranger things have happened (like Kumar from 'Harold and Kumar Go to White Castle,' aka actor Kal Penn, becoming a White House staffer). Season 2 'American Idol' runner up Clay Aiken, who is openly gay, is considering running for office in 2014.
Aiken, who is 35 and a Raleigh native, lost the 'Idol' crown in 2003 to Ruben Studdard. He is now said to be actively considering a bid to represent North Carolina's 2nd congressional district in the U.S. House.
Sources familiar with the singer's plans confirmed the news and say he is taking all of the necessary steps, such as talking to campaign committees and acting like a potential candidate, to launch his political career. He is also supposedly working with a political strategist and has met with pollsters.
This all sounds pretty serious and like Aiken, who is also a father to a young son named Parker, is doing everything he needs to do in order to become a public servant.
Congressman Aiken. Does that have a ring to it? Would you vote for Aiken? It's not like the singer isn't used to being a public figure and in the hot seat!
Next: Whatever Happened to Your Fave 'American Idol' Winners?
Filed Under: Clay Aiken
Categories: Celebrity News, Music News, News


SKP10....系列 Valve actuators for safety shut-off valves open/closed. Two-stage or with integrated constant pressure governor. Datasheet contains dimensions for valves VGF..., VGG... and VGH... .
The actuator's hydraulic drive system consists of a cylinder filled with oil and an electric oscillating pump with piston and relief valve. The relief valve is located in the bypass between the suction and pressure side of the pump. With the actuators of the B-series, it is a valve hydraulically actuated by the pump pressure; with the 2-stage actuators of the A-series, it is a normally open solenoid valve. The cylinder carries a seal which hydraulically separates the inlet from the outlet side of the pump, also serving as a guidance for the piston. The piston is also guided by a rod which is rigidly connected to it. This rod transfers the piston's travel directly to the valve stem. The rod is provided with a disk the position of which is visible through a window in the console (indication of stroke).
The switching points of these switches are adjustable over the entire stroke. The adjusting screws are located in the terminal compartment.
other positions, as well as the limit changeover switches for positioning the lowand high-fire stroke with the two-stage actuators.I, II, III and air. They are used primarily in gas-fired combustion plant. The valves open slowly and close rapidly. All types of VG... gas valves can be combined with the available types of actuators. The actuator is fitted to the valve body by means of the 4 screws contained in the terminal compartment. The square flange can be turned in steps of 90°, thus providing 4 different mounting positions. The actuator can be fitted or replaced while the valve is under pressure. Sealing materials are not required.SKP20.130... operates as a balanced gas pressure governor and controls the gas pressure according to the pressure of the combustion air so that the gas / air ratio remains constant over the entire load range (shifting the setpoint by the static pressure of the combustion air).


She was found all alone on a suburban driveway. She was burning from the heat of the sun beating down on her small body, desperate for shade. She was just a baby.
Luckily, help arrived. Lucy, as she was named, was taken to the Australia Zoo Wildlife Hospital in Queensland, Australia (founded by the late Crocodile Hunter himself, Steve Irwin).
Lucy also suffered from a case of pinkeye, but is now well on her way to making a full recovery. She will then be released back into the wild. Her most recent update shows the koala is not dependent on milk and is well enough to nibble on solids.
It is unknown how Lucy ended up orphaned on a driveway, but according to the Australian Koala Foundation, approximately 80 percent of original koala habitat has already been destroyed through methods such as deforestation, forcing more and more of them to live alongside humans in urban and suburban areas.
Thankfully, this cutie was found just in time and is on a road toward healing filled with love.
Happy Australia Day! And to all the Aussies living abroad Lucy the koala joey says G'day from home!
Responses to "Baby Koala Clings To Teddy Bear After Losing Her Mom"
So glad that this little one was saved due to compassionate people. Is there nothing that can be done about this ruthless deforestation? What is the Government thinking about? Can the public not protest the devastation that is destroying these unique species and the environment? Very upsetting!
You can find out a lot of great information and how to help from anywhere in the world by visiting the Australian Koala Foundations website. I don't work for them but I am a supporter and sponsor a koala. One of their slogans is "No Trees, No Me" with a picture of a koala. Their website gives all sorts of ways we can help preserve the natural environment for koalas and many other wonderful animals.
I don't know if this link will go through but just in case, I'll try.


You have been hearing a lot about the best camera phones of the year, and when it comes to the very finest, three names pop up: the Google Pixel 3 series, the new iPhone XS and XR family, and the Samsung Galaxy series, best represented by the latest Note 9.
But which one of these takes the best photos?
We were as curious as everyone to find out and that's why we went out and shot a bunch of pictures with these phones. We will be bringing you quite a few detailed articles analyzing the differences, but before that, let's start with a blind comparison where you tell us which photo you like better. It works like this: we have hidden the names of the phones, the metadata and all other clues that might reveal which phone took which picture. For this comparison, you tell us based not on the name, but on the photo itself.
Ready? Let's waste no time and get started.
So that wraps up our blind comparison.
Feel free to discuss the photos in the comments section below and we will be unveiling the phones that took these pictures in the very near future, so stay tuned!
Forget it Vic, this thing is not even funny anymore. Everyone here knows you guys work for Apple.
Well, depending on who you ask. Some days we work for Samsung, and then some days for Google, and then some days for LG. You never know. /s But seriously, it's not like we force people to come here. We work hard to get quality coverage. And if your favorite phone does not always win, it's not the end of the world. It's just how we see it and we usually explain in great detail why.
I've said it before and I'll say it again. These blind comparisons only show which photo is liked the most by viewers at that particular instant. And not which one is the most true-to-life. Therefore, whichever phone wins, it won't suddenly become the best. Just thought I should put that out.
To the vast majority of people, a "better-looking" photo is more important than the most true-to-life photo. Only a handful of people care about accurate photos.
And too many selfies. Not conclusive than. Bottom line, you can see one is darker, one is yellowish, and one is icing. Everything is now on how you like pictures being. Like previous said by Amash you have no real scene light to compare. Maybe next time.
also, there is no 100% crop.
I don't care if the Pixel has the best camera, I'm going with Apple products from now on due to the fact that I've had either touch screen issues that Google won't acknowledge on my Pixel 2 XL (and I've only had it for 4 months) or general slowdown after some time during ownership which necessitates a factory reset. Then there's the pesky problem of most Android OEMs being pretty poor with updates which is why I finally went with the Pixel. The Android ecosystem and Google are slipping.
Here's a idea for a new article. Think you might like it. A blind camera test among the flagships.
You can easily pick out which pics are taken with the Pixel and iPhone XS, which leaves the last as the Note 9. All the iPhone XS pics have a distinct yellowish tint to them and the Pixel post processing brightens them too much, the Note 9 seems to do the best overall job here but lacks a bit in dark situations.
R won against p, ok.. Are you blind guys?
Agrees, P has way more light and detail of the trees on the right side of the picture.
The majority of people picked L1 under the picture "PORTRAIT MODE TESTED". Portrait mode completely failed on L1 look again. Look at the blur and branches.
All were bad in their own way. L1 was the best color/exposure for the main subject (man). Composition was bad. Portrait mode was never going to work for any.
I enjoy these articles, but what about the Mate 20 Pro?
PA knows best. We don't have a say in it.
please, please go somewhere else then.
Photography is a visual art thus it's subject to each person's interpretation. There is no "better" camera, it comes down to which takes the kind of pictures you like.
A number of these, I wouldn't pick any which points to a photographer issue.


While swimming pools have traditionally been freshwater, the use of saltwater swimming pools is growing in popularity. There are many misconceptions about salt-water pools. One is that salt-water pools are chlorine free, but this is not true. Salt-water pools actually use chlorine generators. Having a good understanding of how salt-water pools work and what their benefits are is necessary when making the decision between a fresh or salt-water setup for your pool.
The salt content in the ocean is roughly 35,000 parts per million ("ppm"). However, most humans can really only tolerate a salt content of about 3,500 ppm. Modern chlorine generators in swimming pools require the pool to have a salt content of between 2500 and 6000 ppm. Ideally, the generator being used should require less then 3500 ppm since this is what most people find comfortable.
Swimming in such a pool is comparable to showering using soft water. People using traditional fresh water pools find that the chlorine damages and dries their skin. However, upon exiting a salt-water pool, people find that their skin feels smooth, soft and hydrated.
How the Chlorine Generator Works.
The main purpose of the chlorine generator is to create chlorine for the pool, eliminating the need to purchase it, pour it in, and store the leftover chlorine. Most pool owners are attracted to this feature. When a chlorine generator is functioning properly, it will produce chlorine as long as the pump is running. Rather than the chlorine level rising and falling depending on maintenance, the chlorine level is kept constant and level. Bacteria and algae never have a chance to take over.
The key is the cell that makes up the generator. This must be made of precious metals so that is can be maintained and will continue creating chlorine. Using electrolysis, as water passes over the chlorine generator cell; chlorine is produced and immediately converted to Hypochlorous acid. All chlorine creates this same acid when added to water. Hypochlorous acid is the agent that actually acts as a sanitizer in any pool. Whether your pool is fresh or salt-water, and regardless of your choice of chlorine, it is the same end result. As with a fresh-water pool, the pH is vital to keeping the pool running smoothly and clean.
With a salt-water pool and chlorine generator, maintenance will still be required. However, the chlorine levels in the pool will be more consistent, making the pool cleaner. The water will leave your skin feeling soft and smooth rather than dry and brittle. Finally, the pool will cost less to run since there is no need to buy chlorine every week.


We had an enormous amount of rain that fell on us. It probably began around 23:30 with incredible displays of electricity in the sky, the rumblings of thunder soon came and there were a couple of real crackers, which set the dogs off around the countryside. I couldn't drop off to sleep as I was wondering what we were in for as the wind got stronger, because I hadn't seen the forecast for the night and I hadn't realised that we were in for a big'n.
The rain began lightly and at this point I thought it best to go out and close the shutters, the wind was getting up and it was coming from the north, so this blew straight into our bedroom and the living room. I popped out of the back door so not to disturb J or C, and was promptly followed by 3 cats and two kittens who thought I was bringing them food - oh no, not at that time of night.
Shutters on that side of the house closed I went back inside and tried to settle down for the night. I couldn't. The rain began in earnest and so I had to close the windows as well because the wind blew the rain under the exterior shutters (persianas). I popped over to the other side of the house to make sure the rain wasn't coming in there either, but that seemed fine...for a moment.....then I heard it.
It came down incredibly hard, it was like a tropical storm, I whipped round the house closing all the windows I could and although the weather came from the north the wind was all over the place and was blowing it in all directions.
The roof in the living room began to leak! so I grabbed bowls and towels (to stop the drip, drip noise), then I heard a LOT of water outside the front of the house and the dogs barking periodically. I decided to check.
Peering around the front door I saw a bucket float past! and Rex barked at it as if it was an intruder. I grabbed my waterproofs and wellies, desperately trying to pull a pair of socks on - I hate wellington boots with no socks!! You never know if there are spiders hiding in the toe end!! I grabbed a torch and went outside. Meanwhile...C was sound asleep and I couldn't wake him to help me out & J was sparko.
The flooding came up to my shins! great. I knew it was the drains covered in leaves, so for 20 mins I set about clearing them - it took a while but finally the water subsided and drained away. Then I thought ' oh heck the turkeys!'. Their pen is situated at the bottom of a small orange grove and when it rains it gets very, very muddy!!
On my way down the grove past the trees - which were all completely waterlogged - there was a huge puddle by the turkeys gate. I managed to untie the sodden rope and went into the pen, shining the torch to see if they were ok. I couldn't find them. You would think that they would have been snuggled up in their little house - but NO! they were in the corner under a tree, huddled up and as close to the back wall as possible. They looked completely bedraggled and their feathers were all wet, they were not happy boys. Half the pen was underwater, so I carefully picked up Spock and put him under cover and then Humphrey. Their food was mush, so I tipped it out and ran back to the store cupboard to get them some dry mash, ready for the morning. I then had to run back to the garage to grab some dry straw which they then snuggled into quite happily for the rest of the night.
On waking up this morning and looking outside, there was no trace of any flooding at all! The land has been so dry this summer that it just soaked it up as soon as possible.


text:
Q: Will a 1.8GHz MacBook Air be much faster than a 1.3GHz model? I am looking to purchase a refurbished Macbook Air from Apple's refurbished store.
When comparing the models there are two I am looking at. The difference between the two is slight but one of the difference is the processor.
One has a processor of 1.8GHz dual-core Intel Core i5 (Turbo Boost up to 2.8GHz) while the other has a 1.3GHz dual-core Intel Core i5 (Turbo Boost up to 2.6GHz).
I am experienced with PCs but not Macs. The price is nearly identical ($20 dollar difference.)
The newer model is 1.3Ghz.
Am I going to see a big difference in speed between the two Macs?

A: 1.3 GHz seems to be mid-2013 Haswell based machine with Intel HD 5000 graphics, the other one is probably mid-2012 Ivy-bridge one with Intel HD 4000.

*

*Haswell based CPU is about 5-10% faster than Ivy Bridge according to various benchmarks (so, all in all it should be comparable in terms of pure speed, but read further)

*Haswell chips make SSD drives run faster

*Intel HD 5000 is much better than 4000 (and that will give most significant boost to overall performance IMHO)

So, if all other specs are the same, I'd go for 1.3 Haswell one, the problem is that you never know what you'll get when buying refurbished machine.

A: You mention newer is 1.3 GHz, but you should look at little closer at the specific processor powering the two Macbook Airs (MBA). You can visit EveryMac and based on the year of the two MBAs determine the processor family.
If you find that the 1.3 GHz process is from the Haswell family while the 1.8 Ghz is from the previous family/generation, you're better of going with the Haswell chip which will yield better performance and power management yielding better battery life from the MBA.

redpajama_set_name: RedPajamaStackExchange