def test_run_pipe_it_should_not_log_by_default(self): self.__it_should_not_log_by_default(lambda: shellutil.run_pipe([ ["date"], [self.__create_tee_script(return_code=1)] ])) # Raises a CommandError self.__it_should_not_log_by_default(lambda: shellutil.run_pipe( [["date"], ["nonexistent_command"]])) # Raises an OSError
def test_run_pipe_should_capture_the_stderr_of_all_the_commands_in_the_pipe( self): with self.assertRaises(shellutil.CommandError) as context_manager: shellutil.run_pipe([["echo", "TEST STRING"], [self.__create_tee_script()], [self.__create_tee_script()], [self.__create_tee_script(return_code=1)]]) self.assertEqual( "TEST STRING\n" * 3, context_manager.exception.stderr, "Expected 3 copies of the test string since there are 3 commands in the pipe" )
def test_run_pipe_should_raise_an_exception_when_the_last_command_fails( self): tee_script = self.__create_tee_script(return_code=1) self.__it_should_raise_an_exception_when_the_command_fails( lambda: shellutil.run_pipe([["echo", "-n", "TEST_STRING\n"], [tee_script]]))
def test_run_pipe_should_return_a_string_by_default(self): output = shellutil.run_pipe([["echo", "TEST STRING"], [self.__create_tee_script()]]) self.assertTrue( isinstance(output, ustr), "The return value should be a string. Got: '{0}'".format( type(output)))
def test_run_pipe_should_execute_a_pipe_with_two_commands(self): # Output the same string 3 times and then remove duplicates test_string = "A TEST STRING\n" pipe = [["echo", "-n", "-e", test_string * 3], ["uniq"]] output = shellutil.run_pipe(pipe) self.assertEqual(output, test_string)
def test_run_pipe_should_return_a_bytes_object_when_encode_output_is_false( self): output = shellutil.run_pipe( [["echo", "TEST STRING"], [self.__create_tee_script()]], encode_output=False) self.assertTrue( isinstance(output, bytes), "The return value should be a bytes object. Got: '{0}'".format( type(output)))
def test_run_pipe_should_execute_a_pipe_with_more_than_two_commands(self): # # The test pipe splits the output of "ls" in lines and then greps for "." # # Sample output of "ls -d .": # drwxrwxr-x 13 nam nam 4096 Nov 13 16:54 . # pipe = [["ls", "-ld", "."], ["sed", "-r", "s/\\s+/\\n/g"], ["grep", "\\."]] output = shellutil.run_pipe(pipe) self.assertEqual( ".\n", output, "The pipe did not produce the expected output. Got: {0}".format( output))
def test_run_command_run_pipe_run_get_output_should_keep_track_of_the_running_commands( self): # The children processes run this script, which creates a file with the PIDs of the script and its parent and then sleeps for a long time child_script = os.path.join(self.tmp_dir, "write_pids.py") AgentTestCase.create_script( child_script, """ import os import sys import time with open(sys.argv[1], "w") as pid_file: pid_file.write("{0} {1}".format(os.getpid(), os.getppid())) time.sleep(120) """) threads = [] try: child_processes = [] parent_processes = [] try: # each of these files will contain the PIDs of the command that created it and its parent pid_files = [ os.path.join(self.tmp_dir, "pids.txt.{0}".format(i)) for i in range(4) ] # we test these functions in shellutil commands_to_execute = [ # run_get_output must be the first in this list; see the code to fetch the PIDs a few lines below lambda: shellutil.run_get_output("{0} {1}".format( child_script, pid_files[0])), lambda: shellutil.run_command([child_script, pid_files[1]] ), lambda: shellutil.run_pipe([[child_script, pid_files[2]], [child_script, pid_files[3]]]), ] # start each command on a separate thread (since we need to examine the processes running the commands while they are running) def invoke(command): try: command() except shellutil.CommandError as command_error: if command_error.returncode != -9: # test cleanup terminates the commands, so this is expected raise for cmd in commands_to_execute: thread = threading.Thread(target=invoke, args=(cmd, )) thread.start() threads.append(thread) # now fetch the PIDs in the files created by the commands, but wait until they are created if not wait_for(lambda: all( os.path.exists(file) and os.path.getsize(file) > 0 for file in pid_files)): raise Exception( "The child processes did not start within the allowed timeout" ) for sig_file in pid_files: with open(sig_file, "r") as read_handle: pids = read_handle.read().split() child_processes.append(int(pids[0])) parent_processes.append(int(pids[1])) # the first item to in the PIDs we fetched corresponds to run_get_output, which invokes the command using the # shell, so in that case we need to use the parent's pid (i.e. the shell that we started) started_commands = parent_processes[0:1] + child_processes[1:] # wait for all the commands to start def all_commands_running(): all_commands_running.running_commands = shellutil.get_running_commands( ) return len( all_commands_running.running_commands ) >= len(commands_to_execute ) + 1 # +1 because run_pipe starts 2 commands all_commands_running.running_commands = [] if not wait_for(all_commands_running): self.fail( "shellutil.get_running_commands() did not report the expected number of commands after the allowed timeout.\nExpected: {0}\nGot: {1}" .format( format_processes(started_commands), format_processes( all_commands_running.running_commands))) started_commands.sort() all_commands_running.running_commands.sort() self.assertEqual( started_commands, all_commands_running.running_commands, "shellutil.get_running_commands() did not return the expected commands.\nExpected: {0}\nGot: {1}" .format( format_processes(started_commands), format_processes( all_commands_running.running_commands))) finally: # terminate the child processes, since they are blocked for pid in child_processes: os.kill(pid, signal.SIGKILL) # once the processes complete, their PIDs should go away def no_commands_running(): no_commands_running.running_commands = shellutil.get_running_commands( ) return len(no_commands_running.running_commands) == 0 no_commands_running.running_commands = [] if not wait_for(no_commands_running): self.fail( "shellutil.get_running_commands() should return empty after the commands complete. Got: {0}" .format( format_processes( no_commands_running.running_commands))) finally: for thread in threads: thread.join(timeout=5)
def test_run_pipe_should_write_the_command_error_output_to_the_stderr_parameter( self): self.__it_should_write_the_command_error_output_to_the_stderr_parameter( lambda stderr: shellutil.run_pipe([["echo", "TEST STRING"], [self.__create_tee_script()]], stderr=stderr))
def test_run_pipe_should_write_the_command_output_to_the_stdout_parameter( self): self.__it_should_write_the_command_output_to_the_stdout_parameter( lambda stdout: shellutil.run_pipe( [["echo", "TEST STRING"], ["sort"]], stdout=stdout))
def test_run_pipe_should_read_the_command_input_from_the_stdin_parameter( self): self.__it_should_read_the_command_input_from_the_stdin_parameter( lambda stdin: shellutil.run_pipe([["cat"], ["sort"]], stdin=stdin))
def test_run_pipe_should_raise_an_exception_when_it_cannot_execute_the_pipe( self): self.__it_should_raise_an_exception_when_it_cannot_execute_the_command( lambda: shellutil.run_pipe( [["ls", "-ld", "."], ["nonexistent_command"], ["wc", "-l"]]))