d7f46c8a9128e9105e6610c7edddbf2f252e478b
[open-adventure.git] / tests / coverage_dungeon.py
1 #!/usr/bin/env python
2
3 # This is the open-adventure dungeon text coverage report generator. It
4 # consumes a YAML description of the dungeon and determines whether the
5 # various strings contained are present within the test check files.
6 #
7 # Currently, only the location descriptions, arbitrary messages, object
8 # descriptions, hints, classes and turn thrusholds are supported. This will 
9 # be expanded in the future.
10
11 import os
12 import yaml
13 import re
14 import pprint
15
16 test_dir = "."
17 yaml_name = "../adventure.yaml"
18 html_template_path = "coverage_dungeon.html.tpl"
19 html_output_path = "../coverage/adventure.yaml.html"
20
21 location_row = """
22     <tr>
23         <td class="coverFile">{}</td>
24         <td class="{}">&nbsp;</td>
25         <td class="{}">&nbsp;</td>
26     </tr>
27 """
28
29 arb_msg_row = """
30     <tr>
31         <td class="coverFile">{}</td>
32         <td class="{}">&nbsp;</td>
33     </tr>
34 """
35
36 object_row = """
37     <tr>
38         <td class="coverFile">{}</td>
39         <td class="{}">&nbsp;</td>
40     </tr>
41 """
42
43 def search(needle, haystack):
44     # Search for needle in haystack, first escaping needle for regex, then
45     # replacing %s, %d, etc. with regex wildcards, so the variable messages
46     # within the dungeon definition will actually match
47     needle = re.escape(needle) \
48              .replace("\%S", ".*") \
49              .replace("\%s", ".*") \
50              .replace("\%d", ".*") \
51              .replace("\%V", ".*")
52
53     return re.search(needle, haystack)
54
55 def loc_coverage(locations, text):
56     for locname, loc in locations:
57         if loc["description"]["long"] == None or loc["description"]["long"] == '':
58             loc["description"]["long"] = True
59         if loc["description"]["long"] != True:
60             if search(loc["description"]["long"], text):
61                 loc["description"]["long"] = True
62         if loc["description"]["short"] == None or loc["description"]["short"] == '':
63             loc["description"]["short"] = True
64         if loc["description"]["short"] != True:
65             if search(loc["description"]["short"], text):
66                 loc["description"]["short"] = True
67
68 def arb_coverage(arb_msgs, text):
69     for i, msg in enumerate(arb_msgs):
70         (msg_name, msg_text) = msg
71         if msg_text == None or msg_text == '':
72             arb_msgs[i] = (msg_name, True)
73         elif msg_text != True:
74             if search(msg_text, text):
75                 arb_msgs[i] = (msg_name, True)
76
77 def obj_coverage(objects, text):
78     for i, objouter in enumerate(objects):
79         (obj_name, obj) = objouter
80         if obj["descriptions"]:
81             for j, desc in enumerate(obj["descriptions"]):
82                 if desc == None or desc == '':
83                     obj["descriptions"][j] = True
84                     objects[i] = (obj_name, obj)
85                 elif desc != True:
86                     if search(desc, text):
87                         obj["descriptions"][j] = True
88                         objects[i] = (obj_name, obj)
89
90
91 def hint_coverage(hints, text):
92     for name, hint in hints:
93         if hint["question"] != True:
94             if search(hint["question"], text):
95                 hint["question"] = True
96         if hint["hint"] != True:
97             if search(hint["hint"], text):
98                 hint["hint"] = True
99         continue
100
101 def threshold_coverage(classes, text):
102     for i, msg in enumerate(classes):
103         if msg["message"] == None:
104             msg["message"] = True
105         elif msg["message"] != True:
106             if search(msg["message"], text):
107                 msg["message"] = True
108
109
110 if __name__ == "__main__":
111     with open(yaml_name, "r") as f:
112         db = yaml.load(f)
113
114     with open(html_template_path, "r") as f:
115         html_template = f.read()
116
117     locations = db["locations"]
118     arb_msgs = db["arbitrary_messages"]
119     objects = db["objects"]
120     hintsraw = db["hints"]
121     classes = db["classes"]
122     turn_thresholds = db["turn_thresholds"]
123
124     hints = []
125     for hint in hintsraw:
126         hints.append((hint["hint"]["name"], {"question" : hint["hint"]["question"],"hint" : hint["hint"]["hint"]}))
127
128     text = ""
129     for filename in os.listdir(test_dir):
130         if filename.endswith(".chk"):
131             with open(filename, "r") as chk:
132                 text = chk.read()
133                 loc_coverage(locations, text)
134                 arb_coverage(arb_msgs, text)
135                 obj_coverage(objects, text)
136                 hint_coverage(hints, text)
137                 threshold_coverage(classes, text)
138                 threshold_coverage(turn_thresholds, text)
139
140     location_html = ""
141     location_total = len(locations) * 2
142     location_covered = 0
143     locations.sort()
144     for locouter in locations:
145         locname = locouter[0]
146         loc = locouter[1]
147         if loc["description"]["long"] != True:
148             long_success = "uncovered"
149         else:
150             long_success = "covered"
151             location_covered += 1
152
153         if loc["description"]["short"] != True:
154             short_success = "uncovered"
155         else:
156             short_success = "covered"
157             location_covered += 1
158
159         location_html += location_row.format(locname, long_success, short_success)
160     location_percent = round((location_covered / float(location_total)) * 100, 1)
161
162     arb_msgs.sort()
163     arb_msg_html = ""
164     arb_total = len(arb_msgs)
165     arb_covered = 0
166     for name, msg in arb_msgs:
167         if msg != True:
168             success = "uncovered"
169         else:
170             success = "covered"
171             arb_covered += 1
172         arb_msg_html += arb_msg_row.format(name, success)
173     arb_percent = round((arb_covered / float(arb_total)) * 100, 1)
174
175     object_html = ""
176     objects_total = 0
177     objects_covered = 0
178     objects.sort()
179     for (obj_name, obj) in objects:
180         if obj["descriptions"]:
181             for j, desc in enumerate(obj["descriptions"]):
182                 objects_total += 1
183                 if desc != True:
184                     success = "uncovered"
185                 else:
186                     success = "covered"
187                     objects_covered += 1
188                 object_html += object_row.format("%s[%d]" % (obj_name, j), success)
189     objects_percent = round((objects_covered / float(objects_total)) * 100, 1)
190
191     hints.sort()
192     hints_html = "";
193     hints_total = len(hints) * 2
194     hints_covered = 0
195     for name, hint in hints:
196         if hint["question"] != True:
197             question_success = "uncovered"
198         else:
199             question_success = "covered"
200             hints_covered += 1
201         if hint["hint"] != True:
202             hint_success = "uncovered"
203         else:
204             hint_success = "covered"
205             hints_covered += 1
206         hints_html += location_row.format(name, question_success, hint_success)
207     hints_percent = round((hints_covered / float(hints_total)) * 100, 1)
208
209     class_html = ""
210     class_total = len(classes)
211     class_covered = 0
212     for name, msg in enumerate(classes):
213         if msg["message"] != True:
214             success = "uncovered"
215         else:
216             success = "covered"
217             class_covered += 1
218         class_html += arb_msg_row.format(msg["threshold"], success)
219     class_percent = round((class_covered / float(class_total)) * 100, 1)
220
221     turn_html = ""
222     turn_total = len(turn_thresholds)
223     turn_covered = 0
224     for name, msg in enumerate(turn_thresholds):
225         if msg["message"] != True:
226             success = "uncovered"
227         else:
228             success = "covered"
229             turn_covered += 1
230         turn_html += arb_msg_row.format(msg["threshold"], success)
231     turn_percent = round((turn_covered / float(turn_total)) * 100, 1)
232
233     # output some quick report stats
234     print("\nadventure.yaml coverage rate:")
235     print("  locations..........: {}% covered ({} of {})".format(location_percent, location_covered, location_total))
236     print("  arbitrary_messages.: {}% covered ({} of {})".format(arb_percent, arb_covered, arb_total))
237     print("  objects............: {}% covered ({} of {})".format(objects_percent, objects_covered, objects_total))
238     print("  hints..............: {}% covered ({} of {})".format(hints_percent, hints_covered, hints_total))
239     print("  classes............: {}% covered ({} of {})".format(class_percent, class_covered, class_total))
240     print("  turn_thresholds....: {}% covered ({} of {})".format(turn_percent, turn_covered, turn_total))
241
242     # render HTML report
243     with open(html_output_path, "w") as f:
244         f.write(html_template.format(
245                 location_total, location_covered, location_percent,
246                 arb_total, arb_covered, arb_percent,
247                 objects_total, objects_covered, objects_percent,
248                 hints_total, hints_covered, hints_percent,
249                 class_total, class_covered, class_percent,
250                 turn_total, turn_covered, turn_percent,
251                 location_html, arb_msg_html, object_html, hints_html, class_html, turn_html
252         ))