wonderful world of testing

This commit is contained in:
Bryan Bishop 2012-03-24 21:34:19 -05:00
parent 3bd84c1dac
commit 33d8c7a117
2 changed files with 143 additions and 43 deletions

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#utilities to help disassemble pokémon crystal
import sys, os, inspect, md5
import sys, os, inspect, md5, json
from copy import copy
#for IntervalMap
@ -10,6 +10,9 @@ from itertools import izip
#for testing all this crap
import unittest2 as unittest
if not hasattr(json, "dumps"):
json.dumps = json.write
#table of pointers to map groups
#each map group contains some number of map headers
map_group_pointer_table = 0x94000
@ -4435,13 +4438,22 @@ def isolate_incbins():
def process_incbins():
"parse incbin lines into memory"
global incbins
incbins = {} #reset
global asm, incbin_lines, processed_incbins
#load asm if it isn't ready yet
if asm == [] or asm == None:
load_asm()
#get a list of incbins if that hasn't happened yet
if incbin_lines == [] or incbin_lines == None:
isolate_incbins()
#reset the global that this function creates
processed_incbins = {}
#for each incbin..
for incbin in incbin_lines:
#reset this entry
processed_incbin = {}
#get the line number from the global asm line list
line_number = asm.index(incbin)
#forget about all the leading characters
partial_start = incbin[21:]
start = partial_start.split(",")[0].replace("$", "0x")
start = eval(start)
@ -4456,20 +4468,19 @@ def process_incbins():
end = start + interval
end_hex = hex(end).replace("0x", "$")
processed_incbin = {
"line_number": line_number,
processed_incbin = {"line_number": line_number,
"line": incbin,
"start": start,
"interval": interval,
"end": end,
}
"end": end, }
#don't add this incbin if the interval is 0
if interval != 0:
processed_incbins[line_number] = processed_incbin
return processed_incbins
def reset_incbins():
"reset asm before inserting another diff"
global asm, incbin_lines, processed_incbins
asm = None
incbin_lines = []
processed_incbins = {}
@ -4580,7 +4591,7 @@ def apply_diff(diff, try_fixing=True, do_compile=True):
#confirm it's working
if do_compile:
try:
subprocess.check_call("cd ../; make clean; LC_CTYPE=C make", shell=True)
subprocess.check_call("cd ../; make clean; make", shell=True)
return True
except Exception, exc:
if try_fixing:
@ -4592,13 +4603,6 @@ def index(seq, f):
where f(item) == True."""
return next((i for i in xrange(len(seq)) if f(seq[i])), None)
def is_probably_pointer(input):
try:
blah = int(input, 16)
return True
except:
return False
def analyze_intervals():
"""find the largest baserom.gbc intervals"""
global asm, processed_incbins
@ -4614,10 +4618,11 @@ def analyze_intervals():
results.append(processed_incbins[key])
return results
def write_all_labels(all_labels):
fh = open("labels.json", "w")
def write_all_labels(all_labels, filename="labels.json"):
fh = open(filename, "w")
fh.write(json.dumps(all_labels))
fh.close()
return True
def remove_quoted_text(line):
"""get rid of content inside quotes
@ -4632,9 +4637,12 @@ def remove_quoted_text(line):
line = line[0:first] + line[second+1:]
return line
def line_has_comment_address(line, returnable={}):
def line_has_comment_address(line, returnable={}, bank=None):
"""checks that a given line has a comment
with a valid address"""
with a valid address, and returns the address in the object.
Note: bank is required if you have a 4-letter-or-less address,
because otherwise there is no way to figure out which bank
is curretly being scanned."""
#first set the bank/offset to nada
returnable["bank"] = None
returnable["offset"] = None
@ -4658,7 +4666,7 @@ def line_has_comment_address(line, returnable={}):
if line[-2:] == "; ":
return False
#and multiple whitespace doesn't count either
line = line.rstrip(" ")
line = line.rstrip(" ").lstrip(" ")
if line[-1] == ";":
return False
#there must be more content after the semicolon
@ -4675,7 +4683,7 @@ def line_has_comment_address(line, returnable={}):
token = comment.split(" ")[0]
if token in ["0x", "$", "x", ":"]:
return False
bank, offset = None, None
offset = None
#process a token with a A:B format
if ":" in token: #3:3F0A, $3:$3F0A, 0x3:0x3F0A, 3:3F0A
#split up the token
@ -4717,10 +4725,8 @@ def line_has_comment_address(line, returnable={}):
elif "$" in token and not "x" in token:
token = token.replace("$", "0x")
offset = int(token, 16)
bank = calculate_bank(offset)
elif "0x" in token and not "$" in token:
offset = int(token, 16)
bank = calculate_bank(offset)
else: #might just be "1" at this point
token = token.lower()
#check if there are bad characters
@ -4728,9 +4734,10 @@ def line_has_comment_address(line, returnable={}):
if c not in valid:
return False
offset = int(token, 16)
bank = calculate_bank(offset)
if offset == None and bank == None:
return False
if bank == None:
bank = calculate_bank(offset)
returnable["bank"] = bank
returnable["offset"] = offset
returnable["address"] = calculate_pointer(offset, bank=bank)
@ -4773,7 +4780,7 @@ def find_labels_without_addresses():
return without_addresses
label_errors = ""
def get_labels_between(start_line_id, end_line_id, bank_id):
def get_labels_between(start_line_id, end_line_id, bank):
labels = []
#label = {
# "line_number": 15,
@ -4782,6 +4789,8 @@ def get_labels_between(start_line_id, end_line_id, bank_id):
# "offset": 0x5315,
# "address": 0x75315,
#}
if asm == None:
load_asm()
sublines = asm[start_line_id : end_line_id + 1]
for (current_line_offset, line) in enumerate(sublines):
#skip lines without labels
@ -4794,7 +4803,7 @@ def get_labels_between(start_line_id, end_line_id, bank_id):
#setup a place to store return values from line_has_comment_address
returnable = {}
#get the address from the comment
has_comment = line_has_comment_address(line, returnable=returnable)
has_comment = line_has_comment_address(line, returnable=returnable, bank=bank)
#skip this line if it has no address in the comment
if not has_comment: continue
#parse data from line_has_comment_address
@ -4813,7 +4822,7 @@ def get_labels_between(start_line_id, end_line_id, bank_id):
labels.append(label)
return labels
def scan_for_predefined_labels():
def scan_for_predefined_labels(debug=False):
"""looks through the asm file for labels at specific addresses,
this relies on the label having its address after. ex:
@ -4825,8 +4834,9 @@ def scan_for_predefined_labels():
addresses, but faster to write this script. rgbasm would be able
to grab all label addresses better than this script..
"""
bank_intervals = {}
global all_labels
all_labels = []
bank_intervals = {}
#figure out line numbers for each bank
for bank_id in range(0x7F+1):
@ -4836,29 +4846,34 @@ def scan_for_predefined_labels():
abbreviation = "0"
abbreviation_next = "1"
#calculate the start/stop line numbers for this bank
start_line_id = index(asm, lambda line: "\"bank" + abbreviation + "\"" in line)
if bank_id != 0x2c:
if bank_id != 0x7F:
end_line_id = index(asm, lambda line: "\"bank" + abbreviation_next + "\"" in line)
end_line_id += 1
else:
end_line_id = len(asm) - 1
print "bank" + abbreviation + " starts at " + str(start_line_id) + " to " + str(end_line_id)
bank_intervals[bank_id] = {
"start": start_line_id,
"end": end_line_id,
}
if debug:
output = "bank" + abbreviation + " starts at "
output += str(start_line_id)
output += " to "
output += str(end_line_id)
print output
#store the start/stop line number for this bank
bank_intervals[bank_id] = {"start": start_line_id,
"end": end_line_id,}
#for each bank..
for bank_id in bank_intervals.keys():
#get the start/stop line number
bank_data = bank_intervals[bank_id]
start_line_id = bank_data["start"]
end_line_id = bank_data["end"]
#get all labels between these two lines
labels = get_labels_between(start_line_id, end_line_id, bank_id)
#bank_intervals[bank_id]["labels"] = labels
all_labels.extend(labels)
write_all_labels(all_labels)
return all_labels
@ -5112,6 +5127,9 @@ class TestAsmList(unittest.TestCase):
self.assertTrue(x(";3:FFAA"))
self.assertFalse(x('hello world "how are you today;0x1"'))
self.assertTrue(x('hello world "how are you today:0x1";1'))
returnable = {}
self.assertTrue(x("hello_world: ; 0x4050", returnable=returnable, bank=5))
self.assertTrue(returnable["address"] == 0x14050)
def test_line_has_label(self):
x = line_has_label
self.assertTrue(x("hi:"))
@ -5135,6 +5153,88 @@ class TestAsmList(unittest.TestCase):
labels = find_labels_without_addresses()
self.failUnless(len(labels) == 0)
asm = None
def test_get_labels_between(self):
global asm
x = get_labels_between#(start_line_id, end_line_id, bank)
asm = ["HelloWorld: ;1",
"hi:",
"no label on this line",
]
labels = x(0, 2, 0x12)
self.assertEqual(len(labels), 1)
self.assertEqual(labels[0]["label"], "HelloWorld")
del asm
def test_scan_for_predefined_labels(self):
#label keys: line_number, bank, label, offset, address
load_asm()
all_labels = scan_for_predefined_labels()
label_names = [x["label"] for x in all_labels]
self.assertIn("GetFarByte", label_names)
self.assertIn("AddNTimes", label_names)
self.assertIn("CheckShininess", label_names)
def test_write_all_labels(self):
"""dumping json into a file"""
filename = "test_labels.json"
#remove the current file
if os.path.exists(filename):
os.system("rm " + filename)
#make up some labels
labels = []
#fake label 1
label = {"line_number": 5, "bank": 0, "label": "SomeLabel", "address": 0x10}
labels.append(label)
#fake label 2
label = {"line_number": 15, "bank": 2, "label": "SomeOtherLabel", "address": 0x9F0A}
labels.append(label)
#dump to file
write_all_labels(labels, filename=filename)
#open the file and read the contents
file_handler = open(filename, "r")
contents = file_handler.read()
file_handler.close()
#parse into json
obj = json.read(contents)
#begin testing
self.assertEqual(len(obj), len(labels))
self.assertEqual(len(obj), 2)
self.assertEqual(obj, labels)
def test_isolate_incbins(self):
global asm
asm = ["123", "456", "789", "abc", "def", "ghi",
'INCBIN "baserom.gbc",$12DA,$12F8 - $12DA',
"jkl",
'INCBIN "baserom.gbc",$137A,$13D0 - $137A']
lines = isolate_incbins()
self.assertIn(asm[6], lines)
self.assertIn(asm[8], lines)
for line in lines:
self.assertIn("baserom", line)
def test_process_incbins(self):
global incbin_lines, processed_incbins, asm
incbin_lines = ['INCBIN "baserom.gbc",$12DA,$12F8 - $12DA',
'INCBIN "baserom.gbc",$137A,$13D0 - $137A']
asm = copy(incbin_lines)
asm.insert(1, "some other random line")
processed_incbins = process_incbins()
self.assertEqual(len(processed_incbins), len(incbin_lines))
self.assertEqual(processed_incbins[0]["line"], incbin_lines[0])
self.assertEqual(processed_incbins[2]["line"], incbin_lines[1])
def test_reset_incbins(self):
global asm, incbin_lines, processed_incbins
#temporarily override the functions
global load_asm, isolate_incbins, process_incbins
temp1, temp2, temp3 = load_asm, isolate_incbins, process_incbins
def load_asm(): pass
def isolate_incbins(): pass
def process_incbins(): pass
#call reset
reset_incbins()
#check the results
self.assertTrue(asm == [] or asm == None)
self.assertTrue(incbin_lines == [])
self.assertTrue(processed_incbins == {})
#reset the original functions
load_asm, isolate_incbins, process_incbins = temp1, temp2, temp3
class TestMapParsing(unittest.TestCase):
#def test_parse_warp_bytes(self):
# pass #or raise NotImplementedError, bryan_message

View File

@ -24,7 +24,7 @@ GetFarByte: ; 0x304d
INCBIN "baserom.gbc",$305d,$30fe-$305d
AddNTimes ; 0x30fe
AddNTimes: ; 0x30fe
and a
ret z
.loop