Skip to content

Instantly share code, notes, and snippets.

@ahawkins
Created April 2, 2015 20:48
Show Gist options
  • Save ahawkins/8dd877d57539828d16bf to your computer and use it in GitHub Desktop.
Save ahawkins/8dd877d57539828d16bf to your computer and use it in GitHub Desktop.
import std.net.curl, std.stdio, std.parallelism, std.datetime, std.json, std.conv;
import std.array, std.range;
import std.getopt;
import std.algorithm;
import serp_request;
unittest {
auto testRun = TestRun();
assert(testRun.category, "Incorrect category default");
assert(testRun.type, "Incorrect type default");
assert(!testRun.location, "Incorrect location default");
assert(!testRun.account, "Incorrect account default");
// Define two placeholder variables used to test positive and negative
// test requests. This is required because an invocation of this program
// must result in > 0 requests. So from this point on all assertions will
// that the positive value is returned and the negative value is omitted.
SERPRequest positive;
SERPRequest negative;
SERPRequest[] serps;
// The following setup and assertions that given the test run is set
// to require everyting that each part of the SERPRequest is checked.
with(testRun) {
category = true;
type = true;
account = true;
location = true;
}
with(positive) {
category = 1;
location = 1;
adType = "test";
accountType = "test";
}
with(negative) {
category = 0;
location = 1;
adType = "test";
accountType = "test";
}
serps = testRun.requests([ positive, negative ]);
assert(1 == serps.length, "Results incorrect");
assert(positive == serps.front, "Category filter incorrect");
negative.category = positive.category;
negative.location = 0;
serps = testRun.requests([ positive, negative ]);
assert(1 == serps.length, "Results incorrect");
assert(positive == serps.front, "Location filter incorrect");
negative.location = positive.location;
negative.adType = null;
serps = testRun.requests([ positive, negative ]);
assert(1 == serps.length, "Results incorrect");
assert(positive == serps.front, "Type filter incorrect");
negative.adType = positive.adType;
negative.accountType = null;
serps = testRun.requests([ positive, negative ]);
assert(1 == serps.length, "Results incorrect");
assert(positive == serps.front, "Account filter incorrect");
// The following tests that when the test run is set to exclude all
// options that every part of the SERPRequest is checked.
with(testRun) {
category = false;
type = false;
account = false;
location = false;
}
with(positive) {
category = 0;
location = 0;
adType = null;
accountType = null;
}
with(negative) {
category = 1;
location = 0;
adType = null;
accountType = null;
}
serps = testRun.requests([ positive, negative ]);
assert(1 == serps.length, "Results incorrect");
assert(positive == serps.front, "Category filter incorrect");
negative.category = positive.category;
negative.location = 1;
serps = testRun.requests([ positive, negative ]);
assert(1 == serps.length, "Results incorrect");
assert(positive == serps.front, "Location filter incorrect");
negative.location = positive.location;
negative.adType = "testing";
serps = testRun.requests([ positive, negative ]);
assert(1 == serps.length, "Results incorrect");
assert(positive == serps.front, "Type filter incorrect");
negative.adType = positive.adType;
negative.accountType = "testing";
serps = testRun.requests([ positive, negative ]);
assert(1 == serps.length, "Results incorrect");
assert(positive == serps.front, "Account filter incorrect");
}
struct TestRun {
auto category = true;
auto type = true;
auto location = false;
auto account = false;
auto requests(SERPRange)(SERPRange serps) if (isInputRange!SERPRange && is(ElementType!SERPRange == SERPRequest))
out(serps)
{
assert(!serps.empty);
}
body
{
return serps.filter!(delegate bool(SERPRequest serp) {
return (
(category == (serp.category != 0)) &&
(location == (serp.location != 0)) &&
(type == (serp.adType != null)) &&
(account == (serp.accountType != null))
);
}).array;
}
}
enum TestResult { PASS, FAIL, EXCEPTION };
TestResult getStatus(string url) {
auto http = HTTP(url);
scope(failure) { return TestResult.EXCEPTION; }
http.maxRedirects = 0;
http.onReceiveStatusLine = (HTTP.StatusLine status) {
if(status.code == 200 ) {
write(".");
} else {
write("F");
}
stdout.flush();
};
// This property must set, otherwise the contents will be printed to stdout.
http.onReceive = (ubyte[] data) { /+ drop +/ return data.length; };
http.perform();
return http.statusLine.code == 200 ? TestResult.PASS : TestResult.FAIL;
};
JSONValue getJSON(string url) {
return get(url).parseJSON();
}
int main(string[] args)
{
StopWatch sw;
auto server = args[1];
auto threads = 50;
auto testRun = TestRun();
getopt(
args,
"category", &testRun.category,
"type", &testRun.type,
"location", &testRun.location,
"account", &testRun.account,
"c", &threads);
std.parallelism.defaultPoolThreads = threads;
writefln("Testing server %s", server);
auto categoriesJSON = getJSON(server ~ "/v1/categories");
Category[] categories;
foreach(j; categoriesJSON["categories"].array) {
auto category = Category();
category.id = to!int(j["id"].integer);
foreach(adType; j["ad_types"].array) {
category.adTypes ~= adType.str;
}
categories ~= category;
}
writefln("Loaded %d categories", categories.length);
auto locationsJSON = getJSON(server ~ "/v1/locations");
int[] locationIDs = locationsJSON["locations"].array.map!(j => to!int(j["id"].integer)).array;
writefln("Loaded %d locations", locationIDs.length);
sw.start();
auto requests = testRun.
requests(generateSERPs(categories, locationIDs)).
map!(serp => server ~ serp.path);
sw.stop();
writefln("Generated %d SERP combinations in %dms", requests.length, sw.peek.msecs);
writefln("Starting requests with %d threads", threads);
writeln();
sw.start();
auto results = taskPool.amap!getStatus(requests);
sw.stop();
writeln("\n");
auto passes = results.count!(result => result == TestResult.PASS);
auto failures = results.count!(result => result == TestResult.FAIL);
auto exceptions = results.count!(result => result == TestResult.EXCEPTION);
writefln("%d passed, %d failed, %d errors\n", passes, failures, exceptions);
writefln("Finished in %dms", sw.peek.msecs);
return requests.length == passes ? 0 : 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment