{"id":879,"date":"2010-12-14T17:23:02","date_gmt":"2010-12-14T07:23:02","guid":{"rendered":"http:\/\/www.erisian.com.au\/wordpress\/?p=879"},"modified":"2010-12-14T17:25:52","modified_gmt":"2010-12-14T07:25:52","slug":"silly-testcase-hacks","status":"publish","type":"post","link":"https:\/\/www.erisian.com.au\/wordpress\/2010\/12\/14\/silly-testcase-hacks","title":{"rendered":"Silly testcase hacks"},"content":{"rendered":"<p>Martin Pool <a href=\"http:\/\/twitter.com\/#!\/sourcefrog\/status\/14559420616806401\">linked<\/a> to an old <a href=\"http:\/\/evanmiller.org\/functional-tests-as-a-tree-of-continuations.html\">post by Evan Miller<\/a> on how writing tests could be more pleasant if you could just do the setup and teardown parts once, and (essentially) rely on backtracking to make sure it happens for every test. He uses a functional language for his example, and it&#8217;s pretty interesting.<\/p>\n<p>But it is overly indented, and hey, I like my procedural code, so what about trying the same thing in Python? Here&#8217;s my go at it. The code under test was the simplest thing I could think of &#8212; a primality checker:<\/p>\n<blockquote><p><code><\/p>\n<pre>def is_prime(n):\r\n    if n == 1: return False\r\n    i = 2\r\n    while i*i <= n:\r\n        if n % i == 0: return False\r\n        i += 1\r\n    return True<\/pre>\n<p><\/code><\/p><\/blockquote>\n<p>My test function then tests a dozen numbers numbers which I know are prime or not, return True if is_prime got the right answer, and False otherwise. It makes use of a magic \"branch\" function to work out which number to test:<\/p>\n<blockquote><p><code><\/p>\n<pre>def prime_test(branch):\r\n    if branch(True, False):\r\n        n = branch(2,3,5,7,1231231)\r\n        return is_prime(n)\r\n    else:\r\n        n = branch(1,4,6,8,9,10,12312312)\r\n        return not is_prime(n)<\/pre>\n<p><\/code><\/p><\/blockquote>\n<p>In order to get all the tests run, we need a loop, so the test harness looks like:<\/p>\n<blockquote><p><code><\/p>\n<pre>for id, result in run_tests(prime_test):\r\n    print id, result<\/pre>\n<p><\/code><\/p><\/blockquote>\n<p>(Counting up successes, and just printing the ids of failures would make more sense, probably. In any event, the output looks like:<\/p>\n<blockquote><p><code><\/p>\n<pre>[True, 2] True\r\n[True, 3] True\r\n[True, 5] True\r\n[True, 7] True\r\n[True, 1231231] True\r\n[False, 1] True\r\n[False, 4] True\r\n[False, 6] True\r\n[False, 8] True\r\n[False, 9] True\r\n[False, 10] True\r\n[False, 12312312] True<\/pre>\n<p><\/code><\/p><\/blockquote>\n<p>Obviously all the magic happens in run_tests which needs to work out how many test cases there'll end up being, and provide the magic branch function which will give the right values. Using Python's generators to keep some state makes that reasonable straightforward, if a bit head-twisting:<\/p>\n<p><blockquoute><code><\/p>\n<pre>def run_tests(test_fn):\r\n    def branch(*options):\r\n        if len(idx) == state[0]:\r\n            idx.append(0)\r\n        n = idx[state[0]]\r\n        if n+1 < len(options):\r\n            state[1] = state[0]\r\n        state[0] += 1\r\n        vals.append(options[n])\r\n        return options[n]\r\n\r\n    idx = []\r\n    while True:\r\n        state, vals = [0, None], []\r\n        res = test_fn(branch)\r\n        yield (vals, res)\r\n        if state[1] is None: break\r\n        idx[state[1]] += 1\r\n        idx[state[1]+1:] = []<\/pre>\n<p><\/code><\/p><\/blockquote>\n<p>This is purely a coding optimisation -- any setup and teardown in prime_test is performed each time, there's no caching. I don't think there'd be much difficulty writing the same thing in C or similar either -- there's no real use being made of laziness or similar here -- I'm just passing a function that happens to have state around rather than a struct that happens to include a function pointer.<\/p>\n<p>Anyway, kinda nifty, I think!<\/p>\n<p>(Oh, this is also inspired by some of the stuff Clinton was doing with abusing fork() to get full coverage of failure cases for code that uses malloc() and similar, by <a href=\"https:\/\/wiki.csiro.au\/confluence\/display\/ASLABOSS\/Humbug+talk+transcription\">using LD_PRELOAD<\/a>)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Martin Pool linked to an old post by Evan Miller on how writing tests could be more pleasant if you could just do the setup and teardown parts once, and (essentially) rely on backtracking to make sure it happens for every test. He uses a functional language for his example, and it&#8217;s pretty interesting. But [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[16],"tags":[],"_links":{"self":[{"href":"https:\/\/www.erisian.com.au\/wordpress\/wp-json\/wp\/v2\/posts\/879"}],"collection":[{"href":"https:\/\/www.erisian.com.au\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.erisian.com.au\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.erisian.com.au\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.erisian.com.au\/wordpress\/wp-json\/wp\/v2\/comments?post=879"}],"version-history":[{"count":5,"href":"https:\/\/www.erisian.com.au\/wordpress\/wp-json\/wp\/v2\/posts\/879\/revisions"}],"predecessor-version":[{"id":883,"href":"https:\/\/www.erisian.com.au\/wordpress\/wp-json\/wp\/v2\/posts\/879\/revisions\/883"}],"wp:attachment":[{"href":"https:\/\/www.erisian.com.au\/wordpress\/wp-json\/wp\/v2\/media?parent=879"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.erisian.com.au\/wordpress\/wp-json\/wp\/v2\/categories?post=879"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.erisian.com.au\/wordpress\/wp-json\/wp\/v2\/tags?post=879"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}