Created
          April 2, 2015 20:48 
        
      - 
      
 - 
        
Save ahawkins/8dd877d57539828d16bf to your computer and use it in GitHub Desktop.  
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
  | 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