After recently discovering the Utility Mill, I decided to create an automated tester that would check that the latest revision was still operating according to specifications. It checks the web site once a minute and displays status information as it runs. It is included here because it comes in three sections. The first section has several utility functions that you may find useful in your own applications. The actual test suite is located in the second section. The last section includes code that makes getting random text easy and fast (after an initial loading session). Hopefully, the first portion of the code may be of use to you.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | import urllib.request
import urllib.parse
import xml.dom.minidom
def test_latest(name, **query):
revision = get_revision(name)
output = get_output(name, revision, query)
return output
def get_output(name, revision, query):
string = run_utility('xml', name, revision, query)
middle = string.split('<', 1)[1].rsplit('>', 1)[0]
dom = xml.dom.minidom.parseString('<' + middle + '>')
value = extract_output(dom)
dom.unlink()
return value
def run_utility(api, name, revision, query):
url = 'http://utilitymill.com/api/{0}/utility/{1}/{2}/run?{3}'
params = urllib.parse.urlencode(query)
file = urllib.request.urlopen(url.format(api, name, revision, params))
# dom = xml.dom.minidom.parse(file)
return file.read().decode()
def extract_output(dom):
elements = dom.getElementsByTagName('output')
assert len(elements) == 1, 'XML Error'
output = elements[0]
assert len(output.childNodes) == 1, 'XML Error'
child = output.childNodes[0]
assert child.nodeType == child.CDATA_SECTION_NODE, 'XML Error'
return child.nodeValue
def get_revision(name):
string = info_utility('xml', name)
middle = string.split('<', 1)[1].rsplit('>', 1)[0]
dom = xml.dom.minidom.parseString('<' + middle + '>')
value = extract_number(dom)
dom.unlink()
return value
def info_utility(api, name):
url = 'http://utilitymill.com/api/{0}/utility/{1}/info'
file = urllib.request.urlopen(url.format(api, name))
return file.read().decode()
def extract_number(dom):
elements = dom.getElementsByTagName('number')
assert len(elements) == 1, 'XML Error'
number = elements[0]
assert len(number.childNodes) == 1, 'XML Error'
child = number.childNodes[0]
assert child.nodeType == child.TEXT_NODE, 'XML Error'
return child.nodeValue
################################################################################
import random
import spice
import time
# Python 2.5 Hack
__builtins__.xrange = __builtins__.range
def hack1(*args):
return list(xrange(*args))
__builtins__.range = hack1
__builtins__.xmap = __builtins__.map
def hack2(*args):
return list(xmap(*args))
__builtins__.map = hack2
def _decode(string, map_1, map_2):
'Private module function.'
cache = ''
iterator = iter(string)
for byte in iterator:
bits_12 = map_1[ord(byte)] << 6
bits_34 = map_1[ord(next(iterator))] << 4
bits_56 = map_1[ord(next(iterator))] << 2
bits_78 = map_1[ord(next(iterator))]
cache += map_2[bits_12 + bits_34 + bits_56 + bits_78]
return cache
spice._decode = _decode
# END
def main():
try:
while True:
print('Testing', end=' ')
choice = random.randrange(3)
if choice == 1:
# Test "Create Keys" Action
print('Create', end=' ')
choice = random.randrange(4)
if choice == 1:
# Test No Names
print('No Names', end=' ... ')
test_create()
elif choice == 2:
# Test Major Name
print('Major Name', end=' ... ')
n1 = verse()
major, minor = test_create(MAJOR_NAME=n1)
assert major == spice.named_major(n1)
elif choice == 3:
# Test Minor Name
print('Minor Name', end=' ... ')
n2 = verse()
major, minor = test_create(MINOR_NAME=n2)
assert minor == spice.named_minor(n2)
else:
# Test Both Names
print('Both Names', end=' ... ')
n1, n2 = verse(), verse()
major, minor = test_create(MAJOR_NAME=n1, MINOR_NAME=n2)
assert major == spice.named_major(n1)
assert minor == spice.named_minor(n2)
elif choice == 2:
# Test "Encode Input" Action
print('Encode', end=' ... ')
major = spice.crypt_major()
minor = spice.crypt_minor()
data = verse()
encoded = test_encode(major, minor, data)
decoded = spice.decode_string(encoded, major, minor)
assert decoded == data
else:
# Test "Decode Input" Action
print('Decode', end=' ... ')
major = spice.crypt_major()
minor = spice.crypt_minor()
data = verse()
encoded = spice.encode_string(data, major, minor)
decoded = test_decode(major, minor, encoded)
assert decoded == data
print('PASS')
time.sleep(60)
except:
print('FAIL')
def test_create(**query):
output = test_latest('SPICE_Text', ACTION='Create Keys', **query)
x, x, major, x, x, x, minor = output.split('\n')
major = hex2bin(major)
minor = hex2bin(minor)
spice._check_major(major)
spice._check_minor(minor)
return major, minor
def test_encode(major, minor, data):
hma = bin2hex(major)
hmi = bin2hex(minor)
output = test_latest('SPICE_Text', ACTION='Encode Input',
MAJOR_KEY=hma, MINOR_KEY=hmi, INPUT=data)
encoded = hex2bin(output.replace('\r', '').replace('\n', ''))
return encoded
def test_decode(major, minor, data):
hma = bin2hex(major)
hmi = bin2hex(minor)
hda = bin2hex(data)
decoded = test_latest('SPICE_Text', ACTION='Decode Input',
MAJOR_KEY=hma, MINOR_KEY=hmi, INPUT=hda)
return decoded
def bin2hex(x):
return ''.join('%02X' % ord(y) for y in x)
def hex2bin(x):
return ''.join(chr(int(x[y:y+2], 16)) for y in range(0, len(x), 2))
################################################################################
def verse():
return random.choice(random.choice(random.choice(BIBLE)))
def load_bible():
global BIBLE
try:
bible = open('bible13.txt', 'r').read()
except:
bible = get_bible()
open('bible13.txt', 'w').write(bible)
win_fix = bible.replace('\r\n', '\n')
mac_fix = win_fix.replace('\r', '\n')
BIBLE = parse_bible(mac_fix)
def get_bible():
url = 'http://www.gutenberg.org/dirs/etext92/bible13.txt'
file = urllib.request.urlopen(url)
return file.read().decode()
def parse_bible(string):
'Parse Bible and return 3D array.'
book = chap = vers = 1
form = '%02u:%03u:%03u'
book_s, chap_s, vers_s = [], [], []
start = 0
while True:
try:
start = string.index(form % (book, chap, vers), start) + 11
end = string.index('\n\n', start)
vers_s.append(' '.join(string[start:end].replace('\n', '').split()))
start = end
vers += 1
except:
if vers != 1:
chap_s.append(vers_s)
vers_s = []
chap += 1
vers = 1
elif chap != 1:
book_s.append(chap_s)
chap_s = []
book += 1
chap = 1
elif book != 1:
return book_s
else:
raise EOFError
################################################################################
if __name__ == '__main__':
load_bible()
main()
|
neat idea! I should run this for all the utilities someday.
Any chance you could slow it down to say once an hour? The utility popularity ranking algorithm isn't very sophisticated :-(
The tester has been shutdown. After testing the SPICE algorithm 16,000 times online, encode failed twice, and decode failed twice. That's pretty good considering that they may have been requests rejected by the server.
Would it possible to not count executions initiated through your API, Greg?
Does anyone know why, when this code is run on Windows, that the file is saved with "\r\r\n" between the lines?