Bring CLI speedtest into line with the features on the HTML Sepedtest webpage
- implemented exponential moving average
- variable end condition basedon moving averages as per the Web page
- added a Task abstraction for running tests and other tasks
- added progress updates as an option
- full override options for all speedtest parameters
Change-Id: I1a4429993a7b4de9247e2654eca2c13ac5804769
diff --git a/speedtest/Makefile b/speedtest/Makefile
index b3b625c..af5ea27 100644
--- a/speedtest/Makefile
+++ b/speedtest/Makefile
@@ -4,7 +4,8 @@
BINDIR=$(PREFIX)/bin
DEBUG?=-g
WARNINGS=-Wall -Werror -Wno-unused-result -Wno-unused-but-set-variable
-CXXFLAGS=$(DEBUG) $(WARNINGS) -O3 -DNDEBUG -std=c++11 $(EXTRACFLAGS)
+CXXFLAGS=$(DEBUG) $(WARNINGS) -DNDEBUG -std=c++11 $(EXTRACFLAGS)
+#CXXFLAGS=$(DEBUG) $(WARNINGS) -O3 -DNDEBUG -std=c++11 $(EXTRACFLAGS)
LDFLAGS=$(DEBUG) $(EXTRALDFLAGS)
GTEST_DIR=googletest
@@ -15,16 +16,18 @@
TOBJS=curl_env.o url.o errors.o request.o utils.o
OBJS=config.o \
curl_env.o \
- download_test.o \
+ download_task.o \
errors.o \
- generic_test.o \
+ http_task.o \
options.o \
- ping_test.o \
+ ping_task.o \
request.o \
- runner.o \
speedtest.o \
- transfer_test.o \
- upload_test.o \
+ task.o \
+ timed_runner.o \
+ transfer_runner.o \
+ transfer_task.o \
+ upload_task.o \
url.o \
utils.o
@@ -33,27 +36,30 @@
config.o: config.cc config.h url.h
errors.o: errors.cc errors.h
curl_env.o: curl_env.cc curl_env.h errors.h request.h
-download_test.o: download_test.cc download_test.h generic_test.h transfer_test.h utils.h
-generic_test.o: generic_test.cc generic_test.h request.h utils.h
+download_task.o: download_task.cc download_task.h transfer_task.h utils.h
+http_task.o: http_task.cc http_task.h
options.o: options.cc options.h url.h
-ping_test.o: ping_test.cc ping_test.h generic_test.h request.h url.h utils.h
+ping_task.o: ping_task.cc ping_task.h http_task.h request.h url.h utils.h
request.o: request.cc request.h url.h
-runner.o: runner.cc runner.h generic_test.h transfer_test.h
speedtest.o: speedtest.cc \
speedtest.h \
config.h \
curl_env.h \
- download_test.h \
- generic_test.h \
+ download_task.h \
options.h \
- ping_test.h \
+ ping_task.h \
request.h \
- runner.h \
- upload_test.h \
+ task.h \
+ timed_runner.h \
+ transfer_runner.h \
+ upload_task.h \
url.h
speedtest_main.o: speedtest_main.cc options.h speedtest.h
-transfer_test.o: transfer_test.cc transfer_test.h generic_test.h
-upload_test.o: upload_test.cc upload_test.h generic_test.h transfer_test.h utils.h
+task.o: task.cc task.h utils.h
+timed_runner.o: timed_runner.cc timed_runner.h task.h
+transfer_runner.o: transfer_runner.cc transfer_runner.h transfer_task.h utils.h
+transfer_task.o: transfer_task.cc transfer_task.h http_task.h
+upload_task.o: upload_task.cc upload_task.h transfer_task.h utils.h
utils.o: utils.cc options.h
url.o: url.cc url.h utils.h
diff --git a/speedtest/config.cc b/speedtest/config.cc
index d12b4e5..aede959 100644
--- a/speedtest/config.cc
+++ b/speedtest/config.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,7 +35,7 @@
config->download_size = root["downloadSize"].asInt();
config->upload_size = root["uploadSize"].asInt();
- config->interval_size = root["intervalSize"].asInt();
+ config->interval_millis = root["intervalSize"].asInt();
config->location_name = root["locationName"].asString();
config->min_transfer_intervals = root["minTransferIntervals"].asInt();
config->max_transfer_intervals = root["maxTransferIntervals"].asInt();
@@ -79,7 +79,7 @@
void PrintConfig(std::ostream &out, const Config &config) {
out << "Download size: " << config.download_size << " bytes\n"
<< "Upload size: " << config.upload_size << " bytes\n"
- << "Interval size: " << config.interval_size << " ms\n"
+ << "Interval size: " << config.interval_millis << " ms\n"
<< "Location name: " << config.location_name << "\n"
<< "Min transfer intervals: " << config.min_transfer_intervals << "\n"
<< "Max transfer intervals: " << config.max_transfer_intervals << "\n"
diff --git a/speedtest/config.h b/speedtest/config.h
index 01b5483..2988484 100644
--- a/speedtest/config.h
+++ b/speedtest/config.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@
struct Config {
int download_size = 0;
int upload_size = 0;
- int interval_size = 0;
+ int interval_millis = 0;
std::string location_name;
int min_transfer_intervals = 0;
int max_transfer_intervals = 0;
diff --git a/speedtest/config_test.cc b/speedtest/config_test.cc
index 681e0fc..5924921 100644
--- a/speedtest/config_test.cc
+++ b/speedtest/config_test.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -86,7 +86,7 @@
EXPECT_EQ(20000000, config.upload_size);
EXPECT_EQ(20, config.num_downloads);
EXPECT_EQ(15, config.num_uploads);
- EXPECT_EQ(200, config.interval_size);
+ EXPECT_EQ(200, config.interval_millis);
EXPECT_EQ("Kansas City", config.location_name);
EXPECT_EQ(10, config.min_transfer_intervals);
EXPECT_EQ(25, config.max_transfer_intervals);
diff --git a/speedtest/curl_env.cc b/speedtest/curl_env.cc
index 4a49b99..eed370f 100644
--- a/speedtest/curl_env.cc
+++ b/speedtest/curl_env.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/speedtest/curl_env.h b/speedtest/curl_env.h
index 85a34ba..6a70f28 100644
--- a/speedtest/curl_env.h
+++ b/speedtest/curl_env.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/speedtest/download_test.cc b/speedtest/download_task.cc
similarity index 79%
rename from speedtest/download_test.cc
rename to speedtest/download_task.cc
index f23d97a..a643725 100644
--- a/speedtest/download_test.cc
+++ b/speedtest/download_task.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,25 +14,24 @@
* limitations under the License.
*/
-#include "download_test.h"
+#include "download_task.h"
#include <algorithm>
#include <cassert>
#include <iostream>
#include <thread>
-#include "generic_test.h"
#include "utils.h"
namespace speedtest {
-DownloadTest::DownloadTest(const Options &options)
- : TransferTest(options_),
+DownloadTask::DownloadTask(const Options &options)
+ : TransferTask(options_),
options_(options) {
assert(options_.num_transfers > 0);
assert(options_.download_size > 0);
}
-void DownloadTest::RunInternal() {
+void DownloadTask::RunInternal() {
ResetCounters();
threads_.clear();
if (options_.verbose) {
@@ -46,15 +45,15 @@
}
}
-void DownloadTest::StopInternal() {
+void DownloadTask::StopInternal() {
std::for_each(threads_.begin(), threads_.end(), [](std::thread &t) {
t.join();
});
}
-void DownloadTest::RunDownload(int id) {
- GenericTest::RequestPtr download = options_.request_factory(id);
- while (GetStatus() == TestStatus::RUNNING) {
+void DownloadTask::RunDownload(int id) {
+ http::Request::Ptr download = options_.request_factory(id);
+ while (GetStatus() == TaskStatus::RUNNING) {
long downloaded = 0;
download->set_param("i", to_string(id));
download->set_param("size", to_string(options_.download_size));
@@ -67,7 +66,7 @@
TransferBytes(dlnow - downloaded);
downloaded = dlnow;
}
- return GetStatus() != TestStatus::RUNNING;
+ return GetStatus() != TaskStatus::RUNNING;
});
StartRequest();
download->Get();
diff --git a/speedtest/download_test.h b/speedtest/download_task.h
similarity index 68%
rename from speedtest/download_test.h
rename to speedtest/download_task.h
index f8a249f..2b65478 100644
--- a/speedtest/download_test.h
+++ b/speedtest/download_task.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,22 +14,22 @@
* limitations under the License.
*/
-#ifndef SPEEDTEST_DOWNLOAD_TEST_H
-#define SPEEDTEST_DOWNLOAD_TEST_H
+#ifndef SPEEDTEST_DOWNLOAD_TASK_H
+#define SPEEDTEST_DOWNLOAD_TASK_H
#include <thread>
#include <vector>
-#include "transfer_test.h"
+#include "transfer_task.h"
namespace speedtest {
-class DownloadTest : public TransferTest {
+class DownloadTask : public TransferTask {
public:
- struct Options : TransferTest::Options {
+ struct Options : TransferTask::Options {
int download_size = 0;
};
- explicit DownloadTest(const Options &options);
+ explicit DownloadTask(const Options &options);
protected:
void RunInternal() override;
@@ -42,10 +42,10 @@
std::vector<std::thread> threads_;
// disallowed
- DownloadTest(const DownloadTest &) = delete;
- void operator=(const DownloadTest &) = delete;
+ DownloadTask(const DownloadTask &) = delete;
+ void operator=(const DownloadTask &) = delete;
};
} // namespace speedtest
-#endif // SPEEDTEST_DOWNLOAD_TEST_H
+#endif // SPEEDTEST_DOWNLOAD_TASK_H
diff --git a/speedtest/errors.cc b/speedtest/errors.cc
index 05f382b..43c94f8 100644
--- a/speedtest/errors.cc
+++ b/speedtest/errors.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/speedtest/errors.h b/speedtest/errors.h
index 334689d..4c70003 100644
--- a/speedtest/errors.h
+++ b/speedtest/errors.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/speedtest/generic_test.cc b/speedtest/generic_test.cc
deleted file mode 100644
index be012ba..0000000
--- a/speedtest/generic_test.cc
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2015 Google Inc. All rights reserved.
- *
- * 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 "generic_test.h"
-
-#include <cassert>
-#include "utils.h"
-
-namespace speedtest {
-
-const char *AsString(TestStatus status) {
- switch (status) {
- case TestStatus::NOT_STARTED: return "NOT_STARTED";
- case TestStatus::RUNNING: return "RUNNING";
- case TestStatus::STOPPING: return "STOPPING";
- case TestStatus::STOPPED: return "STOPPED";
- }
- std::exit(1);
-}
-
-GenericTest::GenericTest(const Options &options)
- : status_(TestStatus::NOT_STARTED) {
- assert(options.request_factory);
-}
-
-void GenericTest::Run() {
- {
- std::lock_guard <std::mutex> lock(mutex_);
- if (status_ != TestStatus::NOT_STARTED &&
- status_ != TestStatus::STOPPED) {
- return;
- }
- status_ = TestStatus::RUNNING;
- start_time_ = SystemTimeMicros();
- }
- RunInternal();
-}
-
-void GenericTest::Stop() {
- {
- std::lock_guard <std::mutex> lock(mutex_);
- if (status_ != TestStatus::RUNNING) {
- return;
- }
- status_ = TestStatus::STOPPING;
- }
- StopInternal();
- std::lock_guard <std::mutex> lock(mutex_);
- status_ = TestStatus::STOPPED;
- end_time_ = SystemTimeMicros();
-}
-
-TestStatus GenericTest::GetStatus() const {
- std::lock_guard <std::mutex> lock(mutex_);
- return status_;
-}
-
-long GenericTest::GetRunningTime() const {
- std::lock_guard <std::mutex> lock(mutex_);
- switch (status_) {
- case TestStatus::NOT_STARTED:
- break;
- case TestStatus::RUNNING:
- case TestStatus::STOPPING:
- return SystemTimeMicros() - start_time_;
- case TestStatus::STOPPED:
- return end_time_ - start_time_;
- }
- return 0;
-}
-
-void GenericTest::WaitForEnd() {
- std::unique_lock<std::mutex> lock(mutex_);
- condition_.wait(lock, [this]{
- return status_ == TestStatus::STOPPED;
- });
-}
-
-} // namespace speedtest
diff --git a/speedtest/runner.h b/speedtest/http_task.cc
similarity index 74%
copy from speedtest/runner.h
copy to speedtest/http_task.cc
index 4d677fc..1275aa4 100644
--- a/speedtest/runner.h
+++ b/speedtest/http_task.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,15 +14,11 @@
* limitations under the License.
*/
-#ifndef SPEEDTEST_RUNNER_H
-#define SPEEDTEST_RUNNER_H
-
-#include "generic_test.h"
+#include "http_task.h"
namespace speedtest {
-void TimedRun(GenericTest *test, long millis);
+HttpTask::HttpTask(const Options &options): Task(options) {
+}
} // namespace speedtest
-
-#endif // SPEEDTEST_RUNNER_H
diff --git a/speedtest/http_task.h b/speedtest/http_task.h
new file mode 100644
index 0000000..a54e4ba
--- /dev/null
+++ b/speedtest/http_task.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef SPEEDTEST_HTTP_TASK_H
+#define SPEEDTEST_HTTP_TASK_H
+
+#include "task.h"
+
+#include "request.h"
+
+namespace speedtest {
+
+class HttpTask : public Task {
+ public:
+ struct Options : Task::Options {
+ bool verbose = false;
+ std::function<http::Request::Ptr(int)> request_factory;
+ };
+
+ explicit HttpTask(const Options &options);
+
+ private:
+ // disallowed
+ HttpTask(const Task &) = delete;
+ void operator=(const HttpTask &) = delete;
+};
+
+} // namespace speedtest
+
+#endif // SPEEDTEST_HTTP_TASK_H
diff --git a/speedtest/options.cc b/speedtest/options.cc
index db7f3ae..133b857 100644
--- a/speedtest/options.cc
+++ b/speedtest/options.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,10 +29,19 @@
namespace {
-bool ParseLong(const char *s, char **endptr, long *size) {
- assert(s != nullptr);
- assert(size != nullptr);
- *size = strtol(s, endptr, 10);
+bool ParseLong(const char *s, char **endptr, long *number) {
+ assert(s);
+ assert(endptr);
+ assert(number != nullptr);
+ *number = strtol(s, endptr, 10);
+ return !**endptr;
+}
+
+bool ParseDouble(const char *s, char **endptr, double *number) {
+ assert(s);
+ assert(endptr);
+ assert(number != nullptr);
+ *number = strtod(s, endptr);
return !**endptr;
}
@@ -58,29 +67,47 @@
return true;
}
-const int kOptMinTransferTime = 1000;
-const int kOptMaxTransferTime = 1001;
-const int kOptPingRuntime = 1002;
-const int kOptPingTimeout = 1003;
-const int kOptDisableDnsCache = 1004;
-const int kOptMaxConnections = 1005;
+const int kOptDisableDnsCache = 1000;
+const int kOptMaxConnections = 1001;
+const int kOptExponentialMovingAverage = 1002;
+
+const int kOptMinTransferTime = 1100;
+const int kOptMaxTransferTime = 1101;
+const int kOptMinTransferIntervals = 1102;
+const int kOptMaxTransferIntervals = 1103;
+const int kOptMaxTransferVariance = 1104;
+const int kOptIntervalMillis = 1105;
+const int kOptPingRuntime = 1106;
+const int kOptPingTimeout = 1107;
+
const char *kShortOpts = "hvg:a:d:s:t:u:p:";
+
struct option kLongOpts[] = {
{"help", no_argument, nullptr, 'h'},
{"verbose", no_argument, nullptr, 'v'},
{"global_host", required_argument, nullptr, 'g'},
{"user_agent", required_argument, nullptr, 'a'},
+ {"disable_dns_cache", no_argument, nullptr, kOptDisableDnsCache},
+ {"max_connections", required_argument, nullptr, kOptMaxConnections},
+ {"progress_millis", required_argument, nullptr, 'p'},
+ {"exponential_moving_average", no_argument, nullptr,
+ kOptExponentialMovingAverage},
+
{"num_downloads", required_argument, nullptr, 'd'},
{"download_size", required_argument, nullptr, 's'},
{"num_uploads", required_argument, nullptr, 'u'},
{"upload_size", required_argument, nullptr, 't'},
- {"progress", required_argument, nullptr, 'p'},
- {"min_transfer_time", required_argument, nullptr, kOptMinTransferTime},
- {"max_transfer_time", required_argument, nullptr, kOptMaxTransferTime},
+ {"min_transfer_runtime", required_argument, nullptr, kOptMinTransferTime},
+ {"max_transfer_runtime", required_argument, nullptr, kOptMaxTransferTime},
+ {"min_transfer_intervals", required_argument, nullptr,
+ kOptMinTransferIntervals},
+ {"max_transfer_intervals", required_argument, nullptr,
+ kOptMaxTransferIntervals},
+ {"max_transfer_variance", required_argument, nullptr,
+ kOptMaxTransferVariance},
+ {"interval_millis", required_argument, nullptr, kOptIntervalMillis},
{"ping_runtime", required_argument, nullptr, kOptPingRuntime},
{"ping_timeout", required_argument, nullptr, kOptPingTimeout},
- {"disable_dns_cache", no_argument, nullptr, kOptDisableDnsCache},
- {"max_connections", required_argument, nullptr, kOptMaxConnections},
{nullptr, 0, nullptr, 0},
};
const int kMaxNumber = 1000;
@@ -95,23 +122,28 @@
will be used without pinging.
Usage: speedtest [options] [host ...]
- -h, --help This help text
- -v, --verbose Verbose output
- -g, --global_host URL Global host URL
- -a, --user_agent AGENT User agent string for HTTP requests
- --disable_dns_cache Disable global DNS cache
- --max_connections NUM Maximum number of parallel connections
+ -h, --help This help text
+ -v, --verbose Verbose output
+ -g, --global_host URL Global host URL
+ -a, --user_agent AGENT User agent string for HTTP requests
+ -p, --progress_millis NUM Delay in milliseconds between updates
+ --disable_dns_cache Disable global DNS cache
+ --max_connections NUM Maximum number of parallel connections
+ --exponential_moving_average Use exponential instead of simple moving average
These options override the speedtest config parameters:
- -d, --num_downloads NUM Number of simultaneous downloads
- -p, --progress TIME Progress intervals in milliseconds
- -s, --download_size SIZE Download size in bytes
- -t, --upload_size SIZE Upload size in bytes
- -u, --num_uploads NUM Number of simultaneous uploads
- --min_transfer_time TIME Minimum transfer time in milliseconds
- --max_transfer_time TIME Maximum transfer time in milliseconds
- --ping_time TIME Ping time in milliseconds
- --ping_timeout TIME Ping timeout in milliseconds;
+ -d, --num_downloads NUM Number of simultaneous downloads
+ -s, --download_size SIZE Download size in bytes
+ -t, --upload_size SIZE Upload size in bytes
+ -u, --num_uploads NUM Number of simultaneous uploads
+ --min_transfer_runtime TIME Minimum transfer time in milliseconds
+ --max_transfer_runtime TIME Maximum transfer time in milliseconds
+ --min_transfer_intervals NUM Short moving average intervals
+ --max_transfer_intervals NUM Long moving average intervals
+ --max_transfer_variance NUM Max difference between moving averages
+ --interval_millis TIME Interval size in milliseconds
+ --ping_runtime TIME Ping runtime in milliseconds
+ --ping_timeout TIME Ping timeout in milliseconds
)USAGE";
} // namespace
@@ -122,17 +154,25 @@
options->verbose = false;
options->global_host = http::Url(kDefaultHost);
options->global = false;
+ options->user_agent = "";
+ options->progress_millis = 0;
options->disable_dns_cache = false;
options->max_connections = 0;
+ options->exponential_moving_average = false;
+
options->num_downloads = 0;
options->download_size = 0;
options->num_uploads = 0;
options->upload_size = 0;
- options->progress_millis = 0;
- options->min_transfer_time = 0;
- options->max_transfer_time = 0;
+ options->min_transfer_runtime = 0;
+ options->max_transfer_runtime = 0;
+ options->min_transfer_intervals = 0;
+ options->max_transfer_intervals = 0;
+ options->max_transfer_variance = 0.0;
+ options->interval_millis = 0;
options->ping_runtime = 0;
options->ping_timeout = 0;
+
options->hosts.clear();
if (!options->global_host.ok()) {
@@ -221,6 +261,27 @@
case 'v':
options->verbose = true;
break;
+ case kOptDisableDnsCache:
+ options->disable_dns_cache = true;
+ break;
+ case kOptMaxConnections: {
+ long max_connections;
+ char *endptr;
+ if (!ParseLong(optarg, &endptr, &max_connections)) {
+ std::cerr << "Could not parse max connections '" << optarg << "'\n";
+ return false;
+ }
+ if (max_connections < 0) {
+ std::cerr << "Max connections must be nonnegative, got "
+ << optarg << "'\n";
+ return false;
+ }
+ options->max_connections = static_cast<int>(max_connections);
+ break;
+ }
+ case kOptExponentialMovingAverage:
+ options->exponential_moving_average = true;
+ break;
case kOptMinTransferTime: {
long transfer_time;
char *endptr;
@@ -230,11 +291,11 @@
return false;
}
if (transfer_time < 0) {
- std::cerr << "Minimum transfer time must be nonnegative, got "
+ std::cerr << "Minimum transfer runtime must be nonnegative, got "
<< optarg << "'\n";
return false;
}
- options->min_transfer_time = static_cast<int>(transfer_time);
+ options->min_transfer_runtime = static_cast<int>(transfer_time);
break;
}
case kOptMaxTransferTime: {
@@ -246,11 +307,72 @@
return false;
}
if (transfer_time < 0) {
- std::cerr << "Maximum transfer must be nonnegative, got "
+ std::cerr << "Maximum transfer runtime must be nonnegative, got "
<< optarg << "'\n";
return false;
}
- options->max_transfer_time = static_cast<int>(transfer_time);
+ options->max_transfer_runtime = static_cast<int>(transfer_time);
+ break;
+ }
+ case kOptMinTransferIntervals: {
+ long intervals;
+ char *endptr;
+ if (!ParseLong(optarg, &endptr, &intervals)) {
+ std::cerr << "Could not parse minimum transfer intervals '"
+ << optarg << "'\n";
+ return false;
+ }
+ if (intervals < 0) {
+ std::cerr << "Minimum transfer intervals must be nonnegative, got "
+ << optarg << "'\n";
+ return false;
+ }
+ options->min_transfer_intervals = static_cast<int>(intervals);
+ break;
+ }
+ case kOptMaxTransferIntervals: {
+ long intervals;
+ char *endptr;
+ if (!ParseLong(optarg, &endptr, &intervals)) {
+ std::cerr << "Could not parse maximum transfer intervals '"
+ << optarg << "'\n";
+ return false;
+ }
+ if (intervals < 0) {
+ std::cerr << "Maximum transfer intervals must be nonnegative, got "
+ << optarg << "'\n";
+ return false;
+ }
+ options->max_transfer_intervals = static_cast<int>(intervals);
+ break;
+ }
+ case kOptMaxTransferVariance: {
+ double variance;
+ char *endptr;
+ if (!ParseDouble(optarg, &endptr, &variance)) {
+ std::cerr << "Could not parse variance '" << optarg << "'\n";
+ return false;
+ }
+ if (variance < 0) {
+ std::cerr << "Variances must be nonnegative, got " << optarg << "'\n";
+ return false;
+ }
+ options->max_transfer_variance = variance;
+ break;
+ }
+ case kOptIntervalMillis: {
+ long interval_millis;
+ char *endptr;
+ if (!ParseLong(optarg, &endptr, &interval_millis)) {
+ std::cerr << "Could not parse interval time '" << optarg << "'\n";
+ return false;
+ }
+ if (interval_millis < 0) {
+ std::cerr << "Interval time must be nonnegative, got "
+ << optarg << "'\n";
+ return false;
+ }
+ options->interval_millis = static_cast<int>(interval_millis);
break;
}
case kOptPingRuntime: {
@@ -283,24 +405,6 @@
options->ping_timeout = static_cast<int>(ping_timeout);
break;
}
- case kOptDisableDnsCache:
- options->disable_dns_cache = true;
- break;
- case kOptMaxConnections: {
- long max_connections;
- char *endptr;
- if (!ParseLong(optarg, &endptr, &max_connections)) {
- std::cerr << "Could not parse max connections '" << optarg << "'\n";
- return false;
- }
- if (max_connections < 0) {
- std::cerr << "Max connections must be nonnegative, got "
- << optarg << "'\n";
- return false;
- }
- options->max_connections = static_cast<int>(max_connections);
- break;
- }
default:
return false;
}
@@ -338,18 +442,24 @@
<< "Global host: " << options.global_host.url() << "\n"
<< "Global: " << (options.global ? "true" : "false") << "\n"
<< "User agent: " << options.user_agent << "\n"
+ << "Progress interval: " << options.progress_millis << " ms\n"
<< "Disable DNS cache: "
<< (options.disable_dns_cache ? "true" : "false") << "\n"
<< "Max connections: " << options.max_connections << "\n"
+ << "Exponential moving average: "
+ << (options.exponential_moving_average ? "true" : "false") << "\n"
<< "Number of downloads: " << options.num_downloads << "\n"
<< "Download size: " << options.download_size << " bytes\n"
<< "Number of uploads: " << options.num_uploads << "\n"
<< "Upload size: " << options.upload_size << " bytes\n"
- << "Min transfer time: " << options.min_transfer_time << " ms\n"
- << "Max transfer time: " << options.max_transfer_time << " ms\n"
+ << "Min transfer runtime: " << options.min_transfer_runtime << " ms\n"
+ << "Max transfer runtime: " << options.max_transfer_runtime << " ms\n"
+ << "Min transfer intervals: " << options.min_transfer_intervals << "\n"
+ << "Max transfer intervals: " << options.max_transfer_intervals << "\n"
+ << "Max transfer variance: " << options.max_transfer_variance << "\n"
+ << "Interval size: " << options.interval_millis << " ms\n"
<< "Ping runtime: " << options.ping_runtime << " ms\n"
<< "Ping timeout: " << options.ping_timeout << " ms\n"
- << "Progress interval: " << options.progress_millis << " ms\n"
<< "Hosts:\n";
for (const http::Url &host : options.hosts) {
out << " " << host.url() << "\n";
diff --git a/speedtest/options.h b/speedtest/options.h
index 0413798..9028f70 100644
--- a/speedtest/options.h
+++ b/speedtest/options.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,24 +27,29 @@
extern const char* kDefaultHost;
struct Options {
- bool verbose;
- bool usage;
+ bool usage = false;
+ bool verbose = false;
http::Url global_host;
- bool global;
+ bool global = false;
std::string user_agent;
- bool disable_dns_cache;
- int max_connections;
+ bool disable_dns_cache = false;
+ int max_connections = 0;
+ int progress_millis = 0;
+ bool exponential_moving_average = false;
// A value of 0 means use the speedtest config parameters
- int num_downloads;
- long download_size;
- int num_uploads;
- long upload_size;
- int progress_millis;
- int min_transfer_time;
- int max_transfer_time;
- int ping_runtime;
- int ping_timeout;
+ int num_downloads = 0;
+ long download_size = 0;
+ int num_uploads = 0;
+ long upload_size = 0;
+ int min_transfer_runtime = 0;
+ int max_transfer_runtime = 0;
+ int min_transfer_intervals = 0;
+ int max_transfer_intervals = 0;
+ double max_transfer_variance = 0.0;
+ int interval_millis = 0;
+ int ping_runtime = 0;
+ int ping_timeout = 0;
std::vector<http::Url> hosts;
};
diff --git a/speedtest/options_test.cc b/speedtest/options_test.cc
index 113f730..601984a 100644
--- a/speedtest/options_test.cc
+++ b/speedtest/options_test.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -83,19 +83,25 @@
TEST(OptionsTest, Empty_ValidDefault) {
Options options;
TestValidOptions({}, &options);
+ EXPECT_FALSE(options.usage);
+ EXPECT_FALSE(options.verbose);
EXPECT_TRUE(options.global);
EXPECT_EQ(http::Url("any.speed.gfsvc.com"), options.global_host);
- EXPECT_FALSE(options.verbose);
- EXPECT_FALSE(options.usage);
EXPECT_FALSE(options.disable_dns_cache);
EXPECT_EQ(0, options.max_connections);
+ EXPECT_EQ(0, options.progress_millis);
+ EXPECT_FALSE(options.exponential_moving_average);
+
EXPECT_EQ(0, options.num_downloads);
EXPECT_EQ(0, options.download_size);
EXPECT_EQ(0, options.num_uploads);
EXPECT_EQ(0, options.upload_size);
- EXPECT_EQ(0, options.progress_millis);
- EXPECT_EQ(0, options.min_transfer_time);
- EXPECT_EQ(0, options.max_transfer_time);
+ EXPECT_EQ(0, options.min_transfer_runtime);
+ EXPECT_EQ(0, options.max_transfer_runtime);
+ EXPECT_EQ(0, options.min_transfer_intervals);
+ EXPECT_EQ(0, options.max_transfer_intervals);
+ EXPECT_EQ(0, options.max_transfer_variance);
+ EXPECT_EQ(0, options.interval_millis);
EXPECT_EQ(0, options.ping_runtime);
EXPECT_EQ(0, options.ping_timeout);
EXPECT_THAT(options.hosts, testing::IsEmpty());
@@ -122,7 +128,8 @@
TEST(OptionsTest, ShortOptions_Valid) {
Options options;
- TestValidOptions({"-s", "5122",
+ TestValidOptions({"-v",
+ "-s", "5122",
"-t", "7653",
"-d", "20",
"-u", "15",
@@ -132,17 +139,28 @@
"foo.speed.googlefiber.net",
"bar.speed.googlefiber.net"},
&options);
- EXPECT_EQ(5122, options.download_size);
- EXPECT_EQ(7653, options.upload_size);
+ EXPECT_TRUE(options.verbose);
EXPECT_EQ(20, options.num_downloads);
+ EXPECT_EQ(5122, options.download_size);
EXPECT_EQ(15, options.num_uploads);
+ EXPECT_EQ(7653, options.upload_size);
EXPECT_EQ(500, options.progress_millis);
+ EXPECT_FALSE(options.global);
EXPECT_EQ(http::Url("speed.gfsvc.com"), options.global_host);
EXPECT_EQ("CrOS", options.user_agent);
- EXPECT_EQ(0, options.min_transfer_time);
- EXPECT_EQ(0, options.max_transfer_time);
+
+ EXPECT_EQ(0, options.max_connections);
+ EXPECT_FALSE(options.disable_dns_cache);
+ EXPECT_FALSE(options.exponential_moving_average);
+ EXPECT_EQ(0, options.min_transfer_runtime);
+ EXPECT_EQ(0, options.max_transfer_runtime);
+ EXPECT_EQ(0, options.min_transfer_intervals);
+ EXPECT_EQ(0, options.max_transfer_intervals);
+ EXPECT_EQ(0, options.max_transfer_variance);
+ EXPECT_EQ(0, options.interval_millis);
EXPECT_EQ(0, options.ping_runtime);
EXPECT_EQ(0, options.ping_timeout);
+
EXPECT_THAT(options.hosts, testing::UnorderedElementsAre(
http::Url("foo.speed.googlefiber.net"),
http::Url("bar.speed.googlefiber.net")));
@@ -150,33 +168,47 @@
TEST(OptionsTest, LongOptions_Valid) {
Options options;
- TestValidOptions({"--download_size", "5122",
- "--upload_size", "7653",
- "--progress", "1000",
- "--num_uploads", "12",
- "--num_downloads", "16",
+ TestValidOptions({"--verbose",
"--global_host", "speed.gfsvc.com",
"--user_agent", "CrOS",
+ "--progress_millis", "1000",
"--disable_dns_cache",
"--max_connections", "23",
- "--min_transfer_time", "7500",
- "--max_transfer_time", "13500",
+ "--exponential_moving_average",
+ "--num_downloads", "16",
+ "--download_size", "5122",
+ "--num_uploads", "12",
+ "--upload_size", "7653",
+ "--min_transfer_runtime", "7500",
+ "--max_transfer_runtime", "13500",
+ "--min_transfer_intervals", "13",
+ "--max_transfer_intervals", "22",
+ "--max_transfer_variance", "0.12",
+ "--interval_millis", "250",
"--ping_runtime", "2500",
"--ping_timeout", "300",
"foo.speed.googlefiber.net",
"bar.speed.googlefiber.net"},
&options);
- EXPECT_TRUE(options.disable_dns_cache);
- EXPECT_EQ(5122, options.download_size);
- EXPECT_EQ(7653, options.upload_size);
- EXPECT_EQ(16, options.num_downloads);
- EXPECT_EQ(23, options.max_connections);
- EXPECT_EQ(12, options.num_uploads);
- EXPECT_EQ(1000, options.progress_millis);
+ EXPECT_TRUE(options.verbose);
+ EXPECT_FALSE(options.global);
EXPECT_EQ(http::Url("speed.gfsvc.com"), options.global_host);
EXPECT_EQ("CrOS", options.user_agent);
- EXPECT_EQ(7500, options.min_transfer_time);
- EXPECT_EQ(13500, options.max_transfer_time);
+ EXPECT_EQ(1000, options.progress_millis);
+ EXPECT_TRUE(options.disable_dns_cache);
+ EXPECT_EQ(23, options.max_connections);
+ EXPECT_TRUE(options.exponential_moving_average);
+ EXPECT_EQ(16, options.num_downloads);
+ EXPECT_EQ(5122, options.download_size);
+ EXPECT_EQ(12, options.num_uploads);
+ EXPECT_EQ(7653, options.upload_size);
+ EXPECT_EQ("CrOS", options.user_agent);
+ EXPECT_EQ(7500, options.min_transfer_runtime);
+ EXPECT_EQ(13500, options.max_transfer_runtime);
+ EXPECT_EQ(13, options.min_transfer_intervals);
+ EXPECT_EQ(22, options.max_transfer_intervals);
+ EXPECT_EQ(0.12, options.max_transfer_variance);
+ EXPECT_EQ(250, options.interval_millis);
EXPECT_EQ(2500, options.ping_runtime);
EXPECT_EQ(300, options.ping_timeout);
EXPECT_THAT(options.hosts, testing::UnorderedElementsAre(
diff --git a/speedtest/ping_test.cc b/speedtest/ping_task.cc
similarity index 84%
rename from speedtest/ping_test.cc
rename to speedtest/ping_task.cc
index 999b53d..7a1c7be 100644
--- a/speedtest/ping_test.cc
+++ b/speedtest/ping_task.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,24 +14,23 @@
* limitations under the License.
*/
-#include "ping_test.h"
+#include "ping_task.h"
#include <algorithm>
#include <cassert>
#include <iomanip>
#include <iostream>
-#include "generic_test.h"
#include "utils.h"
namespace speedtest {
-PingTest::PingTest(const Options &options)
- : GenericTest(options),
+PingTask::PingTask(const Options &options)
+ : HttpTask(options),
options_(options) {
assert(options_.num_pings > 0);
}
-void PingTest::RunInternal() {
+void PingTask::RunInternal() {
ResetCounters();
success_ = false;
threads_.clear();
@@ -42,7 +41,7 @@
}
}
-void PingTest::StopInternal() {
+void PingTask::StopInternal() {
std::for_each(threads_.begin(), threads_.end(), [](std::thread &t) {
t.join();
});
@@ -76,17 +75,16 @@
if (!min_stats) {
// no servers respondeded
success_ = false;
- return;
} else {
fastest_ = *min_stats;
success_ = true;
}
}
-void PingTest::RunPing(size_t index) {
- GenericTest::RequestPtr ping = options_.request_factory(index);
+void PingTask::RunPing(size_t index) {
+ http::Request::Ptr ping = options_.request_factory(index);
stats_[index].url = ping->url();
- while (GetStatus() == TestStatus::RUNNING) {
+ while (GetStatus() == TaskStatus::RUNNING) {
long req_start = SystemTimeMicros();
if (ping->Get() == CURLE_OK) {
long req_end = SystemTimeMicros();
@@ -100,16 +98,16 @@
}
}
-bool PingTest::IsSucceeded() const {
+bool PingTask::IsSucceeded() const {
return success_;
}
-PingStats PingTest::GetFastest() const {
+PingStats PingTask::GetFastest() const {
std::lock_guard<std::mutex> lock(mutex_);
return fastest_;
}
-void PingTest::ResetCounters() {
+void PingTask::ResetCounters() {
stats_.clear();
stats_.resize(options_.num_pings);
}
diff --git a/speedtest/ping_test.h b/speedtest/ping_task.h
similarity index 77%
rename from speedtest/ping_test.h
rename to speedtest/ping_task.h
index 558c799..b2923a8 100644
--- a/speedtest/ping_test.h
+++ b/speedtest/ping_task.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef PING_TEST_H
-#define PING_TEST_H
+#ifndef SPEEDTEST_PING_TASK_H
+#define SPEEDTEST_PING_TASK_H
#include <atomic>
#include <functional>
@@ -24,7 +24,7 @@
#include <mutex>
#include <thread>
#include <vector>
-#include "generic_test.h"
+#include "http_task.h"
#include "request.h"
#include "url.h"
@@ -37,15 +37,14 @@
http::Url url;
};
-class PingTest : public GenericTest {
+class PingTask : public HttpTask {
public:
- struct Options : GenericTest::Options {
+ struct Options : HttpTask::Options {
int timeout = 0;
int num_pings = 0;
- std::function<RequestPtr(int)> request_factory;
};
- explicit PingTest(const Options &options);
+ explicit PingTask(const Options &options);
bool IsSucceeded() const;
@@ -69,10 +68,10 @@
PingStats fastest_;
// disallowed
- PingTest(const PingTest &) = delete;
- void operator=(const PingTest &) = delete;
+ PingTask(const PingTask &) = delete;
+ void operator=(const PingTask &) = delete;
};
} // namespace speedtest
-#endif // PING_TEST_H
+#endif // SPEEDTEST_PING_TASK_H
diff --git a/speedtest/request.cc b/speedtest/request.cc
index 1542053..ef46d2d 100644
--- a/speedtest/request.cc
+++ b/speedtest/request.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/speedtest/request.h b/speedtest/request.h
index f3344d2..8588e29 100644
--- a/speedtest/request.h
+++ b/speedtest/request.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,6 +42,7 @@
curl_off_t,
curl_off_t,
curl_off_t)>;
+ using Ptr = std::unique_ptr<Request>;
Request(std::shared_ptr<CURL> handle, const Url &url);
virtual ~Request();
diff --git a/speedtest/request_test.cc b/speedtest/request_test.cc
index 4926419..ae7b74f 100644
--- a/speedtest/request_test.cc
+++ b/speedtest/request_test.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,11 +30,11 @@
void SetUp() override {
env = CurlEnv::NewCurlEnv({});
+ request = env->NewRequest(http::Url("http://example.com/foo"));
}
void VerifyQueryString(const char *expected,
Request::QueryStringParams params) {
- request = env->NewRequest(Url());
request->params() = params;
request->UpdateUrl();
EXPECT_EQ(expected, request->url().query_string());
@@ -43,7 +43,7 @@
void VerifyUrl(const char *expected,
const char *url,
Request::QueryStringParams params) {
- request = env->NewRequest(Url(url));
+ request->set_url(Url(url));
request->params() = params;
request->UpdateUrl();
EXPECT_EQ(expected, request->url().url());
@@ -91,9 +91,9 @@
}
TEST_F(RequestTest, Url_OneParamTwoValues_Ok) {
- VerifyUrl("http://example.com/?abc=def&abc=ghi",
+ VerifyUrl("http://example.com/?abc=def&abc=def",
"http://example.com",
- {{"abc", "def"}, {"abc", "ghi"}});
+ {{"abc", "def"}, {"abc", "def"}});
}
TEST_F(RequestTest, Url_EscapeParam_Ok) {
diff --git a/speedtest/speedtest.cc b/speedtest/speedtest.cc
index 1cd9286..ed1263d 100644
--- a/speedtest/speedtest.cc
+++ b/speedtest/speedtest.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,7 +26,8 @@
#include <streambuf>
#include "errors.h"
-#include "runner.h"
+#include "timed_runner.h"
+#include "transfer_runner.h"
#include "utils.h"
namespace speedtest {
@@ -100,9 +101,13 @@
std::string version = LoadFile(kFileVersion);
Trim(&serial);
Trim(&version);
- user_agent_ = std::string("CPE/") +
- (version.empty() ? "unknown version" : version) + "/" +
- (serial.empty() ? "unknown serial" : serial);
+ user_agent_ = "CPE";
+ if (!version.empty()) {
+ user_agent_ += "/" + version;
+ if (!serial.empty()) {
+ user_agent_ += "/" + serial;
+ }
+ }
} else {
user_agent_ = options_.user_agent;
return;
@@ -155,7 +160,7 @@
return;
}
- PingTest::Options options;
+ PingTask::Options options;
options.verbose = options_.verbose;
options.timeout = PingTimeout();
std::vector<http::Url> hosts;
@@ -171,14 +176,14 @@
std::cout << " " << host.url() << "\n";
}
}
- options.request_factory = [&](int id) -> GenericTest::RequestPtr{
+ options.request_factory = [&](int id) -> http::Request::Ptr{
return MakeRequest(hosts[id]);
};
- PingTest find_nearest(options);
+ PingTask find_nearest(options);
if (options_.verbose) {
std::cout << "Starting to find nearest server\n";
}
- TimedRun(&find_nearest, 1500);
+ RunTimed(&find_nearest, 1500);
find_nearest.WaitForEnd();
if (find_nearest.IsSucceeded()) {
PingStats fastest = find_nearest.GetFastest();
@@ -198,7 +203,7 @@
if (options_.verbose) {
std::cout << "Loading config from " << config_url.url() << "\n";
}
- GenericTest::RequestPtr request = MakeRequest(config_url);
+ http::Request::Ptr request = MakeRequest(config_url);
request->set_url(config_url);
std::string json;
request->Get([&](void *data, size_t size){
@@ -208,20 +213,20 @@
}
void Speedtest::RunPingTest() {
- PingTest::Options options;
+ PingTask::Options options;
options.verbose = options_.verbose;
options.timeout = PingTimeout();
options.num_pings = 1;
http::Url ping_url(*server_url_);
ping_url.set_path("/ping");
- options.request_factory = [&](int id) -> GenericTest::RequestPtr{
+ options.request_factory = [&](int id) -> http::Request::Ptr{
return MakeRequest(ping_url);
};
- ping_test_.reset(new PingTest(options));
- TimedRun(ping_test_.get(), PingRunTime());
- ping_test_->WaitForEnd();
- PingStats fastest = ping_test_->GetFastest();
- if (ping_test_->IsSucceeded()) {
+ std::unique_ptr<PingTask> ping(new PingTask(options));
+ RunTimed(ping.get(), PingRunTime());
+ ping->WaitForEnd();
+ PingStats fastest = ping->GetFastest();
+ if (ping->IsSucceeded()) {
long micros = fastest.min_micros;
std::cout << "Ping time: " << round(micros / 1000.0d, 3) << " ms\n";
} else {
@@ -232,52 +237,95 @@
void Speedtest::RunDownloadTest() {
if (options_.verbose) {
- std::cout << "Starting download test at " << config_.location_name
+ std::cout << "Starting download test to " << config_.location_name
<< " (" << server_url_->url() << ")\n";
}
- DownloadTest::Options options;
- options.verbose = options_.verbose;
- options.num_transfers = NumDownloads();
- options.download_size = DownloadSize();
- options.request_factory = [this](int id) -> GenericTest::RequestPtr{
+ DownloadTask::Options download_options;
+ download_options.verbose = options_.verbose;
+ download_options.num_transfers = NumDownloads();
+ download_options.download_size = DownloadSize();
+ download_options.request_factory = [this](int id) -> http::Request::Ptr{
return MakeTransferRequest(id, "/download");
};
- download_test_.reset(new DownloadTest(options));
- TimedRun(download_test_.get(), 5000);
- download_test_->WaitForEnd();
- long bytes = download_test_->bytes_transferred();
- long micros = download_test_->GetRunningTime();
+ std::unique_ptr<DownloadTask> download(new DownloadTask(download_options));
+ TransferRunner::Options runner_options;
+ runner_options.verbose = options_.verbose;
+ runner_options.task = download.get();
+ runner_options.min_runtime = MinTransferRuntime();
+ runner_options.max_runtime = MaxTransferRuntime();
+ runner_options.min_intervals = MinTransferIntervals();
+ runner_options.max_intervals = MaxTransferIntervals();
+ runner_options.max_variance = MaxTransferVariance();
+ runner_options.interval_millis = IntervalMillis();
+ if (options_.progress_millis > 0) {
+ runner_options.progress_millis = options_.progress_millis;
+ runner_options.progress_fn = [](Interval interval) {
+ double speed_variance = variance(interval.short_megabits,
+ interval.long_megabits);
+ std::cout << "[+" << round(interval.running_time / 1000.0, 0) << " ms] "
+ << "Download speed: " << round(interval.short_megabits, 2)
+ << " - " << round(interval.long_megabits, 2)
+ << " Mbps (" << interval.bytes << " bytes, variance "
+ << round(speed_variance, 4) << ")\n";
+ };
+ }
+ TransferRunner runner(runner_options);
+ runner.Run();
+ runner.WaitForEnd();
if (options_.verbose) {
- std::cout << "Downloaded " << bytes << " bytes in "
- << round(micros / 1000.0d, 2) << " ms\n";
+ long running_time = download->GetRunningTimeMicros();
+ std::cout << "Downloaded " << download->bytes_transferred()
+ << " bytes in " << round(running_time / 1000.0, 0) << " ms\n";
}
std::cout << "Download speed: "
- << round(ToMegabits(bytes, micros), 3) << " Mbps\n";
+ << round(runner.GetSpeedInMegabits(), 2) << " Mbps\n";
}
void Speedtest::RunUploadTest() {
if (options_.verbose) {
- std::cout << "Starting upload test at " << config_.location_name
+ std::cout << "Starting upload test to " << config_.location_name
<< " (" << server_url_->url() << ")\n";
}
- UploadTest::Options options;
- options.verbose = options_.verbose;
- options.num_transfers = NumUploads();
- options.payload = MakeRandomData(UploadSize());
- options.request_factory = [this](int id) -> GenericTest::RequestPtr{
+ UploadTask::Options upload_options;
+ upload_options.verbose = options_.verbose;
+ upload_options.num_transfers = NumUploads();
+ upload_options.payload = MakeRandomData(UploadSize());
+ upload_options.request_factory = [this](int id) -> http::Request::Ptr{
return MakeTransferRequest(id, "/upload");
};
- upload_test_.reset(new UploadTest(options));
- TimedRun(upload_test_.get(), 5000);
- upload_test_->WaitForEnd();
- long bytes = upload_test_->bytes_transferred();
- long micros = upload_test_->GetRunningTime();
+
+ std::unique_ptr<UploadTask> upload(new UploadTask(upload_options));
+ TransferRunner::Options runner_options;
+ runner_options.verbose = options_.verbose;
+ runner_options.task = upload.get();
+ runner_options.min_runtime = MinTransferRuntime();
+ runner_options.max_runtime = MaxTransferRuntime();
+ runner_options.min_intervals = MinTransferIntervals();
+ runner_options.max_intervals = MaxTransferIntervals();
+ runner_options.max_variance = MaxTransferVariance();
+ runner_options.interval_millis = IntervalMillis();
+ if (options_.progress_millis > 0) {
+ runner_options.progress_millis = options_.progress_millis;
+ runner_options.progress_fn = [](Interval interval) {
+ double speed_variance = variance(interval.short_megabits,
+ interval.long_megabits);
+ std::cout << "[+" << round(interval.running_time / 1000.0, 0) << " ms] "
+ << "Upload speed: " << round(interval.short_megabits, 2)
+ << " - " << round(interval.long_megabits, 2)
+ << " Mbps (" << interval.bytes << " bytes, variance "
+ << round(speed_variance, 4) << ")\n";
+ };
+ }
+ TransferRunner runner(runner_options);
+ runner.Run();
+ runner.WaitForEnd();
if (options_.verbose) {
- std::cout << "Uploaded " << bytes << " bytes in "
- << round(micros / 1000.0d, 2) << " ms\n";
+ long running_time = upload->GetRunningTimeMicros();
+ std::cout << "Uploaded " << upload->bytes_transferred()
+ << " bytes in " << round(running_time / 1000.0, 0) << " ms\n";
}
std::cout << "Upload speed: "
- << round(ToMegabits(bytes, micros), 3) << " Mbps\n";
+ << round(runner.GetSpeedInMegabits(), 2) << " Mbps\n";
}
int Speedtest::NumDownloads() const {
@@ -316,22 +364,58 @@
: config_.ping_timeout;
}
-GenericTest::RequestPtr Speedtest::MakeRequest(const http::Url &url) {
- GenericTest::RequestPtr request = env_->NewRequest(url);
+int Speedtest::MinTransferRuntime() const {
+ return options_.min_transfer_runtime
+ ? options_.min_transfer_runtime
+ : config_.min_transfer_runtime;
+}
+
+int Speedtest::MaxTransferRuntime() const {
+ return options_.max_transfer_runtime
+ ? options_.max_transfer_runtime
+ : config_.max_transfer_runtime;
+}
+
+int Speedtest::MinTransferIntervals() const {
+ return options_.min_transfer_intervals
+ ? options_.min_transfer_intervals
+ : config_.min_transfer_intervals;
+}
+
+int Speedtest::MaxTransferIntervals() const {
+ return options_.max_transfer_intervals
+ ? options_.max_transfer_intervals
+ : config_.max_transfer_intervals;
+}
+
+double Speedtest::MaxTransferVariance() const {
+ return options_.max_transfer_variance
+ ? options_.max_transfer_variance
+ : config_.max_transfer_variance;
+}
+
+int Speedtest::IntervalMillis() const {
+ return options_.interval_millis
+ ? options_.interval_millis
+ : config_.interval_millis;
+}
+
+http::Request::Ptr Speedtest::MakeRequest(const http::Url &url) {
+ http::Request::Ptr request = env_->NewRequest(url);
if (!user_agent_.empty()) {
request->set_user_agent(user_agent_);
}
return std::move(request);
}
-GenericTest::RequestPtr Speedtest::MakeBaseRequest(
+http::Request::Ptr Speedtest::MakeBaseRequest(
int id, const std::string &path) {
http::Url url(*server_url_);
url.set_path(path);
return MakeRequest(url);
}
-GenericTest::RequestPtr Speedtest::MakeTransferRequest(
+http::Request::Ptr Speedtest::MakeTransferRequest(
int id, const std::string &path) {
http::Url url(*server_url_);
int port_start = config_.transfer_port_start;
diff --git a/speedtest/speedtest.h b/speedtest/speedtest.h
index ea509f7..fb32355 100644
--- a/speedtest/speedtest.h
+++ b/speedtest/speedtest.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,11 +23,10 @@
#include "config.h"
#include "curl_env.h"
-#include "download_test.h"
-#include "generic_test.h"
+#include "download_task.h"
#include "options.h"
-#include "ping_test.h"
-#include "upload_test.h"
+#include "ping_task.h"
+#include "upload_task.h"
#include "url.h"
#include "request.h"
@@ -55,10 +54,16 @@
int UploadSize() const;
int PingTimeout() const;
int PingRunTime() const;
+ int MinTransferRuntime() const;
+ int MaxTransferRuntime() const;
+ int MinTransferIntervals() const;
+ int MaxTransferIntervals() const;
+ double MaxTransferVariance() const;
+ int IntervalMillis() const;
- GenericTest::RequestPtr MakeRequest(const http::Url &url);
- GenericTest::RequestPtr MakeBaseRequest(int id, const std::string &path);
- GenericTest::RequestPtr MakeTransferRequest(int id, const std::string &path);
+ http::Request::Ptr MakeRequest(const http::Url &url);
+ http::Request::Ptr MakeBaseRequest(int id, const std::string &path);
+ http::Request::Ptr MakeTransferRequest(int id, const std::string &path);
std::shared_ptr <http::CurlEnv> env_;
Options options_;
@@ -67,9 +72,6 @@
std::vector<http::Url> servers_;
std::unique_ptr<http::Url> server_url_;
std::unique_ptr<std::string> send_data_;
- std::unique_ptr<PingTest> ping_test_;
- std::unique_ptr<DownloadTest> download_test_;
- std::unique_ptr<UploadTest> upload_test_;
// disable
Speedtest(const Speedtest &) = delete;
diff --git a/speedtest/speedtest_main.cc b/speedtest/speedtest_main.cc
index 8a9c2c9..d756c4b 100644
--- a/speedtest/speedtest_main.cc
+++ b/speedtest/speedtest_main.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/speedtest/task.cc b/speedtest/task.cc
new file mode 100644
index 0000000..84d12c9
--- /dev/null
+++ b/speedtest/task.cc
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2016 Google Inc. All rights reserved.
+ *
+ * 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 "task.h"
+
+#include <algorithm>
+#include <cassert>
+#include <iostream>
+#include <thread>
+#include "utils.h"
+
+namespace speedtest {
+
+const char *AsString(TaskStatus status) {
+ switch (status) {
+ case TaskStatus::NOT_STARTED: return "NOT_STARTED";
+ case TaskStatus::RUNNING: return "RUNNING";
+ case TaskStatus::STOPPING: return "STOPPING";
+ case TaskStatus::STOPPED: return "STOPPED";
+ }
+ std::exit(1);
+}
+
+Task::Task(const Options &options)
+ : status_(TaskStatus::NOT_STARTED) {
+ assert(options.request_factory);
+}
+
+Task::~Task() {
+ Stop();
+ if (runner_.joinable()) {
+ runner_.join();
+ }
+ if (stopper_.joinable()) {
+ stopper_.join();
+ }
+}
+
+void Task::Run() {
+ runner_ = std::thread([=]{
+ {
+ std::lock_guard <std::mutex> lock(mutex_);
+ if (status_ != TaskStatus::NOT_STARTED &&
+ status_ != TaskStatus::STOPPED) {
+ return;
+ }
+ UpdateStatusLocked(TaskStatus::RUNNING);
+ start_time_ = SystemTimeMicros();
+ }
+ RunInternal();
+ });
+ stopper_ = std::thread([=]{
+ WaitFor(TaskStatus::STOPPING);
+ StopInternal();
+ std::lock_guard <std::mutex> lock(mutex_);
+ UpdateStatusLocked(TaskStatus::STOPPED);
+ end_time_ = SystemTimeMicros();
+ });
+}
+
+void Task::Stop() {
+ std::lock_guard <std::mutex> lock(mutex_);
+ if (status_ != TaskStatus::RUNNING) {
+ return;
+ }
+ UpdateStatusLocked(TaskStatus::STOPPING);
+}
+
+TaskStatus Task::GetStatus() const {
+ std::lock_guard <std::mutex> lock(mutex_);
+ return status_;
+}
+
+long Task::GetStartTime() const {
+ std::lock_guard <std::mutex> lock(mutex_);
+ return start_time_;
+}
+
+long Task::GetEndTime() const {
+ std::lock_guard <std::mutex> lock(mutex_);
+ return end_time_;
+}
+
+long Task::GetRunningTimeMicros() const {
+ std::lock_guard <std::mutex> lock(mutex_);
+ switch (status_) {
+ case TaskStatus::NOT_STARTED:
+ break;
+ case TaskStatus::RUNNING:
+ case TaskStatus::STOPPING:
+ return SystemTimeMicros() - start_time_;
+ case TaskStatus::STOPPED:
+ return end_time_ - start_time_;
+ }
+ return 0;
+}
+
+void Task::WaitForEnd() {
+ WaitFor(TaskStatus::STOPPED);
+}
+
+void Task::UpdateStatusLocked(TaskStatus status) {
+ status_ = status;
+ status_cond_.notify_all();
+}
+
+void Task::WaitFor(TaskStatus status) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ status_cond_.wait(lock, [=]{
+ return status_ == status;
+ });
+}
+
+} // namespace speedtest
diff --git a/speedtest/generic_test.h b/speedtest/task.h
similarity index 60%
rename from speedtest/generic_test.h
rename to speedtest/task.h
index cf0d37c..429b078 100644
--- a/speedtest/generic_test.h
+++ b/speedtest/task.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,42 +14,42 @@
* limitations under the License.
*/
-#ifndef SPEEDTEST_GENERIC_TEST_H
-#define SPEEDTEST_GENERIC_TEST_H
+#ifndef SPEEDTEST_TASK_H
+#define SPEEDTEST_TASK_H
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
-#include "request.h"
+#include <thread>
namespace speedtest {
-enum class TestStatus {
+enum class TaskStatus {
NOT_STARTED,
RUNNING,
STOPPING,
STOPPED
};
-const char *AsString(TestStatus status);
+const char *AsString(TaskStatus status);
-class GenericTest {
+class Task {
public:
- using RequestPtr = std::unique_ptr<http::Request>;
-
struct Options {
bool verbose = false;
- std::function<RequestPtr(int)> request_factory;
};
- explicit GenericTest(const Options &options);
+ explicit Task(const Options &options);
+ virtual ~Task();
void Run();
void Stop();
- TestStatus GetStatus() const;
- long GetRunningTime() const;
+ TaskStatus GetStatus() const;
+ long GetStartTime() const;
+ long GetEndTime() const;
+ long GetRunningTimeMicros() const;
void WaitForEnd();
protected:
@@ -57,19 +57,24 @@
virtual void StopInternal() {}
private:
+ // Only call with mutex_
+ void UpdateStatusLocked(TaskStatus status);
+
+ void WaitFor(TaskStatus status);
+
mutable std::mutex mutex_;
- std::condition_variable condition_;
- TestStatus status_;
- bool running_ = false;
+ std::thread runner_;
+ std::thread stopper_;
+ std::condition_variable status_cond_;
+ TaskStatus status_;
long start_time_;
long end_time_;
// disallowed
- GenericTest(const GenericTest &) = delete;
-
- void operator=(const GenericTest &) = delete;
+ Task(const Task &) = delete;
+ void operator=(const Task &) = delete;
};
} // namespace speedtest
-#endif //SPEEDTEST_GENERIC_TEST_H
+#endif //SPEEDTEST_TASK_H
diff --git a/speedtest/runner.cc b/speedtest/timed_runner.cc
similarity index 82%
rename from speedtest/runner.cc
rename to speedtest/timed_runner.cc
index 0dae542..bf7c4cc 100644
--- a/speedtest/runner.cc
+++ b/speedtest/timed_runner.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,20 +14,20 @@
* limitations under the License.
*/
-#include "runner.h"
+#include "timed_runner.h"
#include <cassert>
#include <thread>
namespace speedtest {
-void TimedRun(GenericTest *test, long millis) {
- assert(test);
- test->Run();
+void RunTimed(Task *task, long millis) {
+ assert(task);
+ task->Run();
std::thread timer([=] {
std::this_thread::sleep_for(
std::chrono::milliseconds(millis));
- test->Stop();
+ task->Stop();
});
timer.join();
}
diff --git a/speedtest/runner.h b/speedtest/timed_runner.h
similarity index 83%
rename from speedtest/runner.h
rename to speedtest/timed_runner.h
index 4d677fc..02e673f 100644
--- a/speedtest/runner.h
+++ b/speedtest/timed_runner.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,11 +17,12 @@
#ifndef SPEEDTEST_RUNNER_H
#define SPEEDTEST_RUNNER_H
-#include "generic_test.h"
+#include "task.h"
namespace speedtest {
-void TimedRun(GenericTest *test, long millis);
+// Run a task for a set duration
+void RunTimed(Task *task, long millis);
} // namespace speedtest
diff --git a/speedtest/transfer_runner.cc b/speedtest/transfer_runner.cc
new file mode 100644
index 0000000..d37f087
--- /dev/null
+++ b/speedtest/transfer_runner.cc
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2016 Google Inc. All rights reserved.
+ *
+ * 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 "transfer_runner.h"
+
+#include <algorithm>
+#include <cassert>
+#include <chrono>
+#include <iostream>
+#include <thread>
+#include "transfer_task.h"
+#include "utils.h"
+
+namespace speedtest {
+namespace {
+
+const int kDefaultIntervalMillis = 200;
+
+} // namespace
+
+TransferRunner::TransferRunner(const Options &options)
+ : Task(options),
+ options_(options) {
+ if (options_.interval_millis <= 0) {
+ options_.interval_millis = kDefaultIntervalMillis;
+ }
+}
+
+void TransferRunner::RunInternal() {
+ threads_.clear();
+ intervals_.clear();
+
+ // sentinel value of all zeroes
+ intervals_.emplace_back();
+
+ // If progress updates are created add a thread to send updates
+ if (options_.progress_fn && options_.progress_millis > 0) {
+ if (options_.verbose) {
+ std::cout << "Progress updates every "
+ << options_.progress_millis << " ms\n";
+ }
+ threads_.emplace_back([&] {
+ std::this_thread::sleep_for(
+ std::chrono::milliseconds(options_.progress_millis));
+ while (GetStatus() == TaskStatus::RUNNING) {
+ Interval progress = GetLastInterval();
+ options_.progress_fn(progress);
+ std::this_thread::sleep_for(
+ std::chrono::milliseconds(options_.progress_millis));
+ }
+ Interval progress = GetLastInterval();
+ options_.progress_fn(progress);
+ });
+ } else if (options_.verbose) {
+ std::cout << "No progress updates\n";
+ }
+
+ // Updating thread
+ if (options_.verbose) {
+ std::cout << "Transfer runner updates every "
+ << options_.interval_millis << " ms\n";
+ }
+ threads_.emplace_back([&] {
+ std::this_thread::sleep_for(
+ std::chrono::milliseconds(options_.interval_millis));
+ while (GetStatus() == TaskStatus::RUNNING) {
+ const Interval &interval = AddInterval();
+ if (interval.running_time > options_.max_runtime * 1000) {
+ Stop();
+ return;
+ }
+ if (interval.running_time >= options_.min_runtime * 1000 &&
+ interval.long_megabits > 0 &&
+ interval.short_megabits > 0) {
+ double speed_variance = variance(interval.short_megabits,
+ interval.long_megabits);
+ if (speed_variance <= options_.max_variance) {
+ Stop();
+ return;
+ }
+ }
+ std::this_thread::sleep_for(
+ std::chrono::milliseconds(options_.interval_millis));
+ }
+ });
+
+ options_.task->Run();
+}
+
+void TransferRunner::StopInternal() {
+ options_.task->Stop();
+ options_.task->WaitForEnd();
+ std::for_each(threads_.begin(), threads_.end(), [](std::thread &t) {
+ t.join();
+ });
+ threads_.clear();
+}
+
+const Interval &TransferRunner::AddInterval() {
+ std::lock_guard <std::mutex> lock(mutex_);
+ intervals_.emplace_back();
+ Interval &interval = intervals_[intervals_.size() - 1];
+ interval.running_time = options_.task->GetRunningTimeMicros();
+ interval.bytes = options_.task->bytes_transferred();
+ if (options_.exponential_moving_average) {
+ interval.short_megabits = GetShortEma(options_.min_intervals);
+ interval.long_megabits = GetLongEma(options_.max_intervals);
+ } else {
+ interval.short_megabits = GetSimpleAverage(options_.min_intervals);
+ interval.long_megabits = GetSimpleAverage(options_.max_intervals);
+ }
+ speed_ = interval.long_megabits;
+ return intervals_.back();
+}
+
+Interval TransferRunner::GetLastInterval() const {
+ std::lock_guard <std::mutex> lock(mutex_);
+ return intervals_.back();
+}
+
+double TransferRunner::GetSpeedInMegabits() const {
+ std::lock_guard <std::mutex> lock(mutex_);
+ return speed_;
+}
+
+double TransferRunner::GetShortEma(int num_intervals) {
+ if (intervals_.empty() || num_intervals <= 0) {
+ return 0.0;
+ }
+ Interval last_interval = GetLastInterval();
+ double percent = 2.0d / (num_intervals + 1);
+ return GetSimpleAverage(1) * percent +
+ last_interval.short_megabits * (1 - percent);
+}
+
+double TransferRunner::GetLongEma(int num_intervals) {
+ if (intervals_.empty() || num_intervals <= 0) {
+ return 0.0;
+ }
+ Interval last_interval = GetLastInterval();
+ double percent = 2.0d / (num_intervals + 1);
+ return GetSimpleAverage(1) * percent +
+ last_interval.long_megabits * (1 - percent);
+}
+
+double TransferRunner::GetSimpleAverage(int num_intervals) {
+ if (intervals_.empty() || num_intervals <= 0) {
+ return 0.0;
+ }
+ int end_index = intervals_.size() - 1;
+ int start_index = std::max(0, end_index - num_intervals);
+ const Interval &end = intervals_[end_index];
+ const Interval &start = intervals_[start_index];
+ return ToMegabits(end.bytes - start.bytes,
+ end.running_time - start.running_time);
+}
+
+} // namespace
diff --git a/speedtest/transfer_runner.h b/speedtest/transfer_runner.h
new file mode 100644
index 0000000..793c8ec
--- /dev/null
+++ b/speedtest/transfer_runner.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2016 Google Inc. All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef SPEEDTEST_TRANSFER_RUNNER_H
+#define SPEEDTEST_TRANSFER_RUNNER_H
+
+#include <functional>
+#include <mutex>
+#include <thread>
+#include <vector>
+#include "task.h"
+#include "transfer_task.h"
+
+namespace speedtest {
+
+struct Interval {
+ long bytes = 0;
+ long running_time = 0;
+ double short_megabits = 0.0;
+ double long_megabits = 0.0;
+};
+
+// Run a variable length transfer test using two moving averages.
+// The test runs between min_runtime and max_runtime and otherwise
+// ends when the speed is "stable" meaning the two moving averages
+// are relatively close to one another.
+class TransferRunner : public Task {
+ public:
+ struct Options : public Task::Options {
+ TransferTask *task = nullptr;
+ int min_runtime = 0;
+ int max_runtime = 0;
+ int interval_millis = 0;
+ int progress_millis = 0;
+ int min_intervals = 0;
+ int max_intervals = 0;
+ double max_variance = 0.0;
+ bool exponential_moving_average = false;
+ std::function<void(Interval)> progress_fn;
+ };
+
+ explicit TransferRunner(const Options &options);
+
+ double GetSpeedInMegabits() const;
+ Interval GetLastInterval() const;
+
+ protected:
+ void RunInternal() override;
+ void StopInternal() override;
+
+ private:
+ const Interval &AddInterval();
+ double GetSimpleAverage(int num_intervals);
+ double GetShortEma(int num_intervals);
+ double GetLongEma(int num_intervals);
+
+ Options options_;
+
+ mutable std::mutex mutex_;
+ std::vector<Interval> intervals_;
+ std::vector<std::thread> threads_;
+ double speed_;
+
+ // disallowed
+ TransferRunner(const TransferRunner &) = delete;
+ void operator=(const TransferRunner &) = delete;
+};
+
+} // namespace
+
+#endif //SPEEDTEST_TRANSFER_RUNNER_H
diff --git a/speedtest/transfer_test.cc b/speedtest/transfer_task.cc
similarity index 72%
rename from speedtest/transfer_test.cc
rename to speedtest/transfer_task.cc
index 213d5d9..d742d87 100644
--- a/speedtest/transfer_test.cc
+++ b/speedtest/transfer_task.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,35 +14,37 @@
* limitations under the License.
*/
-#include "transfer_test.h"
+#include "transfer_task.h"
#include <cassert>
+#include <thread>
+#include <vector>
namespace speedtest {
-TransferTest::TransferTest(const Options &options)
- : GenericTest(options),
+TransferTask::TransferTask(const Options &options)
+ : HttpTask(options),
bytes_transferred_(0),
requests_started_(0),
requests_ended_(0) {
assert(options.num_transfers > 0);
}
-void TransferTest::ResetCounters() {
+void TransferTask::ResetCounters() {
bytes_transferred_ = 0;
requests_started_ = 0;
requests_ended_ = 0;
}
-void TransferTest::StartRequest() {
+void TransferTask::StartRequest() {
requests_started_++;
}
-void TransferTest::EndRequest() {
+void TransferTask::EndRequest() {
requests_ended_++;
}
-void TransferTest::TransferBytes(long bytes) {
+void TransferTask::TransferBytes(long bytes) {
bytes_transferred_ += bytes;
}
diff --git a/speedtest/transfer_test.h b/speedtest/transfer_task.h
similarity index 78%
rename from speedtest/transfer_test.h
rename to speedtest/transfer_task.h
index d7307a6..83cff9e 100644
--- a/speedtest/transfer_test.h
+++ b/speedtest/transfer_task.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,17 +18,17 @@
#define SPEEDTEST_TRANSFER_TEST_H
#include <atomic>
-#include "generic_test.h"
+#include "http_task.h"
namespace speedtest {
-class TransferTest : public GenericTest {
+class TransferTask : public HttpTask {
public:
- struct Options : GenericTest::Options {
- int num_transfers;
+ struct Options : HttpTask::Options {
+ int num_transfers = 0;
};
- explicit TransferTest(const Options &options);
+ explicit TransferTask(const Options &options);
long bytes_transferred() const { return bytes_transferred_; }
long requests_started() const { return requests_started_; }
@@ -46,8 +46,8 @@
std::atomic_int requests_ended_;
// disallowed
- TransferTest(const TransferTest &) = delete;
- void operator=(const TransferTest &) = delete;
+ TransferTask(const TransferTask &) = delete;
+ void operator=(const TransferTask &) = delete;
};
} // namespace speedtest
diff --git a/speedtest/upload_test.cc b/speedtest/upload_task.cc
similarity index 82%
rename from speedtest/upload_test.cc
rename to speedtest/upload_task.cc
index 8c014ba..251fc41 100644
--- a/speedtest/upload_test.cc
+++ b/speedtest/upload_task.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,24 +14,23 @@
* limitations under the License.
*/
-#include "upload_test.h"
+#include "upload_task.h"
#include <algorithm>
#include <cassert>
#include <iostream>
-#include "generic_test.h"
#include "utils.h"
namespace speedtest {
-UploadTest::UploadTest(const Options &options)
- : TransferTest(options),
+UploadTask::UploadTask(const Options &options)
+ : TransferTask(options),
options_(options) {
assert(options_.payload);
assert(options_.payload->size() > 0);
}
-void UploadTest::RunInternal() {
+void UploadTask::RunInternal() {
ResetCounters();
threads_.clear();
if (options_.verbose) {
@@ -45,15 +44,15 @@
}
}
-void UploadTest::StopInternal() {
+void UploadTask::StopInternal() {
std::for_each(threads_.begin(), threads_.end(), [](std::thread &t) {
t.join();
});
}
-void UploadTest::RunUpload(int id) {
- GenericTest::RequestPtr upload = options_.request_factory(id);
- while (GetStatus() == TestStatus::RUNNING) {
+void UploadTask::RunUpload(int id) {
+ http::Request::Ptr upload = options_.request_factory(id);
+ while (GetStatus() == TaskStatus::RUNNING) {
long uploaded = 0;
upload->set_param("i", to_string(id));
upload->set_param("time", to_string(SystemTimeMicros()));
@@ -65,7 +64,7 @@
TransferBytes(ulnow - uploaded);
uploaded = ulnow;
}
- return GetStatus() != TestStatus::RUNNING;
+ return GetStatus() != TaskStatus::RUNNING;
});
// disable the Expect header as the server isn't expecting it (perhaps
diff --git a/speedtest/upload_test.h b/speedtest/upload_task.h
similarity index 70%
rename from speedtest/upload_test.h
rename to speedtest/upload_task.h
index 509eed6..323f904 100644
--- a/speedtest/upload_test.h
+++ b/speedtest/upload_task.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,24 +14,24 @@
* limitations under the License.
*/
-#ifndef SPEEDTEST_UPLOAD_TEST_H
-#define SPEEDTEST_UPLOAD_TEST_H
+#ifndef SPEEDTEST_UPLOAD_TASK_H
+#define SPEEDTEST_UPLOAD_TASK_H
#include <memory>
#include <string>
#include <thread>
#include <vector>
-#include "transfer_test.h"
+#include "transfer_task.h"
namespace speedtest {
-class UploadTest : public TransferTest {
+class UploadTask : public TransferTask {
public:
- struct Options : TransferTest::Options {
+ struct Options : TransferTask::Options {
std::shared_ptr<std::string> payload;
};
- explicit UploadTest(const Options &options);
+ explicit UploadTask(const Options &options);
protected:
void RunInternal() override;
@@ -44,10 +44,10 @@
std::vector<std::thread> threads_;
// disallowed
- UploadTest(const UploadTest &) = delete;
- void operator=(const UploadTest &) = delete;
+ UploadTask(const UploadTask &) = delete;
+ void operator=(const UploadTask &) = delete;
};
} // namespace speedtest
-#endif // SPEEDTEST_UPLOAD_TEST_H
+#endif // SPEEDTEST_UPLOAD_TASK_H
diff --git a/speedtest/url.cc b/speedtest/url.cc
index 6292790..61588a0 100644
--- a/speedtest/url.cc
+++ b/speedtest/url.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/speedtest/url.h b/speedtest/url.h
index b02bdd0..4844916 100644
--- a/speedtest/url.h
+++ b/speedtest/url.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/speedtest/url_test.cc b/speedtest/url_test.cc
index b43d4fb..f2945a5 100644
--- a/speedtest/url_test.cc
+++ b/speedtest/url_test.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/speedtest/utils.cc b/speedtest/utils.cc
index b41bb1f..580b54b 100644
--- a/speedtest/utils.cc
+++ b/speedtest/utils.cc
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -49,6 +49,15 @@
return buf;
}
+double variance(double d1, double d2) {
+ if (d2 == 0) {
+ return 0.0;
+ }
+ double smaller = std::min(d1, d2);
+ double larger = std::max(d1, d2);
+ return 1.0 - smaller / larger;
+}
+
double ToMegabits(long bytes, long micros) {
return (8.0d * bytes) / micros;
}
diff --git a/speedtest/utils.h b/speedtest/utils.h
index f3e5f1f..7e8d251 100644
--- a/speedtest/utils.h
+++ b/speedtest/utils.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Google Inc. All rights reserved.
+ * Copyright 2016 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,6 +31,9 @@
// Round a double to a minimum number of significant digits
std::string round(double d, int digits);
+// Return 1 - (shorter / larger)
+double variance(double d1, double d2);
+
// Convert bytes and time in micros to speed in megabits
double ToMegabits(long bytes, long micros);