Bladeren bron

Release: 1c229da821

master
Dirk Alders 4 jaren geleden
bovenliggende
commit
98fea3a4d4
3 gewijzigde bestanden met toevoegingen van 9380 en 0 verwijderingen
  1. 356
    0
      __init__.py
  2. 9024
    0
      _testresults_/unittest.json
  3. BIN
      _testresults_/unittest.pdf

+ 356
- 0
__init__.py Bestand weergeven

@@ -0,0 +1,356 @@
1
+#!/usr/bin/env python
2
+# -*- coding: utf-8 -*-
3
+#
4
+"""
5
+report (Report Module)
6
+======================
7
+
8
+**Author:**
9
+
10
+* Dirk Alders <sudo-dirk@mount-mockery.de>
11
+
12
+**Description:**
13
+
14
+    The Module is designed to help with python logging and to support some handlers for logging to memory.
15
+
16
+**Submodules:**
17
+
18
+* :class:`report.collectingHandler`
19
+* :class:`report.collectingRingHandler`
20
+* :class:`report.collectingTestcaseHandler`
21
+* :func:`report.consoleLoggingConfigure`
22
+* :class:`report.testSession`
23
+
24
+
25
+**Unittest:**
26
+
27
+        See also the :download:`unittest <../../report/_testresults_/unittest.pdf>` documentation.
28
+"""
29
+__DEPENDENCIES__ = []
30
+
31
+import collections
32
+import logging
33
+from logging.config import dictConfig
34
+import os
35
+import sys
36
+
37
+logger_name = 'REPORT'
38
+logger = logging.getLogger(logger_name)
39
+
40
+__DESCRIPTION__ = """The Module {\\tt %s} is designed to help with python logging and to support some handlers for logging to memory.
41
+For more Information read the sphinx documentation.""" % __name__.replace('_', '\_')
42
+"""The Module Description"""
43
+__INTERPRETER__ = (2, 3, )
44
+"""The Tested Interpreter-Versions"""
45
+
46
+SHORT_FMT = "%(asctime)s: %(name)s - %(levelname)s - %(message)s"
47
+""" A short formatter including the most important information"""
48
+LONG_FMT = """~~~~(%(levelname)-10s)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
49
+File "%(pathname)s", line %(lineno)d, in %(funcName)s
50
+%(asctime)s: %(name)s- %(message)s
51
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"""
52
+""" A long formatter which results in links to the source code inside Eclipse"""
53
+MAX_FMT = """
54
+%(name)s
55
+%(levelno)s
56
+%(levelname)s
57
+%(pathname)s
58
+%(filename)s
59
+%(module)s
60
+%(lineno)d
61
+%(funcName)s
62
+%(created)f
63
+%(asctime)s
64
+%(msecs)d
65
+%(relativeCreated)d
66
+%(thread)d
67
+%(threadName)s
68
+%(process)d
69
+%(message)s"""
70
+DEFAULT_FMT = LONG_FMT
71
+"""The default formatstring"""
72
+
73
+
74
+class collectingHandler(logging.Handler):
75
+    MY_LOGS = []
76
+
77
+    def __init__(self):
78
+        logging.Handler.__init__(self)
79
+        self.setFormatter(logging.Formatter(MAX_FMT))
80
+        self.setLevel(logging.DEBUG)
81
+
82
+    def emit(self, record):
83
+        self.format(record)
84
+        self.MY_LOGS.append(record.__dict__)
85
+
86
+    def make_independent(self):
87
+        self.MY_LOGS = []
88
+
89
+    def get_logs(self):
90
+        rv = []
91
+        while len(self.MY_LOGS) > 0:
92
+            rv.append(self.MY_LOGS.pop(0))
93
+        return rv
94
+
95
+    def get_str(self, logs=None, fmt=SHORT_FMT):
96
+        logs = logs or self.MY_LOGS
97
+        return '\n'.join([fmt % log for log in logs])
98
+
99
+    def __len__(self):
100
+        return len(self.MY_LOGS)
101
+
102
+    def __str__(self):
103
+        return self.get_str(self.MY_LOGS)
104
+
105
+
106
+class collectingRingHandler(collectingHandler):
107
+    MY_LOGS = collections.deque([], 10)
108
+
109
+    def __init__(self, max_logs=None):
110
+        collectingHandler.__init__(self)
111
+        if max_logs is not None and max_logs != self.MY_LOGS.maxlen:
112
+            self.MY_LOGS.__init__(list(self.MY_LOGS), max_logs)
113
+
114
+    def make_independent(self):
115
+        self.MY_LOGS = collections.deque([], self.MY_LOGS.maxlen)
116
+
117
+    def get_logs(self):
118
+        return list(self.MY_LOGS)
119
+
120
+
121
+TCEL_SINGLE = 0
122
+"""Testcase level (smoke), this is just a rough test for the main functionality"""
123
+TCEL_SMOKE = 10
124
+"""Testcase level (smoke), this is just a rough test for the main functionality"""
125
+TCEL_SHORT = 50
126
+"""Testcase level (short), this is a short test for an extended functionality"""
127
+TCEL_FULL = 90
128
+"""Testcase level (full), this is a complete test for the full functionality"""
129
+TCEL_NAMES = {
130
+    TCEL_SINGLE: 'Single Test',
131
+    TCEL_SMOKE: 'Smoke Test (Minumum subset)',
132
+    TCEL_SHORT: 'Short Test (Subset)',
133
+    TCEL_FULL: 'Full Test (all defined tests)'
134
+}
135
+"""Dictionary for resolving the test case levels (TCL) to a (human readable) name"""
136
+
137
+TCEL_REVERSE_NAMED = {
138
+    'short': TCEL_SHORT,
139
+    'smoke': TCEL_SMOKE,
140
+    'single': TCEL_SINGLE,
141
+    'full': TCEL_FULL,
142
+}
143
+"""Dictionary for resolving named test case levels (TCL) to test case level number"""
144
+
145
+
146
+class collectingTestcaseHandler(collectingHandler):
147
+    MY_LOGS = []
148
+
149
+    def emit(self, record):
150
+        self.format(record)
151
+        self.MY_LOGS.append(record.__dict__)
152
+        self.MY_LOGS[-1]['moduleLogger'] = collectingHandler().get_logs()
153
+
154
+
155
+def appLoggingConfigure(basepath, target, log_name_lvl=[], fmt=SHORT_FMT, ring_logs=None):
156
+    target_handlers = ['main', 'logwarn']
157
+    # define handler
158
+    #
159
+    if target == 'stdout':
160
+        handler = dict(main={
161
+            'level': 'DEBUG',
162
+            'formatter': 'format',
163
+            'class': 'logging.StreamHandler',
164
+            'stream': 'ext://sys.stdout',
165
+        })
166
+    elif target == 'logfile':
167
+        handler = dict(main={
168
+            'level': 'DEBUG',
169
+            'formatter': 'format',
170
+            'class': 'logging.handlers.RotatingFileHandler',
171
+            'filename': os.path.join(basepath, 'messages.log'),
172
+            'mode': 'a',
173
+            'maxBytes': 10485760,
174
+            'backupCount': 7
175
+        })
176
+    else:
177
+        handler = dict(my_handler={
178
+            'level': 'DEBUG',
179
+            'formatter': 'my_format',
180
+            'class': 'logging.NullHandler',
181
+        })
182
+    if ring_logs is not None:
183
+        target_handlers.append('ring')
184
+        handler['ring'] = {
185
+            'class': 'report.collectingRingHandler',
186
+            'max_logs': ring_logs,
187
+        }
188
+    handler['logwarn'] = {
189
+        'level': 'WARNING',
190
+        'formatter': 'long',
191
+        'class': 'logging.handlers.RotatingFileHandler',
192
+        'filename': os.path.join(basepath, 'messages.warn'),
193
+        'mode': 'a',
194
+        'maxBytes': 10485760,
195
+        'backupCount': 2
196
+    }
197
+    # define loggers
198
+    #
199
+    loggers = {}
200
+    for name, lvl in log_name_lvl:
201
+        loggers[name] = {
202
+            'handlers': target_handlers,
203
+            'level': lvl,
204
+            'propagate': False
205
+        }
206
+    # configure logging
207
+    #
208
+    dictConfig(dict(
209
+        version=1,
210
+        formatters={
211
+            'long': {
212
+                'format': LONG_FMT
213
+            },
214
+            'format': {
215
+                'format': fmt,
216
+            },
217
+        },
218
+        handlers=handler,
219
+        loggers=loggers,
220
+    ))
221
+
222
+
223
+class testSession(dict):
224
+    KEY_NAME = 'name'
225
+    KEY_FAILED_TESTS = 'number_of_failed_tests'
226
+    KEY_POSSIBLY_FAILED_TESTS = 'number_of_possibly_failed_tests'
227
+    KEY_SUCCESS_TESTS = 'number_of_successfull_tests'
228
+    KEY_ALL_TESTS = 'number_of_tests'
229
+    KEY_EXEC_LVL = 'testcase_execution_level'
230
+    KEY_EXEC_NAMES = 'testcase_names'
231
+    KEY_LVL_NAMES = 'level_names'
232
+    KEY_TESTCASELIST = 'testcases'
233
+    KEY_UID_LIST = 'uid_list_sorted'
234
+    #
235
+    DEFAULT_BASE_DATA = {
236
+        KEY_NAME: 'Default Testsession name',
237
+        KEY_FAILED_TESTS: 0,
238
+        KEY_POSSIBLY_FAILED_TESTS: 0,
239
+        KEY_FAILED_TESTS: 0,
240
+        KEY_SUCCESS_TESTS: 0,
241
+        KEY_ALL_TESTS: 0,
242
+        KEY_EXEC_LVL: TCEL_FULL,
243
+        KEY_EXEC_NAMES: TCEL_NAMES,
244
+    }
245
+
246
+    def __init__(self, module_names=[], **kwargs):
247
+        dict.__init__(self, time_consumption=0.)
248
+        self.__testcase__ = None
249
+        self.__set_base_data__(**kwargs)
250
+        self.__configure_logging__(module_names)
251
+
252
+    def __set_base_data__(self, **kwargs):
253
+        for key in set([key for key in self.DEFAULT_BASE_DATA.keys()] + [key for key in kwargs.keys()]):
254
+            self[key] = kwargs.get(key, self.DEFAULT_BASE_DATA.get(key))
255
+        self[self.KEY_TESTCASELIST] = {}
256
+        self[self.KEY_UID_LIST] = []
257
+
258
+    def __configure_logging__(self, module_names):
259
+        #
260
+        # Configure logging for testSession
261
+        #
262
+        logging_config = dict(
263
+            version=1,
264
+            formatters={
265
+                'short': {
266
+                    'format': SHORT_FMT,
267
+                },
268
+                'long': {
269
+                    'format': LONG_FMT,
270
+                },
271
+            },
272
+            handlers={
273
+                'console': {
274
+                    'level': 'DEBUG',
275
+                    'class': 'logging.NullHandler',
276
+                    'formatter': 'short',
277
+                },
278
+                'module_logs': {
279
+                    'level': 'DEBUG',
280
+                    'class': 'report.collectingHandler',
281
+                    'formatter': 'short',
282
+                },
283
+                'testcase_logs': {
284
+                    'level': 'DEBUG',
285
+                    'class': 'report.collectingTestcaseHandler',
286
+                    'formatter': 'short',
287
+                },
288
+            },
289
+            loggers=self.__module_loggers__(module_names),
290
+        )
291
+        dictConfig(logging_config)
292
+
293
+    def __module_loggers__(self, module_names):
294
+        rv = {}
295
+        rv['__tLogger__'] = dict(handlers=['console', 'testcase_logs'], level='DEBUG', propagate=False)
296
+        for name in module_names + ['__mLogger__']:
297
+            rv[name] = dict(handlers=['console', 'module_logs'], level='DEBUG', propagate=False)
298
+        return rv
299
+
300
+    def testCase(self, name, testcase_execution_level, test_method, *args, **kwargs):
301
+        if testcase_execution_level <= self[self.KEY_EXEC_LVL]:
302
+            tLogger = logging.getLogger('__tLogger__')
303
+            tHandler = collectingTestcaseHandler()
304
+            if len(tHandler.MY_LOGS) > 0:
305
+                raise AttributeError("Testcaselogger shall be empty after closing testcase!")
306
+            tLogger._log(logging.DEBUG, name, None)
307
+            if len(tHandler.MY_LOGS) != 1:
308
+                raise AttributeError("Testcaselogger shall have only one entry for the main testcase (temporary)!")
309
+            self.__testcase__ = tHandler.get_logs()[0]
310
+            test_method(logging.getLogger('__tLogger__'), *args, **kwargs)
311
+            self.__close_active_testcase__()
312
+
313
+    def __close_active_testcase__(self):
314
+        if self.__testcase__ is not None:
315
+            name = self.__testcase__.get('message')
316
+            #
317
+            # Add testcase
318
+            #
319
+            tch = collectingTestcaseHandler()
320
+            self.__testcase__['testcaseLogger'] = tch.get_logs()
321
+            if name in self[self.KEY_TESTCASELIST]:
322
+                raise AttributeError("Testcase named %s already exists" % name)
323
+            self[self.KEY_TESTCASELIST][name] = self.__testcase__
324
+            self[self.KEY_UID_LIST].append(name)
325
+            #
326
+            # Adapt testcase data
327
+            #
328
+            self[self.KEY_TESTCASELIST][name]['levelno'] = 0
329
+            self[self.KEY_TESTCASELIST][name]['time_consumption'] = 0.
330
+            for teststep in self[self.KEY_TESTCASELIST][name]['testcaseLogger']:
331
+                # store maximum level to testcase
332
+                if teststep.get('levelno') > self[self.KEY_TESTCASELIST][name]['levelno']:
333
+                    self[self.KEY_TESTCASELIST][name]['levelno'] = teststep.get('levelno')
334
+                    self[self.KEY_TESTCASELIST][name]['levelname'] = teststep.get('levelname')
335
+                # store time_consumption for teststep
336
+                try:
337
+                    teststep['time_consumption'] = teststep['created'] - teststep['moduleLogger'][-1]['created']
338
+                except IndexError:
339
+                    teststep['time_consumption'] = 0.
340
+                # Increment testcase time_comsumption
341
+            # Increment testcase counters
342
+            #
343
+            self[self.KEY_ALL_TESTS] += 1
344
+            if self[self.KEY_TESTCASELIST][name]['levelno'] <= logging.INFO:
345
+                self[self.KEY_SUCCESS_TESTS] += 1
346
+            elif self[self.KEY_TESTCASELIST][name]['levelno'] >= logging.ERROR:
347
+                self[self.KEY_FAILED_TESTS] += 1
348
+            else:
349
+                self[self.KEY_POSSIBLY_FAILED_TESTS] += 1
350
+            # Set testcase time and time_consumption
351
+            self[self.KEY_TESTCASELIST][name]['time_start'] = self.__testcase__['asctime']
352
+            self[self.KEY_TESTCASELIST][name]['time_finished'] = teststep['asctime']
353
+            self[self.KEY_TESTCASELIST][name]['time_consumption'] = teststep['created'] - self.__testcase__['created']
354
+            # Set testcase time consumption
355
+            self['time_consumption'] += self[self.KEY_TESTCASELIST][name]['time_consumption']
356
+        self.__testcase__ = None

+ 9024
- 0
_testresults_/unittest.json
Diff onderdrukt omdat het te groot bestand
Bestand weergeven


BIN
_testresults_/unittest.pdf Bestand weergeven


Laden…
Annuleren
Opslaan