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.
12 yaml_name = "../adventure.yaml"
13 html_template_path = "coverage_dungeon.html.tpl"
14 html_output_path = "../coverage/adventure.yaml.html"
18 <td class="coverFile">{}</td>
19 <td class="{}"> </td>
20 <td class="{}"> </td>
26 <td class="coverFile">{}</td>
27 <td class="{}"> </td>
31 def search(needle, haystack):
32 # Search for needle in haystack, first escaping needle for regex, then
33 # replacing %s, %d, etc. with regex wildcards, so the variable messages
34 # within the dungeon definition will actually match
35 needle = re.escape(needle) \
36 .replace("\\n", "\n") \
37 .replace("\\t", "\t") \
38 .replace("\%S", ".*") \
39 .replace("\%s", ".*") \
40 .replace("\%d", ".*") \
43 return re.search(needle, haystack)
45 def loc_coverage(locations, text):
46 # locations have a long and a short description, that each have to
47 # be checked seperately
48 for locname, loc in locations:
49 if loc["description"]["long"] == None or loc["description"]["long"] == '':
50 loc["description"]["long"] = True
51 if loc["description"]["long"] != True:
52 if search(loc["description"]["long"], text):
53 loc["description"]["long"] = True
54 if loc["description"]["short"] == None or loc["description"]["short"] == '':
55 loc["description"]["short"] = True
56 if loc["description"]["short"] != True:
57 if search(loc["description"]["short"], text):
58 loc["description"]["short"] = True
60 def arb_coverage(arb_msgs, text):
61 # arbitrary messages are a map to tuples
62 for i, msg in enumerate(arb_msgs):
63 (msg_name, msg_text) = msg
64 if msg_text == None or msg_text == '':
65 arb_msgs[i] = (msg_name, True)
66 elif msg_text != True:
67 if search(msg_text, text):
68 arb_msgs[i] = (msg_name, True)
70 def obj_coverage(objects, text):
71 # objects have multiple descriptions based on state
72 for i, objouter in enumerate(objects):
73 (obj_name, obj) = objouter
74 if obj["descriptions"]:
75 for j, desc in enumerate(obj["descriptions"]):
76 if desc == None or desc == '':
77 obj["descriptions"][j] = True
78 objects[i] = (obj_name, obj)
80 if search(desc, text):
81 obj["descriptions"][j] = True
82 objects[i] = (obj_name, obj)
84 def hint_coverage(hints, text):
85 # hints have a "question" where the hint is offered, followed
86 # by the actual hint if the player requests it
87 for name, hint in hints:
88 if hint["question"] != True:
89 if search(hint["question"], text):
90 hint["question"] = True
91 if hint["hint"] != True:
92 if search(hint["hint"], text):
95 def obit_coverage(obituaries, text):
96 # obituaries have a "query" where it asks the player for a resurrection,
97 # followed by a snarky comment if the player says yes
98 for i, obit in enumerate(obituaries):
99 if obit["query"] != True:
100 if search(obit["query"], text):
102 if obit["yes_response"] != True:
103 if search(obit["yes_response"], text):
104 obit["yes_response"] = True
106 def threshold_coverage(classes, text):
107 # works for class thresholds and turn threshold, which have a "message"
109 for i, msg in enumerate(classes):
110 if msg["message"] == None:
111 msg["message"] = True
112 elif msg["message"] != True:
113 if search(msg["message"], text):
114 msg["message"] = True
116 def specials_actions_coverage(items, text):
117 # works for actions or specials
118 for name, item in items:
119 if item["message"] == None or item["message"] == "NO_MESSAGE":
120 item["message"] = True
121 if item["message"] != True:
122 if search(item["message"], text):
123 item["message"] = True
125 if __name__ == "__main__":
126 with open(yaml_name, "r") as f:
129 with open(html_template_path, "r") as f:
130 html_template = f.read()
132 motions = db["motions"]
133 locations = db["locations"]
134 arb_msgs = db["arbitrary_messages"]
135 objects = db["objects"]
136 hintsraw = db["hints"]
137 classes = db["classes"]
138 turn_thresholds = db["turn_thresholds"]
139 obituaries = db["obituaries"]
140 actions = db["actions"]
141 specials = db["specials"]
144 for hint in hintsraw:
145 hints.append((hint["hint"]["name"], {"question" : hint["hint"]["question"],"hint" : hint["hint"]["hint"]}))
148 for filename in os.listdir(test_dir):
149 if filename.endswith(".chk"):
150 with open(filename, "r") as chk:
152 loc_coverage(locations, text)
153 arb_coverage(arb_msgs, text)
154 obj_coverage(objects, text)
155 hint_coverage(hints, text)
156 threshold_coverage(classes, text)
157 threshold_coverage(turn_thresholds, text)
158 obit_coverage(obituaries, text)
159 specials_actions_coverage(actions, text)
160 specials_actions_coverage(specials, text)
163 location_total = len(locations) * 2
166 for locouter in locations:
167 locname = locouter[0]
169 if loc["description"]["long"] != True:
170 long_success = "uncovered"
172 long_success = "covered"
173 location_covered += 1
175 if loc["description"]["short"] != True:
176 short_success = "uncovered"
178 short_success = "covered"
179 location_covered += 1
181 location_html += row_3_fields.format(locname, long_success, short_success)
182 location_percent = round((location_covered / float(location_total)) * 100, 1)
186 arb_total = len(arb_msgs)
188 for name, msg in arb_msgs:
190 success = "uncovered"
194 arb_msg_html += row_2_fields.format(name, success)
195 arb_percent = round((arb_covered / float(arb_total)) * 100, 1)
201 for (obj_name, obj) in objects:
202 if obj["descriptions"]:
203 for j, desc in enumerate(obj["descriptions"]):
206 success = "uncovered"
210 object_html += row_2_fields.format("%s[%d]" % (obj_name, j), success)
211 objects_percent = round((objects_covered / float(objects_total)) * 100, 1)
215 hints_total = len(hints) * 2
217 for name, hint in hints:
218 if hint["question"] != True:
219 question_success = "uncovered"
221 question_success = "covered"
223 if hint["hint"] != True:
224 hint_success = "uncovered"
226 hint_success = "covered"
228 hints_html += row_3_fields.format(name, question_success, hint_success)
229 hints_percent = round((hints_covered / float(hints_total)) * 100, 1)
232 class_total = len(classes)
234 for name, msg in enumerate(classes):
235 if msg["message"] != True:
236 success = "uncovered"
240 class_html += row_2_fields.format(msg["threshold"], success)
241 class_percent = round((class_covered / float(class_total)) * 100, 1)
244 turn_total = len(turn_thresholds)
246 for name, msg in enumerate(turn_thresholds):
247 if msg["message"] != True:
248 success = "uncovered"
252 turn_html += row_2_fields.format(msg["threshold"], success)
253 turn_percent = round((turn_covered / float(turn_total)) * 100, 1)
255 obituaries_html = "";
256 obituaries_total = len(obituaries) * 2
257 obituaries_covered = 0
258 for i, obit in enumerate(obituaries):
259 if obit["query"] != True:
260 query_success = "uncovered"
262 query_success = "covered"
263 obituaries_covered += 1
264 if obit["yes_response"] != True:
265 obit_success = "uncovered"
267 obit_success = "covered"
268 obituaries_covered += 1
269 obituaries_html += row_3_fields.format(i, query_success, obit_success)
270 obituaries_percent = round((obituaries_covered / float(obituaries_total)) * 100, 1)
274 actions_total = len(actions)
276 for name, action in actions:
277 if action["message"] != True:
278 success = "uncovered"
282 actions_html += row_2_fields.format(name, success)
283 actions_percent = round((actions_covered / float(actions_total)) * 100, 1)
286 special_total = len(specials)
288 for name, special in specials:
289 if special["message"] != True:
290 success = "uncovered"
294 special_html += row_2_fields.format(name, success)
295 special_percent = round((special_covered / float(special_total)) * 100, 1)
297 # output some quick report stats
298 print("\nadventure.yaml coverage rate:")
299 print(" locations..........: {}% covered ({} of {})".format(location_percent, location_covered, location_total))
300 print(" arbitrary_messages.: {}% covered ({} of {})".format(arb_percent, arb_covered, arb_total))
301 print(" objects............: {}% covered ({} of {})".format(objects_percent, objects_covered, objects_total))
302 print(" hints..............: {}% covered ({} of {})".format(hints_percent, hints_covered, hints_total))
303 print(" classes............: {}% covered ({} of {})".format(class_percent, class_covered, class_total))
304 print(" turn_thresholds....: {}% covered ({} of {})".format(turn_percent, turn_covered, turn_total))
305 print(" obituaries.........: {}% covered ({} of {})".format(obituaries_percent, obituaries_covered, obituaries_total))
306 print(" actions............: {}% covered ({} of {})".format(actions_percent, actions_covered, actions_total))
307 print(" specials...........: {}% covered ({} of {})".format(special_percent, special_covered, special_total))
310 with open(html_output_path, "w") as f:
311 f.write(html_template.format(
312 location_total, location_covered, location_percent,
313 arb_total, arb_covered, arb_percent,
314 objects_total, objects_covered, objects_percent,
315 hints_total, hints_covered, hints_percent,
316 class_total, class_covered, class_percent,
317 turn_total, turn_covered, turn_percent,
318 obituaries_total, obituaries_covered, obituaries_percent,
319 actions_total, actions_covered, actions_percent,
320 special_total, special_covered, special_percent,
321 location_html, arb_msg_html, object_html, hints_html,
322 class_html, turn_html, obituaries_html, actions_html, special_html