0

I am unit testing my application with py.test and am running into false test failures for this specific piece of code:

# get current cpu usage in %
def get_cpu_use_perc(self):
    cpu_use = os.popen("top -n1 | awk '/Cpu\(s\):/ {print $2}'").readline().strip('\\')
    print('-DBG- CPU Use call from terminal returns: {}'.format(cpu_use))
    self._cpu_use = float(cpu_use)
    return self._cpu_use

I am pretty new to py.test, so I'm not too sure how to fix this. I have tested this specific function separately and it returns a number as I would expect. Looking through the documentation, it looks like there are issues when trying to use stdout. I have tested other code that uses os.popen('command_here').readline() and that works fine, so I assume it has to do with top.

What about this command is causing the test to fail? Is there a better way to get the CPU usage on a raspberry pi without using top that will work with py.test? Is there a marker or something I can add to allow the tool to capture the information I want?

EDIT: Here is an update for code snippet for the unit test:

import time
from system_monitor import system_monitor

# create object
sys_mon = system_monitor()

# simplified unit test
def test_memory(num_checks=20):
    cpu_use_perc = sys_mon.get_cpu_use_perc()

    assert float(cpu_use_perc)  < 5.0  # tested CPU usage, too high for testing

When I run this unit test from the command line, I see:

>>>sudo python test_sys_mon.py
-DBG- CPU Use call from terminal returns: 3.2

When I run my test suite using py.test integrated with setup.py, I see:

>>>sudo python setup.py test
--- other test stuff passing here that is not important...
...
>           cpu_use_perc = sys_mon.get_cpu_use_perc()                                                   

src/system_monitor/test/test_sys_mon.py:128:                                                                                                                                                    

self = <system_monitor.system_monitor.system_monitor object at 0xb58a6ad0>                                             

def get_cpu_use_perc(self):                                                                                        
    cpu_use = os.popen("top -n1 | awk '/Cpu\(s\):/ {print $2}'").readline().strip('\\')                            
    print('-DBG- CPU Use call from terminal returns: {}'.format(cpu_use))                                          
>   self._cpu_use = float(cpu_use)                                                                                 
E   ValueError: could not convert string to float:                                                                 

src/system_monitor/system_monitor.py:50: ValueError                                                                    
------------------------- Captured stdout call -----------------------------

-DBG- CPU Use call from terminal returns:                                                                              
------------------------- Captured stderr call -----------------------------

It looks like this os.popen() is returning an empty string, but I'm not sure how to fix this so I can use the top command with py.test

nichollsg
  • 360
  • 6
  • 21
  • Provide unit test. – Serge Oct 14 '18 at 01:50
  • Try in python interpreter and see if it works. `>>> os.popen("top -n1 | awk '/Cpu\(s\):/ {print $2}'").readline().strip()` – user3713719 Oct 14 '18 at 03:13
  • @Serge I have updated my post to add this information – nichollsg Oct 14 '18 at 19:56
  • @user3713719 I have tried that, but it doesn't look like my post was super clear in that explanation. I have updated to show my debugging steps. – nichollsg Oct 14 '18 at 19:57
  • print `xxx{}xxx`.format(cpu_use) to see if any extra characters in cpu_use. you may need cpu_use.strip(). – user3713719 Oct 14 '18 at 21:49
  • so far two thoughts, pytest is not super good with processes https://github.com/python/mypy/issues/3975, popen is deprecated so it is even hard to call a bug – Serge Oct 15 '18 at 00:05
  • @user3713719 I have run that test, and it is indeed not returning anything: `-DBG- CPU Use call from terminal returns: xxxxxx` – nichollsg Oct 15 '18 at 01:52
  • try mocking os.popen or stdout, or try use subprocess.popen – Serge Oct 15 '18 at 14:55
  • I tried subprocess.check_output(...) and it failed with top command failing (unlike say ps) makes a valid bug, report to pytest bug tracker and hopefully will have a fix or workaround – Serge Oct 16 '18 at 17:10

1 Answers1

0

Resort to mocking, also instead of deprecated os.popen consider subprocess.Popen as in

Unittesting Python code which uses subprocess.Popen

Also consider use ps instead of top

CalledProcessError: Command '('grep', 'route')' returned non-zero exit status 1

Serge
  • 3,387
  • 3
  • 16
  • 34