unitest   C
last analyzed

Complexity

Total Complexity 54

Size/Duplication

Total Lines 463
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 317
dl 0
loc 463
rs 6.4799
c 0
b 0
f 0
wmc 54

30 Methods

Rating   Name   Duplication   Size   Complexity  
A TestGlances.test_097_attribute() 0 22 1
A TestGlances.test_004_load() 0 12 2
A TestGlances.test_012_ip() 0 6 1
A TestGlances.test_015_subsample() 0 11 2
A TestGlances.test_000_update() 0 19 3
A TestGlances.test_010_processes() 0 11 1
A TestGlances.test_009_fs() 0 7 1
A TestGlances.test_017_programs() 0 7 1
A TestGlances.test_002_system() 0 9 2
A TestGlances.test_008_diskio() 0 6 1
A TestGlances.test_013_gpu() 0 7 1
A TestGlances.test_014_sorted_stats() 0 27 2
A TestGlances.test_016_hddsmart() 0 22 4
A TestGlances.setUp() 0 3 1
A TestGlances.test_013_irq() 0 7 1
A TestGlances.test_007_network() 0 6 1
A TestGlances.test_003_cpu() 0 12 2
A TestGlances.test_099_output_bars_must_be_between_0_and_100_percent() 0 20 1
A TestGlances.test_095_methods() 0 9 3
A TestGlances.test_006_swap() 0 11 2
A TestGlances.test_094_thresholds() 0 16 1
A TestGlances.test_018_string_value_to_float() 0 9 1
A TestGlances.test_098_history() 0 17 1
A TestGlances.test_011_folders() 0 7 1
A TestGlances.test_001_plugins() 0 7 2
A TestGlances.test_096_views() 0 9 2
A TestGlances.test_005_mem() 0 11 2
A TestGlances.test_100_secure() 0 10 2
B TestGlances.test_200_memory_leak() 0 42 8
A TestGlances.test_999_the_end() 0 5 1

How to fix   Complexity   

Complexity

Complex classes like unitest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
#
4
# Glances - An eye on your system
5
#
6
# SPDX-FileCopyrightText: 2022 Nicolas Hennion <[email protected]>
7
#
8
# SPDX-License-Identifier: LGPL-3.0-only
9
#
10
11
"""Glances unitary tests suite."""
12
13
#
14
# ./venv/bin/python unitest.py
15
#
16
17
import time
18
import unittest
19
20
from glances.main import GlancesMain
21
from glances.stats import GlancesStats
22
from glances import __version__
23
from glances.globals import WINDOWS, LINUX
24
from glances.outputs.glances_bars import Bar
25
from glances.thresholds import GlancesThresholdOk
26
from glances.thresholds import GlancesThresholdCareful
27
from glances.thresholds import GlancesThresholdWarning
28
from glances.thresholds import GlancesThresholdCritical
29
from glances.thresholds import GlancesThresholds
30
from glances.plugins.glances_plugin import GlancesPlugin
31
from glances.programs import processes_to_programs
32
from glances.compat import subsample, range, string_value_to_float
33
from glances.secure import secure_popen
34
from glances.compat import PY3
35
36
if PY3:
37
    pass
38
39
# Global variables
40
# =================
41
42
# Init Glances core
43
core = GlancesMain()
44
test_config = core.get_config()
45
test_args = core.get_args()
46
47
# Init Glances stats
48
stats = GlancesStats(config=test_config,
49
                     args=test_args)
50
51
# Unitest class
52
# ==============
53
print('Unitary tests for Glances %s' % __version__)
54
55
56
class TestGlances(unittest.TestCase):
57
    """Test Glances class."""
58
59
    def setUp(self):
60
        """The function is called *every time* before test_*."""
61
        print('\n' + '=' * 78)
62
63
    def test_000_update(self):
64
        """Update stats (mandatory step for all the stats).
65
66
        The update is made twice (for rate computation).
67
        """
68
        print('INFO: [TEST_000] Test the stats update function')
69
        try:
70
            stats.update()
71
        except Exception as e:
72
            print('ERROR: Stats update failed: %s' % e)
73
            self.assertTrue(False)
74
        time.sleep(1)
75
        try:
76
            stats.update()
77
        except Exception as e:
78
            print('ERROR: Stats update failed: %s' % e)
79
            self.assertTrue(False)
80
81
        self.assertTrue(True)
82
83
    def test_001_plugins(self):
84
        """Check mandatory plugins."""
85
        plugins_to_check = ['system', 'cpu', 'load', 'mem', 'memswap', 'network', 'diskio', 'fs']
