jat.py 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. #!/usr/bin/python2
  2. import yaml
  3. import sys
  4. class ParsingError(ValueError):
  5. pass
  6. class _EventGroup(object):
  7. def __init__(self, events):
  8. if not events:
  9. raise ValueError("no events in event group")
  10. self._events = events
  11. @property
  12. def all_events(self):
  13. return self._events
  14. @property
  15. def asserts(self):
  16. return [e for e in self._events if e.is_assert]
  17. @property
  18. def fails(self):
  19. return [e for e in self._events if e.etype == 'ASSERT.FAIL']
  20. @property
  21. def passes(self):
  22. return [e for e in self._events if e.etype == 'ASSERT.PASS']
  23. @property
  24. def events(self):
  25. return self._events
  26. @property
  27. def lints(self):
  28. lints = []
  29. for e in self._events:
  30. lints += e.lints
  31. return lints
  32. class EventTree(object):
  33. def __init__(self, events, head=None):
  34. self.children = []
  35. self.rest = [e for e in events]
  36. self.head = head
  37. while self.rest:
  38. if self.peek().startswith('SESSION.'):
  39. self.pop()
  40. elif self.peek() == 'PHASE.START':
  41. phead = self.pop()
  42. pbody = self.pull_branch('PHASE.END')
  43. self.children.append(Phase(pbody, phead))
  44. elif self.peek().startswith('MESSAGE.'):
  45. self.children.append(self.pop())
  46. elif self.peek().startswith('ASSERT.'):
  47. self.children.append(self.pop())
  48. def peek(self):
  49. return self.rest[0].etype
  50. def pop(self):
  51. return self.rest.pop(0)
  52. def pull_branch(self, final_etype):
  53. out = []
  54. while self.rest:
  55. if self.peek() == final_etype:
  56. self.pop()
  57. return out
  58. else:
  59. out.append(self.pop())
  60. self.lints += [Lint('could not find: %s' % final_etype, out)]
  61. return out
  62. @property
  63. def lints(self):
  64. lints = []
  65. for e in self.children:
  66. lints += e.lints
  67. return lints
  68. class Phase(EventTree):
  69. is_phase = True
  70. @property
  71. def id(self):
  72. return self.head._rawdata['phase']['id']
  73. @property
  74. def name(self):
  75. return self.head._rawdata['phase']['name']
  76. @property
  77. def type(self):
  78. return self.head._rawdata['phase']['type']
  79. @property
  80. def verdict(self):
  81. out = 'UNKNOWN'
  82. for e in self.children:
  83. if e.is_assert:
  84. if e.verdict == 'PASS':
  85. out = 'PASS'
  86. elif e.verdict == 'FAIL':
  87. return 'FAIL'
  88. return out
  89. class Event(object):
  90. is_assert = False
  91. is_message = False
  92. is_phase = False
  93. def __str__(self):
  94. return str(self._rawdata)
  95. def __init__(self, rawdata):
  96. self._rawdata = rawdata
  97. self.etype = rawdata['etype']
  98. self.stamp = rawdata['stamp']
  99. self.data = rawdata.get('data', dict())
  100. @staticmethod
  101. def from_data(data):
  102. etype = data['etype']
  103. if etype in ['ASSERT.PASS', 'ASSERT.FAIL']:
  104. cls = AssertEvent
  105. elif etype in ['PHASE.START', 'PHASE.END']:
  106. cls = StructureInfoEvent
  107. elif etype in ['SESSION.START', 'SESSION.RELOAD', 'SESSION.END']:
  108. cls = StructureInfoEvent
  109. elif etype == 'MESSAGE.INFO':
  110. cls = MessageEvent
  111. elif etype == 'MESSAGE.WARNING':
  112. cls = MessageEvent
  113. elif etype == 'MESSAGE.ERROR':
  114. cls = MessageEvent
  115. elif etype == 'MESSAGE.PROMISE':
  116. cls = PromiseEvent
  117. else:
  118. raise ParsingError("unknown event type: %s" % etype)
  119. return cls(data)
  120. @property
  121. def lints(self):
  122. return []
  123. class MessageEvent(Event):
  124. is_message = True
  125. @property
  126. def message(self):
  127. return self._rawdata['message']
  128. @property
  129. def severity(self):
  130. return self.etype.split('.')[1]
  131. @property
  132. def lints(self):
  133. if self.severity == 'WARNING':
  134. return [Lint("test warning", self._rawdata)]
  135. elif self.severity == 'ERROR':
  136. return [Lint("test error", self._rawdata)]
  137. else:
  138. return []
  139. class PromiseEvent(Event):
  140. @property
  141. def lints(self):
  142. return [Lint("promises are not supported (yet)", self._rawdata)]
  143. class StructureInfoEvent(Event):
  144. pass
  145. class AssertEvent(Event):
  146. is_assert = True
  147. @property
  148. def caseid(self):
  149. return self._rawdata['caseid']
  150. @property
  151. def hint(self):
  152. return self._rawdata['hint']
  153. @property
  154. def verdict(self):
  155. return self.etype.split('.')[1]
  156. class EventLog(_EventGroup):
  157. """
  158. Session event log (all events from session)
  159. """
  160. @classmethod
  161. def from_data(cls, data):
  162. return cls([Event.from_data(e) for e in data])
  163. class Lint(object):
  164. """
  165. Lint-like warning coming from log parsing (eg. unclosed phase)
  166. """
  167. def __init__(self, msg, data):
  168. self.msg = msg
  169. self._data = data
  170. class Session(object):
  171. def __init__(self, doc):
  172. self._doc = doc
  173. self._format = doc['format']
  174. if not self._format.startswith('jat/0.'):
  175. raise ParsingError('unsupported log format: %s' % self._format)
  176. self.jat_version = doc['jat_version']
  177. self.start = doc['start']
  178. self.sessid = doc.get('sessid', 'noid')
  179. self.finalized = doc.get('finalized', False)
  180. self.end = doc.get('end')
  181. self.lints = []
  182. self.eventlog = EventLog.from_data(doc['events'])
  183. self.lints += self.eventlog.lints
  184. self.eventtree = EventTree(self.eventlog._events)
  185. self.lints += self.eventtree.lints
  186. if not self.finalized:
  187. self.lints.append(Lint("session not finalized", self.sessid))
  188. def load(fpath):
  189. with open(fpath) as ylog:
  190. sessions = [Session(doc) for doc in yaml.load_all(ylog)]
  191. return sessions