diff --git a/ci/run_python_tests.sh b/ci/run_python_tests.sh index edb0322cc..101d75288 100755 --- a/ci/run_python_tests.sh +++ b/ci/run_python_tests.sh @@ -26,7 +26,7 @@ python -m unittest discover -s pycue/tests -t pycue -p "*.py" PYTHONPATH=pycue python -m unittest discover -s pyoutline/tests -t pyoutline -p "*.py" PYTHONPATH=pycue python -m unittest discover -s cueadmin/tests -t cueadmin -p "*.py" PYTHONPATH=pycue:pyoutline python -m unittest discover -s cuesubmit/tests -t cuesubmit -p "*.py" -python -m unittest discover -s rqd/tests -t rqd -p "*.py" +python -m pytest rqd/tests # Xvfb no longer supports Python 2. diff --git a/requirements.txt b/requirements.txt index 8af83f243..4fc14043b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,6 +15,7 @@ pylint==2.15.10;python_version>="3.7" pynput==1.7.6 PyYAML==5.1 six==1.16.0 +pytest==8.3.3 # Optional requirements # Sentry support for rqd diff --git a/rqd/rqd/rqcore.py b/rqd/rqd/rqcore.py index 6cd36a391..abff2aed0 100644 --- a/rqd/rqd/rqcore.py +++ b/rqd/rqd/rqcore.py @@ -131,6 +131,7 @@ def _createCommandFile(self, command): @rtype: string @return: Command file location""" # TODO: this should use tempfile to create the files and clean them up afterwards + commandFile = None try: if platform.system() == "Windows": rqd_tmp_dir = os.path.join(tempfile.gettempdir(), 'rqd') diff --git a/rqd/rqd/rqmachine.py b/rqd/rqd/rqmachine.py index 1f67798e3..0687858c7 100644 --- a/rqd/rqd/rqmachine.py +++ b/rqd/rqd/rqmachine.py @@ -215,10 +215,23 @@ def __updateGpuAndLlu(self, frame): frame.lluTime = int(stat) def _getStatFields(self, pidFilePath): + """ Read stats file and return list of values + Stats file can star with these formats: + - 105 name ... + - 105 (name) ... + - 105 (name with space) ... + - 105 (name with) (space and parenthesis) ... + """ with open(pidFilePath, "r", encoding='utf-8') as statFile: - stats = statFile.read().split() - stats[1] = stats[1].strip('()') - return stats + txt = statFile.read() + try: + open_par_index = txt.index('(') + close_par_index = txt.rindex(')') + name = txt[open_par_index:close_par_index].strip("()") + reminder = (txt[0:open_par_index] + txt[close_par_index + 1:]).split() + return reminder[0:1] + [name] + reminder[1:] + except ValueError: + return txt.split() def rssUpdate(self, frames): """Updates the rss and maxrss for all running frames""" @@ -269,6 +282,9 @@ def rssUpdate(self, frames): # Fetch swap usage "swap": self._getProcSwap(pid), } + + # TODO: Improve this logic to avoid collecting data from all running procs. + # instead, focus on the monitored procs hierarchy # cmdline: p = psutil.Process(int(pid)) pids[pid]["cmd_line"] = p.cmdline() diff --git a/rqd/rqd/rqnimby.py b/rqd/rqd/rqnimby.py index 15b8dd89f..fcdfdd9c3 100644 --- a/rqd/rqd/rqnimby.py +++ b/rqd/rqd/rqnimby.py @@ -58,7 +58,7 @@ def getNimby(rqCore): # Ideally ImportError could be used here, but pynput # can throw other kinds of exception while trying to # access runpy components - log.exception("Failed to import pynput, falling back to Select module") + log.debug("Failed to import pynput, falling back to Select module") # Still enabling the application start as hosts can be manually locked # using the API/GUI return NimbyNop(rqCore) @@ -242,8 +242,7 @@ def openEvents(self): if device.startswith("event") or device.startswith("mice"): try: # pylint: disable=consider-using-with - self.fileObjList.append(open("/dev/input/%s" % device, "rb", - encoding='utf-8')) + self.fileObjList.append(open("/dev/input/%s" % device, "rb")) except IOError: # Bad device found log.exception("IOError: Failed to open /dev/input/%s", device) diff --git a/rqd/tests/test_cuebot_listener.py b/rqd/tests/cuebot_listener_test.py similarity index 100% rename from rqd/tests/test_cuebot_listener.py rename to rqd/tests/cuebot_listener_test.py diff --git a/rqd/tests/cuerqd_tests.py b/rqd/tests/cuerqd_test.py similarity index 100% rename from rqd/tests/cuerqd_tests.py rename to rqd/tests/cuerqd_test.py diff --git a/rqd/tests/rqconstants_tests.py b/rqd/tests/rqconstants_test.py similarity index 92% rename from rqd/tests/rqconstants_tests.py rename to rqd/tests/rqconstants_test.py index 45e52c0b1..0503994a2 100644 --- a/rqd/tests/rqconstants_tests.py +++ b/rqd/tests/rqconstants_test.py @@ -39,7 +39,7 @@ import rqd.rqutil import rqd.compiled_proto.report_pb2 -from .rqmachine_tests import ( +from .rqmachine_test import ( CPUINFO, LOADAVG_LOW_USAGE, MEMINFO_MODERATE_USAGE, @@ -121,6 +121,7 @@ def makeRqMachine(self): """ [Override] DEFAULT_FACILITY = test_facility +RQD_TAGS = test_tag1 test_tag2 test_tag3 """, ) def test_facility(self): @@ -128,19 +129,6 @@ def test_facility(self): machine = self.makeRqMachine() self.assertEqual(machine.renderHost.facility, "test_facility") - - @MockConfig( - tempdir, - """ -[Override] -RQD_TAGS = test_tag1 test_tag2 test_tag3 -""", - ) - def test_tags(self): - self.assertEqual(rqd.rqconstants.RQD_TAGS, "test_tag1 test_tag2 test_tag3") - - machine = self.makeRqMachine() - self.assertEqual(machine.renderHost.facility, "cloud") self.assertTrue( set(["test_tag1", "test_tag2", "test_tag3"]).issubset( machine.renderHost.tags diff --git a/rqd/tests/rqcore_tests.py b/rqd/tests/rqcore_test.py similarity index 69% rename from rqd/tests/rqcore_tests.py rename to rqd/tests/rqcore_test.py index 09f06d23f..fc155a3be 100644 --- a/rqd/tests/rqcore_tests.py +++ b/rqd/tests/rqcore_test.py @@ -24,6 +24,7 @@ from builtins import str import os.path import unittest +import subprocess import mock import pyfakefs.fake_filesystem_unittest @@ -40,16 +41,16 @@ class RqCoreTests(unittest.TestCase): - @mock.patch('rqd.rqnimby.NimbySelect', autospec=True) - @mock.patch('rqd.rqnetwork.Network', autospec=True) - @mock.patch('rqd.rqmachine.Machine', autospec=True) + @mock.patch("rqd.rqnimby.NimbyPynput", autospec=True) + @mock.patch("rqd.rqnetwork.Network", autospec=True) + @mock.patch("rqd.rqmachine.Machine", autospec=True) def setUp(self, machineMock, networkMock, nimbyMock): self.machineMock = machineMock self.networkMock = networkMock self.nimbyMock = nimbyMock self.rqcore = rqd.rqcore.RqCore() - @mock.patch.object(rqd.rqcore.RqCore, 'nimbyOn') + @mock.patch.object(rqd.rqcore.RqCore, "nimbyOn") def test_startServer(self, nimbyOnMock): rqd.rqconstants.OVERRIDE_NIMBY = False self.machineMock.return_value.isDesktop.return_value = False @@ -59,7 +60,7 @@ def test_startServer(self, nimbyOnMock): self.networkMock.return_value.start_grpc.assert_called() nimbyOnMock.assert_not_called() - @mock.patch.object(rqd.rqcore.RqCore, 'nimbyOn', autospec=True) + @mock.patch.object(rqd.rqcore.RqCore, "nimbyOn", autospec=True) def test_startServerWithNimby(self, nimbyOnMock): rqd.rqconstants.OVERRIDE_NIMBY = True self.machineMock.return_value.isDesktop.return_value = False @@ -69,7 +70,7 @@ def test_startServerWithNimby(self, nimbyOnMock): self.networkMock.return_value.start_grpc.assert_called() nimbyOnMock.assert_called_with(self.rqcore) - @mock.patch.object(rqd.rqcore.RqCore, 'nimbyOn', autospec=True) + @mock.patch.object(rqd.rqcore.RqCore, "nimbyOn", autospec=True) def test_startDesktopNimbyOn(self, nimbyOnMock): rqd.rqconstants.OVERRIDE_NIMBY = True self.machineMock.return_value.isDesktop.return_value = True @@ -79,7 +80,7 @@ def test_startDesktopNimbyOn(self, nimbyOnMock): self.networkMock.return_value.start_grpc.assert_called() nimbyOnMock.assert_called_with(self.rqcore) - @mock.patch.object(rqd.rqcore.RqCore, 'nimbyOn') + @mock.patch.object(rqd.rqcore.RqCore, "nimbyOn") def test_startDesktopNimbyOff(self, nimbyOnMock): rqd.rqconstants.OVERRIDE_NIMBY = False self.machineMock.return_value.isDesktop.return_value = True @@ -89,7 +90,7 @@ def test_startDesktopNimbyOff(self, nimbyOnMock): self.networkMock.return_value.start_grpc.assert_called() nimbyOnMock.assert_not_called() - @mock.patch.object(rqd.rqcore.RqCore, 'nimbyOn') + @mock.patch.object(rqd.rqcore.RqCore, "nimbyOn") def test_startDesktopNimbyUndefined(self, nimbyOnMock): rqd.rqconstants.OVERRIDE_NIMBY = None self.machineMock.return_value.isDesktop.return_value = True @@ -99,9 +100,9 @@ def test_startDesktopNimbyUndefined(self, nimbyOnMock): self.networkMock.return_value.start_grpc.assert_called() nimbyOnMock.assert_not_called() - @mock.patch('rqd.rqnetwork.Network', autospec=True) - @mock.patch('rqd.rqmachine.Machine', autospec=True) - @mock.patch.object(rqd.rqcore.RqCore, 'nimbyOn') + @mock.patch("rqd.rqnetwork.Network", autospec=True) + @mock.patch("rqd.rqmachine.Machine", autospec=True) + @mock.patch.object(rqd.rqcore.RqCore, "nimbyOn") def test_startDesktopNimbyOffWithFlag(self, nimbyOnMock, machineMock, networkMock): rqd.rqconstants.OVERRIDE_NIMBY = True machineMock.return_value.isDesktop.return_value = True @@ -112,7 +113,7 @@ def test_startDesktopNimbyOffWithFlag(self, nimbyOnMock, machineMock, networkMoc networkMock.return_value.start_grpc.assert_called() nimbyOnMock.assert_not_called() - @mock.patch('threading.Timer') + @mock.patch("threading.Timer") def test_grpcConnected(self, timerMock): update_rss_thread = mock.MagicMock() interval_thread = mock.MagicMock() @@ -124,15 +125,15 @@ def test_grpcConnected(self, timerMock): update_rss_thread.start.assert_called() interval_thread.start.assert_called() - @mock.patch.object(rqd.rqcore.RqCore, 'sendStatusReport', autospec=True) - @mock.patch('threading.Timer') + @mock.patch.object(rqd.rqcore.RqCore, "sendStatusReport", autospec=True) + @mock.patch("threading.Timer") def test_onInterval(self, timerMock, sendStatusReportMock): self.rqcore.onInterval() timerMock.return_value.start.assert_called() sendStatusReportMock.assert_called_with(self.rqcore) - @mock.patch('threading.Timer', autospec=True) + @mock.patch("threading.Timer", autospec=True) def test_onIntervalWithSleepTime(self, timerMock): sleep_time = 72 @@ -141,8 +142,8 @@ def test_onIntervalWithSleepTime(self, timerMock): timerMock.assert_called_with(sleep_time, mock.ANY) timerMock.return_value.start.assert_called() - @mock.patch.object(rqd.rqcore.RqCore, 'shutdownRqdNow') - @mock.patch('threading.Timer', new=mock.MagicMock()) + @mock.patch.object(rqd.rqcore.RqCore, "shutdownRqdNow") + @mock.patch("threading.Timer", new=mock.MagicMock()) def test_onIntervalShutdown(self, shutdownRqdNowMock): self.rqcore.shutdownRqdIdle() self.machineMock.return_value.isUserLoggedIn.return_value = False @@ -153,9 +154,11 @@ def test_onIntervalShutdown(self, shutdownRqdNowMock): shutdownRqdNowMock.assert_called_with() - @mock.patch('threading.Timer') + @mock.patch("threading.Timer") def test_updateRss(self, timerMock): - self.rqcore.storeFrame('frame-id', mock.MagicMock(spec=rqd.rqnetwork.RunningFrame)) + self.rqcore.storeFrame( + "frame-id", mock.MagicMock(spec=rqd.rqnetwork.RunningFrame) + ) self.rqcore.updateRss() @@ -163,21 +166,25 @@ def test_updateRss(self, timerMock): timerMock.return_value.start.assert_called() def test_getFrame(self): - frame_id = 'arbitrary-frame-id' + frame_id = "arbitrary-frame-id" frame = mock.MagicMock(spec=rqd.rqnetwork.RunningFrame) self.rqcore.storeFrame(frame_id, frame) self.assertEqual(frame, self.rqcore.getFrame(frame_id)) def test_getFrameKeys(self): - frame_ids = ['frame1', 'frame2'] - self.rqcore.storeFrame(frame_ids[0], mock.MagicMock(spec=rqd.rqnetwork.RunningFrame)) - self.rqcore.storeFrame(frame_ids[1], mock.MagicMock(spec=rqd.rqnetwork.RunningFrame)) + frame_ids = ["frame1", "frame2"] + self.rqcore.storeFrame( + frame_ids[0], mock.MagicMock(spec=rqd.rqnetwork.RunningFrame) + ) + self.rqcore.storeFrame( + frame_ids[1], mock.MagicMock(spec=rqd.rqnetwork.RunningFrame) + ) self.assertEqual(set(frame_ids), set(self.rqcore.getFrameKeys())) def test_storeFrame(self): - frame_id = 'arbitrary-frame-id' + frame_id = "arbitrary-frame-id" frame = mock.MagicMock(spec=rqd.rqnetwork.RunningFrame) with self.assertRaises(KeyError): self.rqcore.getFrame(frame_id) @@ -187,19 +194,23 @@ def test_storeFrame(self): self.assertEqual(frame, self.rqcore.getFrame(frame_id)) def test_storeFrameDuplicate(self): - frame_id = 'arbitrary-frame-id' - self.rqcore.storeFrame(frame_id, mock.MagicMock(spec=rqd.rqnetwork.RunningFrame)) + frame_id = "arbitrary-frame-id" + self.rqcore.storeFrame( + frame_id, mock.MagicMock(spec=rqd.rqnetwork.RunningFrame) + ) with self.assertRaises(rqd.rqexceptions.RqdException): - self.rqcore.storeFrame(frame_id, mock.MagicMock(spec=rqd.rqnetwork.RunningFrame)) + self.rqcore.storeFrame( + frame_id, mock.MagicMock(spec=rqd.rqnetwork.RunningFrame) + ) def test_deleteFrame(self): - frame_id = 'arbitrary-frame-id' + frame_id = "arbitrary-frame-id" frame = mock.MagicMock(spec=rqd.rqnetwork.RunningFrame) self.rqcore.storeFrame(frame_id, frame) self.rqcore.deleteFrame(frame_id) - self.rqcore.deleteFrame('unknown-key-should-succeed') + self.rqcore.deleteFrame("unknown-key-should-succeed") with self.assertRaises(KeyError): self.rqcore.getFrame(frame_id) @@ -207,17 +218,20 @@ def test_deleteFrame(self): def test_killAllFrame(self): frameAttendantThread = mock.MagicMock() frameAttendantThread.is_alive.return_value = False - frame1Id = 'frame1' - frame2Id = 'frame2' - frame3Id = 'frame3' + frame1Id = "frame1" + frame2Id = "frame2" + frame3Id = "frame3" frame1 = rqd.rqnetwork.RunningFrame( - self.rqcore, rqd.compiled_proto.rqd_pb2.RunFrame(frame_id=frame1Id)) + self.rqcore, rqd.compiled_proto.rqd_pb2.RunFrame(frame_id=frame1Id) + ) frame1.frameAttendantThread = frameAttendantThread frame2 = rqd.rqnetwork.RunningFrame( - self.rqcore, rqd.compiled_proto.rqd_pb2.RunFrame(frame_id=frame2Id)) + self.rqcore, rqd.compiled_proto.rqd_pb2.RunFrame(frame_id=frame2Id) + ) frame2.frameAttendantThread = frameAttendantThread frame3 = rqd.rqnetwork.RunningFrame( - self.rqcore, rqd.compiled_proto.rqd_pb2.RunFrame(frame_id=frame3Id)) + self.rqcore, rqd.compiled_proto.rqd_pb2.RunFrame(frame_id=frame3Id) + ) frame3.frameAttendantThread = frameAttendantThread self.rqcore.storeFrame(frame1Id, frame1) self.rqcore.storeFrame(frame2Id, frame2) @@ -226,23 +240,26 @@ def test_killAllFrame(self): # There's no result to verify here; if the method completes successfully # it means that all frames were properly killed, as the method won't finish # until its frame cache is cleared by the kill process. - self.rqcore.killAllFrame('arbitrary reason') + self.rqcore.killAllFrame("arbitrary reason") def test_killAllFrameIgnoreNimby(self): frameAttendantThread = mock.MagicMock() frameAttendantThread.is_alive.return_value = False - frame1Id = 'frame1' - frame2Id = 'frame2' + frame1Id = "frame1" + frame2Id = "frame2" frame1 = rqd.rqnetwork.RunningFrame( - self.rqcore, rqd.compiled_proto.rqd_pb2.RunFrame(frame_id=frame1Id)) + self.rqcore, rqd.compiled_proto.rqd_pb2.RunFrame(frame_id=frame1Id) + ) frame1.frameAttendantThread = frameAttendantThread frame2 = rqd.rqnetwork.RunningFrame( - self.rqcore, rqd.compiled_proto.rqd_pb2.RunFrame(frame_id=frame2Id, ignore_nimby=True)) + self.rqcore, + rqd.compiled_proto.rqd_pb2.RunFrame(frame_id=frame2Id, ignore_nimby=True), + ) frame2.frameAttendantThread = frameAttendantThread self.rqcore.storeFrame(frame1Id, frame1) self.rqcore.storeFrame(frame2Id, frame2) - self.rqcore.killAllFrame('NIMBY related reason') + self.rqcore.killAllFrame("NIMBY related reason") self.assertEqual(frame2, self.rqcore.getFrame(frame2Id)) @@ -251,17 +268,25 @@ def test_releaseCores(self): num_booked_cores = 7 num_cores_to_release = 5 self.rqcore.cores = rqd.compiled_proto.report_pb2.CoreDetail( - total_cores=50, idle_cores=num_idle_cores, locked_cores=2, - booked_cores=num_booked_cores) + total_cores=50, + idle_cores=num_idle_cores, + locked_cores=2, + booked_cores=num_booked_cores, + ) self.rqcore.releaseCores(num_cores_to_release) # pylint: disable=no-member - self.assertEqual(num_booked_cores-num_cores_to_release, self.rqcore.cores.booked_cores) - self.assertEqual(num_idle_cores+num_cores_to_release, self.rqcore.cores.idle_cores) - - @mock.patch.object(rqd.rqcore.RqCore, 'nimbyOff') - def test_shutdown(self, nimbyOffMock): + self.assertEqual( + num_booked_cores - num_cores_to_release, self.rqcore.cores.booked_cores + ) + self.assertEqual( + num_idle_cores + num_cores_to_release, self.rqcore.cores.idle_cores + ) + + @mock.patch.object(rqd.rqcore.RqCore, "nimbyOff") + @mock.patch("os._exit") + def test_shutdown(self, nimbyOffMock, exitMock): self.rqcore.onIntervalThread = mock.MagicMock() self.rqcore.updateRssThread = mock.MagicMock() @@ -271,8 +296,8 @@ def test_shutdown(self, nimbyOffMock): self.rqcore.onIntervalThread.cancel.assert_called() self.rqcore.updateRssThread.cancel.assert_called() - @mock.patch('rqd.rqnetwork.Network', autospec=True) - @mock.patch('sys.exit') + @mock.patch("rqd.rqnetwork.Network", autospec=True) + @mock.patch("os._exit") def test_handleExit(self, networkMock, exitMock): self.rqcore = rqd.rqcore.RqCore() @@ -280,9 +305,11 @@ def test_handleExit(self, networkMock, exitMock): exitMock.assert_called() - @mock.patch('rqd.rqcore.FrameAttendantThread') + @mock.patch("rqd.rqcore.FrameAttendantThread") def test_launchFrame(self, frameThreadMock): - self.rqcore.cores = rqd.compiled_proto.report_pb2.CoreDetail(total_cores=100, idle_cores=20) + self.rqcore.cores = rqd.compiled_proto.report_pb2.CoreDetail( + total_cores=100, idle_cores=20 + ) self.machineMock.return_value.state = rqd.compiled_proto.host_pb2.UP self.nimbyMock.return_value.locked = False frame = rqd.compiled_proto.rqd_pb2.RunFrame(uid=22, num_cores=10) @@ -299,7 +326,8 @@ def test_launchFrameOnDownHost(self): with self.assertRaises(rqd.rqexceptions.CoreReservationFailureException): self.rqcore.launchFrame(frame) - def test_launchFrameOnHostWaitingForShutdown(self): + @mock.patch("os._exit") + def test_launchFrameOnHostWaitingForShutdown(self, exitMock): self.machineMock.return_value.state = rqd.compiled_proto.host_pb2.UP self.nimbyMock.return_value.active = False frame = rqd.compiled_proto.rqd_pb2.RunFrame() @@ -308,13 +336,16 @@ def test_launchFrameOnHostWaitingForShutdown(self): with self.assertRaises(rqd.rqexceptions.CoreReservationFailureException): self.rqcore.launchFrame(frame) - @mock.patch('rqd.rqcore.FrameAttendantThread') + @mock.patch("rqd.rqcore.FrameAttendantThread") def test_launchFrameOnNimbyHost(self, frameThreadMock): - self.rqcore.cores = rqd.compiled_proto.report_pb2.CoreDetail(total_cores=100, idle_cores=20) + self.rqcore.cores = rqd.compiled_proto.report_pb2.CoreDetail( + total_cores=100, idle_cores=20 + ) self.machineMock.return_value.state = rqd.compiled_proto.host_pb2.UP frame = rqd.compiled_proto.rqd_pb2.RunFrame(uid=22, num_cores=10) frameIgnoreNimby = rqd.compiled_proto.rqd_pb2.RunFrame( - uid=22, num_cores=10, ignore_nimby=True) + uid=22, num_cores=10, ignore_nimby=True + ) self.rqcore.nimby = mock.create_autospec(rqd.rqnimby.NimbySelect) self.rqcore.nimby.locked = True @@ -326,11 +357,15 @@ def test_launchFrameOnNimbyHost(self, frameThreadMock): frameThreadMock.return_value.start.assert_called() def test_launchDuplicateFrame(self): - self.rqcore.cores = rqd.compiled_proto.report_pb2.CoreDetail(total_cores=100, idle_cores=20) + self.rqcore.cores = rqd.compiled_proto.report_pb2.CoreDetail( + total_cores=100, idle_cores=20 + ) self.machineMock.return_value.state = rqd.compiled_proto.host_pb2.UP self.nimbyMock.return_value.locked = False - frameId = 'arbitrary-frame-id' - self.rqcore.storeFrame(frameId, rqd.compiled_proto.rqd_pb2.RunFrame(frame_id=frameId)) + frameId = "arbitrary-frame-id" + self.rqcore.storeFrame( + frameId, rqd.compiled_proto.rqd_pb2.RunFrame(frame_id=frameId) + ) frameToLaunch = rqd.compiled_proto.rqd_pb2.RunFrame(frame_id=frameId) rqd.rqconstants.OVERRIDE_NIMBY = None @@ -354,7 +389,9 @@ def test_launchFrameWithInvalidCoreCount(self): self.rqcore.launchFrame(frame) def test_launchFrameWithInsufficientCores(self): - self.rqcore.cores = rqd.compiled_proto.report_pb2.CoreDetail(total_cores=100, idle_cores=5) + self.rqcore.cores = rqd.compiled_proto.report_pb2.CoreDetail( + total_cores=100, idle_cores=5 + ) self.machineMock.return_value.state = rqd.compiled_proto.host_pb2.UP self.nimbyMock.return_value.locked = False frame = rqd.compiled_proto.rqd_pb2.RunFrame(uid=22, num_cores=10) @@ -363,14 +400,15 @@ def test_launchFrameWithInsufficientCores(self): self.rqcore.launchFrame(frame) def test_getRunningFrame(self): - frameId = 'arbitrary-frame-id' + frameId = "arbitrary-frame-id" frame = rqd.compiled_proto.rqd_pb2.RunFrame(frame_id=frameId) self.rqcore.storeFrame(frameId, frame) self.assertEqual(frame, self.rqcore.getRunningFrame(frameId)) - self.assertIsNone(self.rqcore.getRunningFrame('some-unknown-frame-id')) + self.assertIsNone(self.rqcore.getRunningFrame("some-unknown-frame-id")) - def test_rebootNowNoUser(self): + @mock.patch("os._exit") + def test_rebootNowNoUser(self, exitMock): self.machineMock.return_value.isUserLoggedIn.return_value = False self.nimbyMock.return_value.active = False @@ -384,7 +422,8 @@ def test_rebootNowWithUser(self): with self.assertRaises(rqd.rqexceptions.RqdException): self.rqcore.rebootNow() - def test_rebootIdleNoFrames(self): + @mock.patch("os._exit") + def test_rebootIdleNoFrames(self, exitMock): self.machineMock.return_value.isUserLoggedIn.return_value = False self.nimbyMock.return_value.active = False @@ -392,10 +431,12 @@ def test_rebootIdleNoFrames(self): self.machineMock.return_value.reboot.assert_called_with() - def test_rebootIdleWithFrames(self): - frame1Id = 'frame1' + @mock.patch("os._exit") + def test_rebootIdleWithFrames(self, exitMock): + frame1Id = "frame1" frame1 = rqd.rqnetwork.RunningFrame( - self.rqcore, rqd.compiled_proto.rqd_pb2.RunFrame(frame_id=frame1Id)) + self.rqcore, rqd.compiled_proto.rqd_pb2.RunFrame(frame_id=frame1Id) + ) self.rqcore.storeFrame(frame1Id, frame1) self.rqcore.rebootIdle() @@ -403,29 +444,13 @@ def test_rebootIdleWithFrames(self): self.assertTrue(self.rqcore.isWaitingForIdle()) self.machineMock.return_value.reboot.assert_not_called() - @mock.patch('os.getuid', new=mock.MagicMock(return_value=0)) - @mock.patch('platform.system', new=mock.MagicMock(return_value='Linux')) - def test_nimbyOn(self): - self.nimbyMock.return_value.active = False - - self.rqcore.nimbyOn() - - self.nimbyMock.return_value.run.assert_called_with() - - def test_nimbyOff(self): - self.nimbyMock.return_value.active = True - - self.rqcore.nimbyOff() - - self.nimbyMock.return_value.stop.assert_called_with() - - @mock.patch.object(rqd.rqcore.RqCore, 'killAllFrame', autospec=True) + @mock.patch.object(rqd.rqcore.RqCore, "killAllFrame", autospec=True) def test_onNimbyLock(self, killAllFrameMock): self.rqcore.onNimbyLock() killAllFrameMock.assert_called_with(self.rqcore, mock.ANY) - @mock.patch.object(rqd.rqcore.RqCore, 'sendStatusReport', autospec=True) + @mock.patch.object(rqd.rqcore.RqCore, "sendStatusReport", autospec=True) def test_onNimbyUnlock(self, sendStatusReportMock): self.rqcore.onNimbyUnlock() @@ -507,6 +532,7 @@ def test_unlockAllWhenNimbyLocked(self): self.rqcore.cores.total_cores = 50 self.rqcore.cores.idle_cores = 40 self.rqcore.cores.locked_cores = 10 + self.rqcore.nimby.locked = True self.rqcore.unlockAll() @@ -514,41 +540,95 @@ def test_unlockAllWhenNimbyLocked(self): self.assertEqual(40, self.rqcore.cores.idle_cores) self.assertEqual(0, self.rqcore.cores.locked_cores) + def test_sendFrameCompleteReport(self): + logDir = "/path/to/log/dir/" + frameId = "arbitrary-frame-id" + jobName = "arbitrary-job-name" + frameName = "arbitrary-frame-name" + frameUid = 928 + frameUsername = "my-random-user" + children = rqd.compiled_proto.report_pb2.ChildrenProcStats() + returnCode = 0 -@mock.patch('rqd.rqutil.checkAndCreateUser', new=mock.MagicMock()) -@mock.patch('rqd.rqutil.permissionsHigh', new=mock.MagicMock()) -@mock.patch('rqd.rqutil.permissionsLow', new=mock.MagicMock()) -@mock.patch('subprocess.Popen') -@mock.patch('time.time') -@mock.patch('rqd.rqutil.permissionsUser', spec=True) + runFrame = rqd.compiled_proto.rqd_pb2.RunFrame( + frame_id=frameId, + job_name=jobName, + frame_name=frameName, + uid=frameUid, + user_name=frameUsername, + log_dir=logDir, + children=children, + ) + frameInfo = rqd.rqnetwork.RunningFrame(self.rqcore, runFrame) + frameInfo.exitStatus = 0 + frameInfo.exitSignal = 0 + frameInfo.ignoreNimby = True + + renderHost = rqd.compiled_proto.report_pb2.RenderHost( + name="arbitrary-host-name" + ) + self.rqcore.machine.getHostInfo.return_value = renderHost + self.rqcore.nimby = mock.MagicMock() + self.rqcore.nimby.locked.return_value = False + self.rqcore.network.reportRunningFrameCompletion = mock.MagicMock() + self.rqcore.sendFrameCompleteReport(frameInfo) + + self.rqcore.network.reportRunningFrameCompletion.assert_called_once_with( + rqd.compiled_proto.report_pb2.FrameCompleteReport( + host=renderHost, + frame=rqd.compiled_proto.report_pb2.RunningFrameInfo( + job_name=jobName, + frame_id=frameId, + frame_name=frameName, + children=children, + ), + exit_status=returnCode, + ) + ) + + +@mock.patch("rqd.rqutil.checkAndCreateUser", new=mock.MagicMock()) +@mock.patch("rqd.rqutil.permissionsHigh", new=mock.MagicMock()) +@mock.patch("rqd.rqutil.permissionsLow", new=mock.MagicMock()) +@mock.patch("subprocess.Popen") +@mock.patch("time.time") +@mock.patch("rqd.rqutil.permissionsUser", spec=True) class FrameAttendantThreadTests(pyfakefs.fake_filesystem_unittest.TestCase): def setUp(self): self.setUpPyfakefs() - rqd.rqconstants.SU_ARGUMENT = '-c' - - @mock.patch('platform.system', new=mock.Mock(return_value='Linux')) - @mock.patch('tempfile.gettempdir') - @mock.patch('rqd.rqcore.pipe_to_file', new=mock.MagicMock()) - def test_runLinux(self, getTempDirMock, permsUser, timeMock, popenMock): # mkdirMock, openMock, + rqd.rqconstants.SU_ARGUMENT = "-c" + + @mock.patch("platform.system", new=mock.Mock(return_value="Linux")) + @mock.patch("tempfile.gettempdir") + @mock.patch("select.poll") + def test_runLinux( + self, selectMock, getTempDirMock, permsUser, timeMock, popenMock + ): # mkdirMock, openMock, # given currentTime = 1568070634.3 - jobTempPath = '/job/temp/path/' - logDir = '/path/to/log/dir/' - tempDir = '/some/random/temp/dir' - frameId = 'arbitrary-frame-id' - jobName = 'arbitrary-job-name' - frameName = 'arbitrary-frame-name' + jobTempPath = "/job/temp/path/" + logDir = "/path/to/log/dir/" + tempDir = "/some/random/temp/dir" + frameId = "arbitrary-frame-id" + jobName = "arbitrary-job-name" + frameName = "arbitrary-frame-name" frameUid = 928 - frameUsername = 'my-random-user' + frameUsername = "my-random-user" returnCode = 0 - renderHost = rqd.compiled_proto.report_pb2.RenderHost(name='arbitrary-host-name') - logFile = os.path.join(logDir, '%s.%s.rqlog' % (jobName, frameName)) + renderHost = rqd.compiled_proto.report_pb2.RenderHost( + name="arbitrary-host-name" + ) + logFile = os.path.join(logDir, "%s.%s.rqlog" % (jobName, frameName)) self.fs.create_dir(tempDir) timeMock.return_value = currentTime getTempDirMock.return_value = tempDir + popenMock.return_value.wait.return_value = returnCode + popenMock.return_value.stdout.readline.return_value = None + + selectMock.return_value.poll.return_value = [] rqCore = mock.MagicMock() rqCore.intervalStartTime = 20 @@ -566,7 +646,8 @@ def test_runLinux(self, getTempDirMock, permsUser, timeMock, popenMock): # mkdir uid=frameUid, user_name=frameUsername, log_dir=logDir, - children=children) + children=children, + ) frameInfo = rqd.rqnetwork.RunningFrame(rqCore, runFrame) # when @@ -578,10 +659,15 @@ def test_runLinux(self, getTempDirMock, permsUser, timeMock, popenMock): # mkdir permsUser.assert_called_with(frameUid, mock.ANY) popenMock.assert_called_with( [ - '/bin/nice', '/usr/bin/time', '-p', '-o', - jobTempPath + 'rqd-stat-' + frameId + '-' + str(currentTime), - '/bin/su', frameUsername, '-c', - '"' + tempDir + '/rqd-cmd-' + frameId + '-' + str(currentTime) + '"' + "/bin/nice", + "/usr/bin/time", + "-p", + "-o", + jobTempPath + "rqd-stat-" + frameId + "-" + str(currentTime), + "/bin/su", + frameUsername, + "-c", + '"' + tempDir + "/rqd-cmd-" + frameId + "-" + str(currentTime) + '"', ], env=mock.ANY, cwd=jobTempPath, @@ -589,35 +675,35 @@ def test_runLinux(self, getTempDirMock, permsUser, timeMock, popenMock): # mkdir stdout=mock.ANY, stderr=mock.ANY, close_fds=mock.ANY, - preexec_fn=mock.ANY) + preexec_fn=mock.ANY, + ) self.assertTrue(os.path.exists(logDir)) self.assertTrue(os.path.isfile(logFile)) _, kwargs = popenMock.call_args - rqCore.network.reportRunningFrameCompletion.assert_called_with( - rqd.compiled_proto.report_pb2.FrameCompleteReport( - host=renderHost, - frame=rqd.compiled_proto.report_pb2.RunningFrameInfo( - job_name=jobName, frame_id=frameId, frame_name=frameName, children=children), - exit_status=returnCode)) + rqCore.sendFrameCompleteReport.assert_called_with( + frameInfo + ) # TODO(bcipriano) Re-enable this test once Windows is supported. The main sticking point here # is that the log directory is always overridden on Windows which makes mocking difficult. - @mock.patch('platform.system', new=mock.Mock(return_value='Windows')) + @mock.patch("platform.system", new=mock.Mock(return_value="Windows")) def disabled__test_runWindows(self, permsUser, timeMock, popenMock): currentTime = 1568070634.3 - jobTempPath = '/job/temp/path/' - logDir = '/path/to/log/dir/' - tempDir = 'C:\\temp' - frameId = 'arbitrary-frame-id' - jobId = 'arbitrary-job-id' - jobName = 'arbitrary-job-name' - frameName = 'arbitrary-frame-name' + jobTempPath = "/job/temp/path/" + logDir = "/path/to/log/dir/" + tempDir = "C:\\temp" + frameId = "arbitrary-frame-id" + jobId = "arbitrary-job-id" + jobName = "arbitrary-job-name" + frameName = "arbitrary-frame-name" frameUid = 928 - frameUsername = 'my-random-user' + frameUsername = "my-random-user" returnCode = 0 - renderHost = rqd.compiled_proto.report_pb2.RenderHost(name='arbitrary-host-name') + renderHost = rqd.compiled_proto.report_pb2.RenderHost( + name="arbitrary-host-name" + ) timeMock.return_value = currentTime popenMock.return_value.returncode = returnCode @@ -640,7 +726,8 @@ def disabled__test_runWindows(self, permsUser, timeMock, popenMock): user_name=frameUsername, log_dir=logDir, children=children, - environment={'CUE_IFRAME': '2000'}) + environment={"CUE_IFRAME": "2000"}, + ) frameInfo = rqd.rqnetwork.RunningFrame(rqCore, runFrame) attendantThread = rqd.rqcore.FrameAttendantThread(rqCore, runFrame, frameInfo) @@ -649,41 +736,51 @@ def disabled__test_runWindows(self, permsUser, timeMock, popenMock): permsUser.assert_called_with(frameUid, mock.ANY) popenMock.assert_called_with( - [tempDir + '/rqd-cmd-' + frameId + '-' + str(currentTime) + '.bat'], + [tempDir + "/rqd-cmd-" + frameId + "-" + str(currentTime) + ".bat"], stdin=mock.ANY, stdout=mock.ANY, - stderr=mock.ANY) + stderr=mock.ANY, + ) # TODO(bcipriano) Verify the log directory was created and used for stdout/stderr. rqCore.network.reportRunningFrameCompletion.assert_called_with( rqd.compiled_proto.report_pb2.FrameCompleteReport( host=renderHost, frame=rqd.compiled_proto.report_pb2.RunningFrameInfo( - job_name=jobName, frame_id=frameId, frame_name=frameName, children=children), - exit_status=returnCode)) - - @mock.patch('platform.system', new=mock.Mock(return_value='Darwin')) - @mock.patch('tempfile.gettempdir') + job_name=jobName, + frame_id=frameId, + frame_name=frameName, + children=children, + ), + exit_status=returnCode, + ) + ) + + @mock.patch("platform.system", new=mock.Mock(return_value="Darwin")) + @mock.patch("tempfile.gettempdir") def test_runDarwin(self, getTempDirMock, permsUser, timeMock, popenMock): # given currentTime = 1568070634.3 - jobTempPath = '/job/temp/path/' - logDir = '/path/to/log/dir/' - tempDir = '/some/random/temp/dir' - frameId = 'arbitrary-frame-id' - jobName = 'arbitrary-job-name' - frameName = 'arbitrary-frame-name' + jobTempPath = "/job/temp/path/" + logDir = "/path/to/log/dir/" + tempDir = "/some/random/temp/dir" + frameId = "arbitrary-frame-id" + jobName = "arbitrary-job-name" + frameName = "arbitrary-frame-name" frameUid = 928 - frameUsername = 'my-random-user' + frameUsername = "my-random-user" returnCode = 0 - renderHost = rqd.compiled_proto.report_pb2.RenderHost(name='arbitrary-host-name') - logFile = os.path.join(logDir, '%s.%s.rqlog' % (jobName, frameName)) + renderHost = rqd.compiled_proto.report_pb2.RenderHost( + name="arbitrary-host-name" + ) + logFile = os.path.join(logDir, "%s.%s.rqlog" % (jobName, frameName)) self.fs.create_dir(tempDir) timeMock.return_value = currentTime getTempDirMock.return_value = tempDir popenMock.return_value.returncode = returnCode + popenMock.return_value.stdout.readline.return_value = None rqCore = mock.MagicMock() rqCore.intervalStartTime = 20 @@ -701,7 +798,8 @@ def test_runDarwin(self, getTempDirMock, permsUser, timeMock, popenMock): uid=frameUid, user_name=frameUsername, log_dir=logDir, - children=children) + children=children, + ) frameInfo = rqd.rqnetwork.RunningFrame(rqCore, runFrame) # when @@ -713,29 +811,29 @@ def test_runDarwin(self, getTempDirMock, permsUser, timeMock, popenMock): permsUser.assert_called_with(frameUid, mock.ANY) popenMock.assert_called_with( [ - '/usr/bin/su', frameUsername, '-c', - '"' + tempDir + '/rqd-cmd-' + frameId + '-' + str(currentTime) + '"' + "/usr/bin/su", + frameUsername, + "-c", + '"' + tempDir + "/rqd-cmd-" + frameId + "-" + str(currentTime) + '"', ], env=mock.ANY, cwd=jobTempPath, stdin=mock.ANY, stdout=mock.ANY, stderr=mock.ANY, - preexec_fn=mock.ANY) + preexec_fn=mock.ANY, + ) self.assertTrue(os.path.exists(logDir)) self.assertTrue(os.path.isfile(logFile)) _, kwargs = popenMock.call_args - self.assertEqual(logFile, kwargs['stdout'].name) - self.assertEqual(logFile, kwargs['stderr'].name) + self.assertEqual(subprocess.PIPE, kwargs["stdout"]) + self.assertEqual(subprocess.STDOUT, kwargs["stderr"]) - rqCore.network.reportRunningFrameCompletion.assert_called_with( - rqd.compiled_proto.report_pb2.FrameCompleteReport( - host=renderHost, - frame=rqd.compiled_proto.report_pb2.RunningFrameInfo( - job_name=jobName, frame_id=frameId, frame_name=frameName, children=children), - exit_status=returnCode)) + rqCore.sendFrameCompleteReport.assert_called_with( + frameInfo + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/rqd/tests/rqmachine_tests.py b/rqd/tests/rqmachine_test.py similarity index 89% rename from rqd/tests/rqmachine_tests.py rename to rqd/tests/rqmachine_test.py index 1b1bdaf4a..c74b22e80 100644 --- a/rqd/tests/rqmachine_tests.py +++ b/rqd/tests/rqmachine_test.py @@ -303,15 +303,19 @@ def _test_rssUpdate(self, proc_stat): self.assertAlmostEqual(0.034444696691, float(updatedFrameInfo.attributes['pcpu'])) @mock.patch('time.time', new=mock.MagicMock(return_value=1570057887.61)) - def test_rssUpdate(self): + @mock.patch('psutil.Process') + def test_rssUpdate(self, processMock): + processMock.return_value.cmdline.return_value = "some_command" self._test_rssUpdate(PROC_PID_STAT) @mock.patch('time.time', new=mock.MagicMock(return_value=1570057887.61)) - def test_rssUpdateWithSpaces(self): + @mock.patch('psutil.Process') + def test_rssUpdateWithSpaces(self, processMock): self._test_rssUpdate(PROC_PID_STAT_WITH_SPACES) @mock.patch('time.time', new=mock.MagicMock(return_value=1570057887.61)) - def test_rssUpdateWithBrackets(self): + @mock.patch('psutil.Process') + def test_rssUpdateWithBrackets(self, processMock): self._test_rssUpdate(PROC_PID_STAT_WITH_BRACKETS) @mock.patch.object( @@ -461,43 +465,41 @@ def test_reserveHT(self): self.machine.setupTaskset() + #-----------------------Core Map------------------------ + # phys 0 phys 1 + # core 0 core 0 + # proc 0 proc 4 + # proc 8 proc 12 + # core 1 core 1 + # proc 1 proc 5 + # proc 9 proc 13 + # core 2 core 2 + # proc 2 proc 6 + # proc 10 proc 14 + # core 3 core 3 + # proc 3 proc 7 + # proc 11 proc 15 # ------------------------step1------------------------- - # phys_id 1 - # - core_id 0 - # - process_id 4 - # - process_id 12 - # - core_id 1 - # - process_id 5 - # - process_id 13 - # - core_id 3 - # - process_id 7 - # - process_id 15 - tasksets1 = self.machine.reserveHT(300) - # pylint: disable=no-member - self.assertItemsEqual(['4', '5', '7', '12', '13', '15'], sorted(tasksets1.split(','))) - - # ------------------------step2------------------------- - # phys_id 0 - # - core_id 0 - # - process_id 0 - # - process_id 8 - # - core_id 1 - # - process_id 1 - # - process_id 9 - # - core_id 2 - # - process_id 2 - # - process_id 10 - # - core_id 3 - # - process_id 3 - # - process_id 11 - tasksets0 = self.machine.reserveHT(400) - # pylint: disable=no-member - self.assertItemsEqual(['0', '1', '2', '3', '8', '9', '10', '11'], - sorted(tasksets0.split(','))) - - # reserved cores got updated properly - # pylint: disable=no-member - self.assertItemsEqual([0, 1, 2, 3], self.coreDetail.reserved_cores[0].coreid) + def assertTaskSet(taskset_list): + """Ensure all tasks are being allocated with the right thread pairs""" + phys0 = [('0', '8'), ('1', '9'), ('10', '2'), ('11', '3')] + phys1 = [('12', '4'), ('13', '5'), ('14', '6'), ('15', '7')] + + taskset_2_2 = list(zip(taskset_list[::2], taskset_list[1::2])) + if taskset_2_2[0] in phys0: + for t in taskset_2_2: + self.assertTrue(tuple(sorted(t)) in phys0, "%s not in %s" % (t, phys0)) + elif taskset_2_2[0] in phys1: + for t in taskset_2_2: + self.assertTrue(tuple(sorted(t)) in phys1, "%s not in %s" % (t, phys1)) + + tasksets0 = self.machine.reserveHT(300) + self.assertIsNotNone(tasksets0) + assertTaskSet(tasksets0.split(",")) + + tasksets1 = self.machine.reserveHT(400) + self.assertIsNotNone(tasksets1) + assertTaskSet(tasksets1.split(",")) # Make sure tastsets don't overlap self.assertTrue(set(tasksets0.split(',')).isdisjoint(tasksets1.split(','))) @@ -507,8 +509,6 @@ def test_reserveHT(self): self.machine.releaseHT(tasksets0) # pylint: disable=no-member self.assertTrue(1 in self.coreDetail.reserved_cores) - # pylint: disable=no-member - self.assertItemsEqual([0, 1, 3], self.coreDetail.reserved_cores[1].coreid) # ------------------------step4------------------------- # phys_id 0 @@ -519,29 +519,12 @@ def test_reserveHT(self): # - process_id 1 # - process_id 9 tasksets3 = self.machine.reserveHT(200) - # pylint: disable=no-member - self.assertItemsEqual(['0', '1', '8', '9'], sorted(tasksets3.split(','))) + assertTaskSet(tasksets3.split(",")) # ------------------------step5------------------------- - # phys_id 0 - # - core_id 2 - # - process_id 2 - # - process_id 10 - # - core_id 3 - # - process_id 3 - # - process_id 11 - # phys_id 1 - # - core_id 2 - # - process_id 6 - # - process_id 14 - tasksets4 = self.machine.reserveHT(300) - # pylint: disable=no-member - self.assertItemsEqual(['2', '10', '3', '11', '6', '14'], sorted(tasksets4.split(','))) - - # ------------------------step6------------------------- - # No cores available + # Missing one core with self.assertRaises(rqd.rqexceptions.CoreReservationFailureException): - self.machine.reserveHT(300) + tasksets4 = self.machine.reserveHT(300) def test_tags(self): @@ -553,9 +536,17 @@ def test_tags(self): self.assertTrue(all(tag in machine.__dict__['_Machine__renderHost'].tags for tag in tags)) -class CpuinfoTests(unittest.TestCase): +@mock.patch('platform.system', new=mock.MagicMock(return_value='Linux')) +class CpuinfoTestsLinux(pyfakefs.fake_filesystem_unittest.TestCase): + @mock.patch('platform.system', new=mock.MagicMock(return_value='Linux')) def setUp(self): + self.setUpPyfakefs() + self.fs.create_file('/proc/cpuinfo', contents=CPUINFO) + self.loadavg = self.fs.create_file('/proc/loadavg', contents=LOADAVG_LOW_USAGE) + self.procStat = self.fs.create_file('/proc/stat', contents=PROC_STAT) + self.meminfo = self.fs.create_file('/proc/meminfo', contents=MEMINFO_MODERATE_USAGE) + self.fs.add_real_directory(os.path.dirname(__file__)) self.rqd = rqd.rqcore.RqCore() def test_shark(self): @@ -591,6 +582,7 @@ def test_srdsvr09(self): def __cpuinfoTestHelper(self, pathCpuInfo): # File format: _cpuinfo_dub_x-x-x where x-x-x is totalCores-coresPerProc-numProcs pathCpuInfo = os.path.join(os.path.dirname(__file__), 'cpuinfo', pathCpuInfo) + self.meminfo.set_contents(MEMINFO_MODERATE_USAGE) renderHost, coreInfo = self.rqd.machine.testInitMachineStats(pathCpuInfo) totalCores, coresPerProc, numProcs = pathCpuInfo.split('_')[-1].split('-')[:3] diff --git a/rqd/tests/rqnimby_tests.py b/rqd/tests/rqnimby_test.py similarity index 98% rename from rqd/tests/rqnimby_tests.py rename to rqd/tests/rqnimby_test.py index 04cf3e765..5459fa32d 100644 --- a/rqd/tests/rqnimby_tests.py +++ b/rqd/tests/rqnimby_test.py @@ -28,6 +28,7 @@ import rqd.rqcore import rqd.rqmachine +import rqd.rqconstants import rqd.rqnimby @@ -38,6 +39,7 @@ def setUp(self): self.setUpPyfakefs() self.inputDevice = self.fs.create_file('/dev/input/event0', contents='mouse event') + rqd.rqconstants.USE_NIMBY_PYNPUT = False self.rqMachine = mock.MagicMock(spec=rqd.rqmachine.Machine) self.rqCore = mock.MagicMock(spec=rqd.rqcore.RqCore) self.rqCore.machine = self.rqMachine