86
        print('INFO: [TEST_001] Check the mandatory plugins list: %s' % ', '.join(plugins_to_check))
87
        plugins_list = stats.getPluginsList()
88
        for plugin in plugins_to_check:
89
            self.assertTrue(plugin in plugins_list)
90
91
    def test_002_system(self):
92
        """Check SYSTEM plugin."""
93
        stats_to_check = ['hostname', 'os_name']
94
        print('INFO: [TEST_002] Check SYSTEM stats: %s' % ', '.join(stats_to_check))
95
        stats_grab = stats.get_plugin('system').get_raw()
96
        for stat in stats_to_check:
97
            # Check that the key exist
98
            self.assertTrue(stat in stats_grab, msg='Cannot find key: %s' % stat)
99
        print('INFO: SYSTEM stats: %s' % stats_grab)
100
101
    def test_003_cpu(self):
102
        """Check CPU plugin."""
103
        stats_to_check = ['system', 'user', 'idle']
104
        print('INFO: [TEST_003] Check mandatory CPU stats: %s' % ', '.join(stats_to_check))
105
        stats_grab = stats.get_plugin('cpu').get_raw()
106
        for stat in stats_to_check:
107
            # Check that the key exist
108
            self.assertTrue(stat in stats_grab, msg='Cannot find key: %s' % stat)
109
            # Check that % is > 0 and < 100
110
            self.assertGreaterEqual(stats_grab[stat], 0)
111
            self.assertLessEqual(stats_grab[stat], 100)
112
        print('INFO: CPU stats: %s' % stats_grab)
113
114
    @unittest.skipIf(WINDOWS, "Load average not available on Windows")
115
    def test_004_load(self):
116
        """Check LOAD plugin."""
117
        stats_to_check = ['cpucore', 'min1', 'min5', 'min15']
118
        print('INFO: [TEST_004] Check LOAD stats: %s' % ', '.join(stats_to_check))
119
        stats_grab = stats.get_plugin('load').get_raw()
120
        for stat in stats_to_check:
121
            # Check that the key exist
122
            self.assertTrue(stat in stats_grab, msg='Cannot find key: %s' % stat)
123
            # Check that % is > 0
124
            self.assertGreaterEqual(stats_grab[stat], 0)
125
        print('INFO: LOAD stats: %s' % stats_grab)
126
127
    def test_005_mem(self):
128
        """Check MEM plugin."""
129
        stats_to_check = ['available', 'used', 'free', 'total']
130
        print('INFO: [TEST_005] Check MEM stats: %s' % ', '.join(stats_to_check))
131
        stats_grab = stats.get_plugin('mem').get_raw()
132
        for stat in stats_to_check:
133
            # Check that the key exist
134
            self.assertTrue(stat in stats_grab, msg='Cannot find key: %s' % stat)
135
            # Check that % is > 0
136
            self.assertGreaterEqual(stats_grab[stat], 0)
137
        print('INFO: MEM stats: %s' % stats_grab)
138
139
    def test_006_swap(self):
140
        """Check MEMSWAP plugin."""
141
        stats_to_check = ['used', 'free', 'total']
142
        print('INFO: [TEST_006] Check SWAP stats: %s' % ', '.join(stats_to_check))
143
        stats_grab = stats.get_plugin('memswap').get_raw()
144
        for stat in stats_to_check:
145
            # Check that the key exist
146
            self.assertTrue(stat in stats_grab, msg='Cannot find key: %s' % stat)
147
            # Check that % is > 0
148
            self.assertGreaterEqual(stats_grab[stat], 0)
149
        print('INFO: SWAP stats: %s' % stats_grab)
150
151
    def test_007_network(self):
152
        """Check NETWORK plugin."""
153
        print('INFO: [TEST_007] Check NETWORK stats')
154
        stats_grab = stats.get_plugin('network').get_raw()
155
        self.assertTrue(type(stats_grab) is list, msg='Network stats is not a list')
156
        print('INFO: NETWORK stats: %s' % stats_grab)
157
158
    def test_008_diskio(self):
159
        """Check DISKIO plugin."""
160
        print('INFO: [TEST_008] Check DISKIO stats')
161
        stats_grab = stats.get_plugin('diskio').get_raw()
162
        self.assertTrue(type(stats_grab) is list, msg='DiskIO stats is not a list')
163
        print('INFO: diskio stats: %s' % stats_grab)
164
165
    def test_009_fs(self):
166
        """Check File System plugin."""
167
        # stats_to_check = [ ]
168
        print('INFO: [TEST_009] Check FS stats')
169
        stats_grab = stats.get_plugin('fs').get_raw()
