Test Failed
Push — master ( 7e7379...128504 )
by Nicolas
03:31
created

unitest.TestGlances.test_200_memory_leak()   B

Complexity

Conditions 8

Size

Total Lines 42
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

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