1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Glances - An eye on your system
5#
6# Copyright (C) 2019 Nicolargo <nicolas@nicolargo.com>
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
23import time
24import unittest
25
26from glances.main import GlancesMain
27from glances.stats import GlancesStats
28from glances import __version__
29from glances.globals import WINDOWS, LINUX
30from glances.outputs.glances_bars import Bar
31from glances.thresholds import GlancesThresholdOk
32from glances.thresholds import GlancesThresholdCareful
33from glances.thresholds import GlancesThresholdWarning
34from glances.thresholds import GlancesThresholdCritical
35from glances.thresholds import GlancesThresholds
36from glances.plugins.glances_plugin import GlancesPlugin
37from glances.compat import subsample, range
38
39# Global variables
40# =================
41
42
43# Init Glances core
44core = GlancesMain()
45
46# Init Glances stats
47stats = GlancesStats(config=core.get_config(),
48                     args=core.get_args())
49
50# Unitest class
51# ==============
52print('Unitary tests for Glances %s' % __version__)
53
54
55class TestGlances(unittest.TestCase):
56    """Test Glances class."""
57
58    def setUp(self):
59        """The function is called *every time* before test_*."""
60        print('\n' + '=' * 78)
61
62    def test_000_update(self):
63        """Update stats (mandatory step for all the stats).
64
65        The update is made twice (for rate computation).
66        """
67        print('INFO: [TEST_000] Test the stats update function')
68        try:
69            stats.update()
70        except Exception as e:
71            print('ERROR: Stats update failed: %s' % e)
72            self.assertTrue(False)
73        time.sleep(1)
74        try:
75            stats.update()
76        except Exception as e:
77            print('ERROR: Stats update failed: %s' % e)
78            self.assertTrue(False)
79
80        self.assertTrue(True)
81
82    def test_001_plugins(self):
83        """Check mandatory plugins."""
84        plugins_to_check = ['system', 'cpu', 'load', 'mem', 'memswap', 'network', 'diskio', 'fs', 'irq']
85        print('INFO: [TEST_001] Check the mandatory plugins list: %s' % ', '.join(plugins_to_check))
86        plugins_list = stats.getPluginsList()
87        for plugin in plugins_to_check:
88            self.assertTrue(plugin in plugins_list)
89
90    def test_002_system(self):
91        """Check SYSTEM plugin."""
92        stats_to_check = ['hostname', 'os_name']
93        print('INFO: [TEST_002] Check SYSTEM stats: %s' % ', '.join(stats_to_check))
94        stats_grab = stats.get_plugin('system').get_raw()
95        for stat in stats_to_check:
96            # Check that the key exist
97            self.assertTrue(stat in stats_grab, msg='Cannot find key: %s' % stat)
98        print('INFO: SYSTEM stats: %s' % stats_grab)
99
100    def test_003_cpu(self):
101        """Check CPU plugin."""
102        stats_to_check = ['system', 'user', 'idle']
103        print('INFO: [TEST_003] Check mandatory CPU stats: %s' % ', '.join(stats_to_check))
104        stats_grab = stats.get_plugin('cpu').get_raw()
105        for stat in stats_to_check:
106            # Check that the key exist
107            self.assertTrue(stat in stats_grab, msg='Cannot find key: %s' % stat)
108            # Check that % is > 0 and < 100
109            self.assertGreaterEqual(stats_grab[stat], 0)
110            self.assertLessEqual(stats_grab[stat], 100)
111        print('INFO: CPU stats: %s' % stats_grab)
112
113    @unittest.skipIf(WINDOWS, "Load average not available on Windows")
114    def test_004_load(self):
115        """Check LOAD plugin."""
116        stats_to_check = ['cpucore', 'min1', 'min5', 'min15']
117        print('INFO: [TEST_004] Check LOAD stats: %s' % ', '.join(stats_to_check))
118        stats_grab = stats.get_plugin('load').get_raw()
119        for stat in stats_to_check:
120            # Check that the key exist
121            self.assertTrue(stat in stats_grab, msg='Cannot find key: %s' % stat)
122            # Check that % is > 0
123            self.assertGreaterEqual(stats_grab[stat], 0)
124        print('INFO: LOAD stats: %s' % stats_grab)
125
126    def test_005_mem(self):
127        """Check MEM plugin."""
128        stats_to_check = ['available', 'used', 'free', 'total']
129        print('INFO: [TEST_005] Check MEM stats: %s' % ', '.join(stats_to_check))
130        stats_grab = stats.get_plugin('mem').get_raw()
131        for stat in stats_to_check:
132            # Check that the key exist
133            self.assertTrue(stat in stats_grab, msg='Cannot find key: %s' % stat)
134            # Check that % is > 0
135            self.assertGreaterEqual(stats_grab[stat], 0)
136        print('INFO: MEM stats: %s' % stats_grab)
137
138    def test_006_swap(self):
139        """Check MEMSWAP plugin."""
140        stats_to_check = ['used', 'free', 'total']
141        print('INFO: [TEST_006] Check SWAP stats: %s' % ', '.join(stats_to_check))
142        stats_grab = stats.get_plugin('memswap').get_raw()
143        for stat in stats_to_check:
144            # Check that the key exist
145            self.assertTrue(stat in stats_grab, msg='Cannot find key: %s' % stat)
146            # Check that % is > 0
147            self.assertGreaterEqual(stats_grab[stat], 0)
148        print('INFO: SWAP stats: %s' % stats_grab)
149
150    def test_007_network(self):
151        """Check NETWORK plugin."""
152        print('INFO: [TEST_007] Check NETWORK stats')
153        stats_grab = stats.get_plugin('network').get_raw()
154        self.assertTrue(type(stats_grab) is list, msg='Network stats is not a list')
155        print('INFO: NETWORK stats: %s' % stats_grab)
156
157    def test_008_diskio(self):
158        """Check DISKIO plugin."""
159        print('INFO: [TEST_008] Check DISKIO stats')
160        stats_grab = stats.get_plugin('diskio').get_raw()
161        self.assertTrue(type(stats_grab) is list, msg='DiskIO stats is not a list')
162        print('INFO: diskio stats: %s' % stats_grab)
163
164    def test_009_fs(self):
165        """Check File System plugin."""
166        # stats_to_check = [ ]
167        print('INFO: [TEST_009] Check FS stats')
168        stats_grab = stats.get_plugin('fs').get_raw()
169        self.assertTrue(type(stats_grab) is list, msg='FileSystem stats is not a list')
170        print('INFO: FS stats: %s' % stats_grab)
171
172    def test_010_processes(self):
173        """Check Process plugin."""
174        # stats_to_check = [ ]
175        print('INFO: [TEST_010] Check PROCESS stats')
176        stats_grab = stats.get_plugin('processcount').get_raw()
177        # total = stats_grab['total']
178        self.assertTrue(type(stats_grab) is dict, msg='Process count stats is not a dict')
179        print('INFO: PROCESS count stats: %s' % stats_grab)
180        stats_grab = stats.get_plugin('processlist').get_raw()
181        self.assertTrue(type(stats_grab) is list, msg='Process count stats is not a list')
182        print('INFO: PROCESS list stats: %s items in the list' % len(stats_grab))
183        # Check if number of processes in the list equal counter
184        # self.assertEqual(total, len(stats_grab))
185
186    def test_011_folders(self):
187        """Check File System plugin."""
188        # stats_to_check = [ ]
189        print('INFO: [TEST_011] Check FOLDER stats')
190        stats_grab = stats.get_plugin('folders').get_raw()
191        self.assertTrue(type(stats_grab) is list, msg='Folders stats is not a list')
192        print('INFO: Folders stats: %s' % stats_grab)
193
194    def test_012_ip(self):
195        """Check IP plugin."""
196        print('INFO: [TEST_012] Check IP stats')
197        stats_grab = stats.get_plugin('ip').get_raw()
198        self.assertTrue(type(stats_grab) is dict, msg='IP stats is not a dict')
199        print('INFO: IP stats: %s' % stats_grab)
200
201    @unittest.skipIf(not LINUX, "IRQs available only on Linux")
202    def test_013_irq(self):
203        """Check IRQ plugin."""
204        print('INFO: [TEST_013] Check IRQ stats')
205        stats_grab = stats.get_plugin('irq').get_raw()
206        self.assertTrue(type(stats_grab) is list, msg='IRQ stats is not a list')
207        print('INFO: IRQ stats: %s' % stats_grab)
208
209    @unittest.skipIf(not LINUX, "GPU available only on Linux")
210    def test_013_gpu(self):
211        """Check GPU plugin."""
212        print('INFO: [TEST_014] Check GPU stats')
213        stats_grab = stats.get_plugin('gpu').get_raw()
214        self.assertTrue(type(stats_grab) is list, msg='GPU stats is not a list')
215        print('INFO: GPU stats: %s' % stats_grab)
216
217    def test_014_sorted_stats(self):
218        """Check sorted stats method."""
219        print('INFO: [TEST_015] Check sorted stats method')
220        aliases = {
221            "key2": "alias11",
222            "key5": "alias2",
223        }
224        unsorted_stats = [
225            {"key": "key4"},
226            {"key": "key2"},
227            {"key": "key5"},
228            {"key": "key21"},
229            {"key": "key3"},
230        ]
231
232        gp = GlancesPlugin()
233        gp.get_key = lambda: "key"
234        gp.has_alias = aliases.get
235        gp.stats = unsorted_stats
236
237        sorted_stats = gp.sorted_stats()
238        self.assertEqual(len(sorted_stats), 5)
239        self.assertEqual(sorted_stats[0]["key"], "key5")
240        self.assertEqual(sorted_stats[1]["key"], "key2")
241        self.assertEqual(sorted_stats[2]["key"], "key3")
242        self.assertEqual(sorted_stats[3]["key"], "key4")
243        self.assertEqual(sorted_stats[4]["key"], "key21")
244
245    def test_015_subsample(self):
246        """Test subsampling function."""
247        print('INFO: [TEST_015] Subsampling')
248        for l in [([1, 2, 3], 4),
249                  ([1, 2, 3, 4], 4),
250                  ([1, 2, 3, 4, 5, 6, 7], 4),
251                  ([1, 2, 3, 4, 5, 6, 7, 8], 4),
252                  (list(range(1, 800)), 4),
253                  (list(range(1, 8000)), 800)]:
254            l_subsample = subsample(l[0], l[1])
255            self.assertLessEqual(len(l_subsample), l[1])
256
257    def test_016_hddsmart(self):
258        """Check hard disk SMART data plugin."""
259        try:
260            from glances.compat import is_admin
261        except ImportError:
262            print("INFO: [TEST_016] pySMART not found, not running SMART plugin test")
263            return
264
265        stat = 'DeviceName'
266        print('INFO: [TEST_016] Check SMART stats: {}'.format(stat))
267        stats_grab = stats.get_plugin('smart').get_raw()
268        if not is_admin():
269            print("INFO: Not admin, SMART list should be empty")
270            assert len(stats_grab) == 0
271        elif stats_grab == {}:
272            print("INFO: Admin but SMART list is empty")
273            assert len(stats_grab) == 0
274        else:
275            print(stats_grab)
276            self.assertTrue(stat in stats_grab[0].keys(), msg='Cannot find key: %s' % stat)
277
278        print('INFO: SMART stats: %s' % stats_grab)
279
280    def test_094_thresholds(self):
281        """Test thresholds classes"""
282        print('INFO: [TEST_094] Thresholds')
283        ok = GlancesThresholdOk()
284        careful = GlancesThresholdCareful()
285        warning = GlancesThresholdWarning()
286        critical = GlancesThresholdCritical()
287        self.assertTrue(ok < careful)
288        self.assertTrue(careful < warning)
289        self.assertTrue(warning < critical)
290        self.assertFalse(ok > careful)
291        self.assertEqual(ok, ok)
292        self.assertEqual(str(ok), 'OK')
293        thresholds = GlancesThresholds()
294        thresholds.add('cpu_percent', 'OK')
295        self.assertEqual(thresholds.get(stat_name='cpu_percent').description(), 'OK')
296
297    def test_095_methods(self):
298        """Test mandatories methods"""
299        print('INFO: [TEST_095] Mandatories methods')
300        mandatories_methods = ['reset', 'update']
301        plugins_list = stats.getPluginsList()
302        for plugin in plugins_list:
303            for method in mandatories_methods:
304                self.assertTrue(hasattr(stats.get_plugin(plugin), method),
305                                msg='{} has no method {}()'.format(plugin, method))
306
307    def test_096_views(self):
308        """Test get_views method"""
309        print('INFO: [TEST_096] Test views')
310        plugins_list = stats.getPluginsList()
311        for plugin in plugins_list:
312            stats_grab = stats.get_plugin(plugin).get_raw()
313            views_grab = stats.get_plugin(plugin).get_views()
314            self.assertTrue(type(views_grab) is dict,
315                            msg='{} view is not a dict'.format(plugin))
316
317    def test_097_attribute(self):
318        """Test GlancesAttribute classe"""
319        print('INFO: [TEST_097] Test attribute')
320        # GlancesAttribute
321        from glances.attribute import GlancesAttribute
322        a = GlancesAttribute('a', description='ad', history_max_size=3)
323        self.assertEqual(a.name, 'a')
324        self.assertEqual(a.description, 'ad')
325        a.description = 'adn'
326        self.assertEqual(a.description, 'adn')
327        a.value = 1
328        a.value = 2
329        self.assertEqual(len(a.history), 2)
330        a.value = 3
331        self.assertEqual(len(a.history), 3)
332        a.value = 4
333        # Check if history_max_size=3 is OK
334        self.assertEqual(len(a.history), 3)
335        self.assertEqual(a.history_size(), 3)
336        self.assertEqual(a.history_len(), 3)
337        self.assertEqual(a.history_value()[1], 4)
338        self.assertEqual(a.history_mean(nb=3), 4.5)
339
340    def test_098_history(self):
341        """Test GlancesHistory classe"""
342        print('INFO: [TEST_098] Test history')
343        # GlancesHistory
344        from glances.history import GlancesHistory
345        h = GlancesHistory()
346        h.add('a', 1)
347        h.add('a', 2)
348        h.add('a', 3)
349        h.add('b', 10)
350        h.add('b', 20)
351        h.add('b', 30)
352        self.assertEqual(len(h.get()), 2)
353        self.assertEqual(len(h.get()['a']), 3)
354        h.reset()
355        self.assertEqual(len(h.get()), 2)
356        self.assertEqual(len(h.get()['a']), 0)
357
358    def test_099_output_bars_must_be_between_0_and_100_percent(self):
359        """Test quick look plugin.
360
361        > bar.min_value
362        0
363        > bar.max_value
364        100
365        > bar.percent = -1
366        > bar.percent
367        0
368        > bar.percent = 101
369        > bar.percent
370        100
371        """
372        print('INFO: [TEST_099] Test progress bar')
373        bar = Bar(size=1)
374        bar.percent = -1
375        self.assertLessEqual(bar.percent, bar.min_value)
376        bar.percent = 101
377        self.assertGreaterEqual(bar.percent, bar.max_value)
378
379    def test_999_the_end(self):
380        """Free all the stats"""
381        print('INFO: [TEST_999] Free the stats')
382        stats.end()
383        self.assertTrue(True)
384
385
386if __name__ == '__main__':
387    unittest.main()
388