170
        self.assertTrue(type(stats_grab) is list, msg='FileSystem stats is not a list')
171
        print('INFO: FS stats: %s' % stats_grab)
172
173
    def test_010_processes(self):
174
        """Check Process plugin."""
175
        # stats_to_check = [ ]
176
        print('INFO: [TEST_010] Check PROCESS stats')
177
        stats_grab = stats.get_plugin('processcount').get_raw()
178
        # total = stats_grab['total']
179
        self.assertTrue(type(stats_grab) is dict, msg='Process count stats is not a dict')
180
        print('INFO: PROCESS count stats: %s' % stats_grab)
181
        stats_grab = stats.get_plugin('processlist').get_raw()
182
        self.assertTrue(type(stats_grab) is list, msg='Process count stats is not a list')
183
        print('INFO: PROCESS list stats: %s items in the list' % len(stats_grab))
184
        # Check if number of processes in the list equal counter
185
        # self.assertEqual(total, len(stats_grab))
186
187
    def test_011_folders(self):
188
        """Check File System plugin."""
189
        # stats_to_check = [ ]
190
        print('INFO: [TEST_011] Check FOLDER stats')
191
        stats_grab = stats.get_plugin('folders').get_raw()
192
        self.assertTrue(type(stats_grab) is list, msg='Folders stats is not a list')
193
        print('INFO: Folders stats: %s' % stats_grab)
194
195
    def test_012_ip(self):
196
        """Check IP plugin."""
197
        print('INFO: [TEST_012] Check IP stats')
198
        stats_grab = stats.get_plugin('ip').get_raw()
199
        self.assertTrue(type(stats_grab) is dict, msg='IP stats is not a dict')
200
        print('INFO: IP stats: %s' % stats_grab)
201
202
    @unittest.skipIf(not LINUX, "IRQs available only on Linux")
203
    def test_013_irq(self):
204
        """Check IRQ plugin."""
205
        print('INFO: [TEST_013] Check IRQ stats')
206
        stats_grab = stats.get_plugin('irq').get_raw()
207
        self.assertTrue(type(stats_grab) is list, msg='IRQ stats is not a list')
208
        print('INFO: IRQ stats: %s' % stats_grab)
209
210
    @unittest.skipIf(not LINUX, "GPU available only on Linux")
211
    def test_013_gpu(self):
212
        """Check GPU plugin."""
213
        print('INFO: [TEST_014] Check GPU stats')
214
        stats_grab = stats.get_plugin('gpu').get_raw()
215
        self.assertTrue(type(stats_grab) is list, msg='GPU stats is not a list')
216
        print('INFO: GPU stats: %s' % stats_grab)
217
218
    def test_014_sorted_stats(self):
219
        """Check sorted stats method."""
220
        print('INFO: [TEST_015] Check sorted stats method')
221
        aliases = {
222
            "key2": "alias11",
223
            "key5": "alias2",
224
        }
225
        unsorted_stats = [
226
            {"key": "key4"},
227
            {"key": "key2"},
228
            {"key": "key5"},
229
            {"key": "key21"},
230
            {"key": "key3"},
231
        ]
232
233
        gp = GlancesPlugin()
234
        gp.get_key = lambda: "key"
235
        gp.has_alias = aliases.get
236
        gp.stats = unsorted_stats
237
238
        sorted_stats = gp.sorted_stats()
239
        self.assertEqual(len(sorted_stats), 5)
240
        self.assertEqual(sorted_stats[0]["key"], "key5")
241
        self.assertEqual(sorted_stats[1]["key"], "key2")
242
        self.assertEqual(sorted_stats[2]["key"], "key3")
243
        self.assertEqual(sorted_stats[3]["key"], "key4")
244
        self.assertEqual(sorted_stats[4]["key"], "key21")
245
246
    def test_015_subsample(self):
247
        """Test subsampling function."""
248
        print('INFO: [TEST_015] Subsampling')
249
        for l_test in [([1, 2, 3], 4),
250
                       ([1, 2, 3, 4], 4),
251
                       ([1, 2, 3, 4, 5, 6, 7], 4),
252
                       ([1, 2, 3, 4, 5, 6, 7, 8], 4),
253
                       (list(range(1, 800)), 4),
254
                       (list(range(1, 8000)), 800)]:
255
            l_subsample = subsample(l_test[0], l_test[1])
256
            self.assertLessEqual(len(l_subsample), l_test[1])
257
258
    def test_016_hddsmart(self):
259
        """Check hard disk SMART data plugin."""
260
        try:
261
            from glances.compat import is_admin
262
        except ImportError:
