jat.py 6.0KB

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