Coverage for tests/e2e/fuzz/common.py : 73%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2from __future__ import annotations
4# -- stdlib --
5from itertools import chain, combinations as C, permutations as P
6from typing import Any, cast
7import logging
8import random
10# -- third party --
11import gevent
13# -- own --
14from game.base import EventHandler
15from thb.actions import ActionStageLaunchCard, CardChooser, skill_wrap, CharacterChooser
16from thb.inputlets import ActionInputlet, ChooseOptionInputlet, ChoosePeerCardInputlet
19# -- code --
20log = logging.getLogger('UserInputFuzzingHandler')
23def let_it_go(*cores):
24 while True:
25 gevent.idle(-100)
27 for i in range(10): 27 ↛ 33line 27 didn't jump to line 33, because the loop on line 27 didn't complete
28 if any(c.server._ep.active for c in cores):
29 break
31 gevent.sleep(0.05)
32 else:
33 raise Exception('STUCK!')
35 for c in cores:
36 c.server._ep.active = False
39class UserInputFuzzingHandler(EventHandler):
41 def handle(self, evt: str, arg: Any) -> Any:
42 if evt == 'user_input':
43 trans, ilet = arg
44 self.react(trans, ilet)
46 return arg
48 def react(self, trans, ilet):
49 p = ilet.actor
50 g = self.game
52 if trans.name == 'ActionStageAction':
53 if random.random() < 0.05:
54 return False
56 cards = list(p.showncards) + list(p.cards) + list(p.equips)
58 while random.random() < 0.5:
59 # Skill
60 skl = [sk for sk in p.skills if sk.target.__name__ != 't_None']
61 if skl: 61 ↛ 64line 61 didn't jump to line 64, because the condition on line 61 was never false
62 sk = random.choice(skl)
63 else:
64 break
66 # Card
67 cc = list(chain(*[C(cards, i) for i in range(4)]))
68 random.shuffle(cc)
70 for cl in cc:
71 for tl in self.possible_targets(g, p, sk):
72 c = skill_wrap(p, [sk], cl, {})
74 ok, reason = self.ui_meta_walk_wrapped([c])
75 if not ok: 75 ↛ 76line 75 didn't jump to line 76, because the condition on line 75 was never true
76 assert not c.check(), (c, c.associated_cards, c.check(), ok, reason)
77 continue
79 try:
80 tl, ok = c.target(p, tl)
81 except Exception as e:
82 raise Exception(f"{c}.target: {c.target} failed") from e
84 try:
85 ok2, reason = c.ui_meta.is_action_valid(c, tl)
86 except Exception as e:
87 raise Exception(f"{c}.ui_meta.is_action_valid failed") from e
89 # This can happen
90 # assert (ok and bool(c.check())) == ok2, {
91 # 'ok': ok,
92 # 'c.check()': c.check(),
93 # 'ok2': ok2,
94 # 'c': c,
95 # 'tl': tl,
96 # 'reason': reason,
97 # }
99 if not ok:
100 continue
102 if self.try_launch(ilet, cl, tl, skills=[sk]):
103 return
104 break
106 for c in cards:
107 if not c.associated_action:
108 continue
110 for t in self.possible_targets(g, p, c):
111 if self.try_launch(ilet, [c], t):
112 return True
114 elif trans.name in ('Action', 'AskForRejectAction') and isinstance(ilet, ActionInputlet):
115 rst = {
116 'skills': [],
117 'cards': [],
118 'characters': [],
119 }
121 if ilet.categories:
122 cond = cast(CardChooser, ilet.initiator).cond
123 cl = list(p.showncards) + list(p.cards)
125 for skcls in ilet.actor.skills: 125 ↛ 137line 125 didn't jump to line 137, because the loop on line 125 didn't complete
126 if cond([skcls(ilet.actor)]) and random.random() < 0.5:
127 for c in chain([], C(cl, 1), C(cl, 2), [cl]): 127 ↛ 134line 127 didn't jump to line 134, because the loop on line 127 didn't complete
128 sk = skill_wrap(p, [skcls], c, {})
129 assert cond([sk])
130 rst['skills'] = [skcls]
131 rst['cards'] = c
132 break
134 if rst: 134 ↛ 125line 134 didn't jump to line 125, because the condition on line 134 was never false
135 break
136 else:
137 for c in chain(C(cl, 1), C(cl, 2), [cl]):
138 if cond(c):
139 rst['cards'] = c
140 break
142 if ilet.candidates:
143 target = cast(CharacterChooser, ilet.initiator).choose_player_target
144 # (self, pl: Sequence[Character]) -> Tuple[List[Character], bool]: ...
145 pl = ilet.candidates
146 for p in chain(C(pl, 1), C(pl, 2), [pl]): 146 ↛ 152line 146 didn't jump to line 152, because the loop on line 146 didn't complete
147 p, ok = target(p)
148 if ok: 148 ↛ 146line 148 didn't jump to line 146, because the condition on line 148 was never false
149 rst['characters'] = p
150 break
152 if rst: 152 ↛ 155line 152 didn't jump to line 155, because the condition on line 152 was never false
153 ilet.set_result(**rst)
155 return True
157 elif trans.name == 'ChooseOption' and isinstance(ilet, ChooseOptionInputlet):
158 ilet.set_option(random.choice(list(ilet.options) * 2 + [None]))
160 elif trans.name == 'ChoosePeerCard' and isinstance(ilet, ChoosePeerCardInputlet):
161 tgt = ilet.target
162 if random.random() < 0.9:
163 cats = [getattr(tgt, i) for i in ilet.categories]
164 cl = list(chain(*cats))
165 if cl: 165 ↛ exitline 165 didn't return from function 'react', because the condition on line 165 was never false
166 ilet.set_card(random.choice(cl))
167 elif trans.name == 'SortCharacter': 167 ↛ 168line 167 didn't jump to line 168, because the condition on line 167 was never true
168 pass
169 elif trans.name == 'ChooseGirl':
170 from settings import TESTING_CHARACTERS as TESTS
171 choices = [c for c in ilet.mapping[ilet.actor] if c.char_cls and c.char_cls.__name__ in TESTS]
172 if choices: 172 ↛ 173line 172 didn't jump to line 173, because the condition on line 172 was never true
173 c = random.choice(choices)
174 log.info('Got %s', c.char_cls)
175 ilet.set_choice(c)
176 elif trans.name == 'HarvestChoose':
177 pass
178 elif trans.name == 'Pindian':
179 pass
180 elif trans.name == 'HopeMask':
181 pass
182 elif trans.name == 'Prophet':
183 pass
184 elif trans.name == 'ChooseIndividualCard': 184 ↛ 185line 184 didn't jump to line 185, because the condition on line 184 was never true
185 pass
186 elif trans.name == 'BanGirl': 186 ↛ 189line 186 didn't jump to line 189, because the condition on line 186 was never false
187 pass
188 else:
189 log.warning('Not processing %s transaction', trans.name)
190 1/0
192 def ui_meta_walk_wrapped(self, cl, check_is_complete=False):
193 from thb.cards.base import Skill
194 for c in cl:
195 if not isinstance(c, Skill):
196 continue
198 if check_is_complete: 198 ↛ 199line 198 didn't jump to line 199, because the condition on line 198 was never true
199 rst, reason = c.ui_meta.is_complete(c)
200 if not rst:
201 return rst, reason
203 rst, reason = self.ui_meta_walk_wrapped(c.associated_cards, True)
204 if not rst: 204 ↛ 205line 204 didn't jump to line 205, because the condition on line 204 was never true
205 return rst, reason
207 return True, 'OK'
209 def try_launch(self, ilet, cl, tl, skills=[]):
210 p = ilet.actor
211 if skills:
212 c = skill_wrap(p, skills, cl, {})
213 else:
214 assert len(cl) == 1, cl
215 c, = cl
217 act = ActionStageLaunchCard(p, tl, c)
218 if act.can_fire():
219 ilet.set_result(skills=skills, cards=cl, characters=tl)
220 return True
222 return False
224 def possible_targets(self, g, me, c):
225 target = c.target
226 tn = target.__name__
227 pl = g.players
229 if tn == 't_None': 229 ↛ 230line 229 didn't jump to line 230, because the condition on line 229 was never true
230 raise Exception('Fuck')
232 elif tn == 't_Self': 232 ↛ 233line 232 didn't jump to line 233, because the condition on line 232 was never true
233 rl = [[me]]
235 elif tn == 't_OtherOne': 235 ↛ 236line 235 didn't jump to line 236, because the condition on line 235 was never true
236 rl = list(pl)
237 random.shuffle(rl)
238 rl = [[i] for i in rl if i is not me]
240 elif tn == 't_One': 240 ↛ 241line 240 didn't jump to line 241, because the condition on line 240 was never true
241 rl = list(pl)
242 random.shuffle(rl)
243 rl = [[i] for i in rl]
245 elif tn == 't_All': 245 ↛ 246line 245 didn't jump to line 246, because the condition on line 245 was never true
246 rl = list(pl)
247 rl.remove(me)
248 rl = [rl]
250 elif tn == 't_AllInclusive': 250 ↛ 251line 250 didn't jump to line 251, because the condition on line 250 was never true
251 rl = [pl]
253 elif tn == '_t_OtherLessEqThanN': 253 ↛ 254line 253 didn't jump to line 254, because the condition on line 253 was never true
254 n = target._for_test_OtherLessEqThanN
255 rl = []
256 for i in range(n+1):
257 rl.extend(list(P(pl, i)))
258 random.shuffle(rl)
260 elif tn == 't_OneOrNone': 260 ↛ 261line 260 didn't jump to line 261, because the condition on line 260 was never true
261 rl = [[i] for i in pl]
262 rl.append([])
263 random.shuffle(rl)
265 elif tn == '_t_OtherN': 265 ↛ 266line 265 didn't jump to line 266, because the condition on line 265 was never true
266 n = target._for_test_OtherN
267 rl = list(P(pl, n))
268 random.shuffle(rl)
269 rl = [list(i) for i in rl]
270 else:
271 rl = []
272 for i in range(3): # HACK: should be enough
273 rl.extend(list(P(pl, i)))
274 random.shuffle(rl)
276 return rl