263
            print("INFO: [TEST_016] pySMART not found, not running SMART plugin test")
264
            return
265
266
        stat = 'DeviceName'
267
        print('INFO: [TEST_016] Check SMART stats: {}'.format(stat))
268
        stats_grab = stats.get_plugin('smart').get_raw()
269
        if not is_admin():
270
            print("INFO: Not admin, SMART list should be empty")
271
            assert len(stats_grab) == 0
272
        elif stats_grab == {}:
273
            print("INFO: Admin but SMART list is empty")
274
            assert len(stats_grab) == 0
275
        else:
276
            print(stats_grab)
277
            self.assertTrue(stat in stats_grab[0].keys(), msg='Cannot find key: %s' % stat)
278
279
        print('INFO: SMART stats: %s' % stats_grab)
280
281
    def test_017_programs(self):
282
        """Check Programs function (it's not a plugin)."""
283
        # stats_to_check = [ ]
284
        print('INFO: [TEST_017] Check PROGRAM stats')
285
        stats_grab = processes_to_programs(stats.get_plugin('processlist').get_raw())
286
        self.assertTrue(type(stats_grab) is list, msg='Programs stats is not a list')
287
        print('INFO: PROGRAM list stats: %s items in the list' % len(stats_grab))
288
        # Check if number of processes in the list equal counter
289
        # self.assertEqual(total, len(stats_grab))
290
291
    def test_018_string_value_to_float(self):
292
        """Check string_value_to_float function"""
293
        print('INFO: [TEST_018] Check string_value_to_float function')
294
        self.assertEqual(string_value_to_float('32kB'), 32000.0)
295
        self.assertEqual(string_value_to_float('32 KB'), 32000.0)
296
        self.assertEqual(string_value_to_float('15.5MB'), 15500000.0)
297
        self.assertEqual(string_value_to_float('25.9'), 25.9)
298
        self.assertEqual(string_value_to_float('12'), 12)
299
        self.assertEqual(string_value_to_float('--'), None)
300
301
    def test_094_thresholds(self):
302
        """Test thresholds classes"""
303
        print('INFO: [TEST_094] Thresholds')
304
        ok = GlancesThresholdOk()
305
        careful = GlancesThresholdCareful()
306
        warning = GlancesThresholdWarning()
307
        critical = GlancesThresholdCritical()
308
        self.assertTrue(ok < careful)
309
        self.assertTrue(careful < warning)
310
        self.assertTrue(warning < critical)
311
        self.assertFalse(ok > careful)
312
        self.assertEqual(ok, ok)
313
        self.assertEqual(str(ok), 'OK')
314
        thresholds = GlancesThresholds()
315
        thresholds.add('cpu_percent', 'OK')
316
        self.assertEqual(thresholds.get(stat_name='cpu_percent').description(), 'OK')
317
318
    def test_095_methods(self):
319
        """Test mandatories methods"""
320
        print('INFO: [TEST_095] Mandatories methods')
321
        mandatories_methods = ['reset', 'update']
322
        plugins_list = stats.getPluginsList()
323
        for plugin in plugins_list:
324
            for method in mandatories_methods:
325
                self.assertTrue(hasattr(stats.get_plugin(plugin), method),
326
                                msg='{} has no method {}()'.format(plugin, method))
327
328
    def test_096_views(self):
329
        """Test get_views method"""
330
        print('INFO: [TEST_096] Test views')
331
        plugins_list = stats.getPluginsList()
332
        for plugin in plugins_list:
333
            stats.get_plugin(plugin).get_raw()
334
            views_grab = stats.get_plugin(plugin).get_views()
335
            self.assertTrue(type(views_grab) is dict,
336
                            msg='{} view is not a dict'.format(plugin))
337
338
    def test_097_attribute(self):
339
        """Test GlancesAttribute classes"""
340
        print('INFO: [TEST_097] Test attribute')
341
        # GlancesAttribute
342
        from glances.attribute import GlancesAttribute
343
        a = GlancesAttribute('a', description='ad', history_max_size=3)
344
        self.assertEqual(a.name, 'a')
345
        self.assertEqual(a.description, 'ad')
346
        a.description = 'adn'
347
        self.assertEqual(a.description, 'adn')
348
        a.value = 1
349
        a.value = 2
350
        self.assertEqual(len(a.history), 2)
351
        a.value = 3
352
        self.assertEqual(len(a.history), 3)
353
        a.value = 4
354
        # Check if history_max_size=3 is OK
355
        self.assertEqual(len(a.history), 3)
356
        self.assertEqual(a.history_size(), 3)
357
        self.assertEqual(a.history_len(), 3)
358
        self.assertEqual(a.history_value()[1], 4)
359
        self.assertEqual(a.history_mean(nb=3), 4.5)
360
361
    def test_098_history(self):
362
        """Test GlancesHistory classes"""
363
        print('INFO: [TEST_098] Test history')
364
        # GlancesHistory
365
        from glances.history import GlancesHistory
366
        h = GlancesHistory()
367
        h.add('a', 1, history_max_size=100)
368
        h.add('a', 2, history_max_size=100)
369
        h.add('a', 3, history_max_size=100)
370
        h.add('b', 10, history_max_size=100)
371
        h.add('b', 20, history_max_size=100)
372
        h.add('b', 30, history_max_size=100)
373
        self.assertEqual(len(h.get()), 2)
374
        self.assertEqual(len(h.get()['a']), 3)
375
        h.reset()
376
        self.assertEqual(len(h.get()), 2)
377
        self.assertEqual(len(h.get()['a']), 0)
378
379
    def test_099_output_bars_must_be_between_0_and_100_percent(self):
380
        """Test quick look plugin.
381
382
        > bar.min_value
383
        0
384
        > bar.max_value
385
        100
386
        > bar.percent = -1
387
        > bar.percent
388
        0
389
        > bar.percent = 101
390
        > bar.percent
391
        100
392
        """
393
        print('INFO: [TEST_099] Test progress bar')
394
        bar = Bar(size=1)
395
        bar.percent = -1
396
        self.assertLessEqual(bar.percent, bar.min_value)
397
        bar.percent = 101
398
        self.assertGreaterEqual(bar.percent, bar.max_value)
399
400
    def test_100_secure(self):
401
        """Test secure functions"""
402
        print('INFO: [TEST_100] Secure functions')
403
        if WINDOWS:
404
            self.assertEqual(secure_popen('echo TEST'), 'TEST\r\n')
405
            self.assertEqual(secure_popen('echo TEST1 && echo TEST2'), 'TEST1\r\nTEST2\r\n')
406
        else:
407
            self.assertEqual(secure_popen('echo -n TEST'), 'TEST')
408
            self.assertEqual(secure_popen('echo FOO | grep FOO'), 'FOO\n')
409
            self.assertEqual(secure_popen('echo -n TEST1 && echo -n TEST2'), 'TEST1TEST2')
410
411
    def test_200_memory_leak(self):
412
        """Memory leak check"""
413
        # Only available in PY3
414
        if not PY3:
415
            return
416
        import tracemalloc
417
        print('INFO: [TEST_200] Memory leak check')
418
        tracemalloc.start()
419
        # 3 iterations just to init the stats and fill the memory
420
        for _ in range(3):
421
            stats.update()
422
423
        # Start the memory leak check
424
        snapshot_begin = tracemalloc.take_snapshot()
425
        for _ in range(3):
426
            stats.update()
427
        snapshot_end = tracemalloc.take_snapshot()
428
        snapshot_diff = snapshot_end.compare_to(snapshot_begin, 'filename')
429
        memory_leak = sum([s.size_diff for s in snapshot_diff])
430
        print('INFO: Memory leak: {} bytes'.format(memory_leak))
431
432
        # snapshot_begin = tracemalloc.take_snapshot()
433
        for _ in range(30):
434
            stats.update()
435
        snapshot_end = tracemalloc.take_snapshot()
436
        snapshot_diff = snapshot_end.compare_to(snapshot_begin, 'filename')
437
        memory_leak = sum([s.size_diff for s in snapshot_diff])
438
        print('INFO: Memory leak: {} bytes'.format(memory_leak))
439
440
        # snapshot_begin = tracemalloc.take_snapshot()
441
        for _ in range(300):
442
            stats.update()
443
        snapshot_end = tracemalloc.take_snapshot()
444
        snapshot_diff = snapshot_end.compare_to(snapshot_begin, 'filename')
445
        memory_leak = sum([s.size_diff for s in snapshot_diff])
446
        print('INFO: Memory leak: {} bytes'.format(memory_leak))
447
        snapshot_top = snapshot_end.compare_to(snapshot_begin, 'traceback')
448
        print("Memory consumption (top 5):")
449
        for stat in snapshot_top[:5]:
450
            print(stat)
451
            for line in stat.traceback.format():
452
                print(line)
453
454
    def test_999_the_end(self):
455
        """Free all the stats"""
456
        print('INFO: [TEST_999] Free the stats')
457
        stats.end()
458
        self.assertTrue(True)
459
460
461
if __name__ == '__main__':
462
    unittest.main()
463