PATCH: Improve CompilerTest

Mark Mitchell mark at codesourcery.com
Thu Dec 1 07:34:01 UTC 2005


This patch contains several little tweaks to CompilerTest:

* You can now provide arguments to the program generated by the
  compiler, e.g., if the compiler generates a program named "foo", you
  can run it like "foo arg1 arg2".

* You can now decide that "foo" need not exit with code zero, without
  causing the test to fail.

* You can validate that the output from the executable is what you
  expect by implementing _CheckExecutableOutput.

--
Mark Mitchell
CodeSourcery, LLC
mark at codesourcery.com

2005-11-30  Mark Mitchell  <mark at codesourcery.com>

	* qm/test/classes/compiler_test.py (CompilationStep): Provide default 
	values.
	(CompilerBase._MakeDirectory): Do not create the temporary build
	directory if it already exists.
	(CompilerTest.Run):  Create the temporary build directory.  Record
	the output from the compiler here.
	(CompilerTest._GetExecutableArguments): New function.
	(CompilerTest._MustExecutableExitSuccessfully): New function.
	(CompilerTest._RunExecutable): Use new functions.
	(CompilerTest._CheckOutput): Do not record the output here.
	Adjust use of _DiagnosticsToString.
	(CompilerTest._CheckExecutableOutput): New function.
	(CompilerTest._DiagnosticsToString): Add annotation parameter.

Index: qm/test/classes/compiler_test.py
===================================================================
RCS file: /home/qm/Repository/qm/qm/test/classes/compiler_test.py,v
retrieving revision 1.4
diff -c -5 -p -r1.4 compiler_test.py
*** qm/test/classes/compiler_test.py	1 Dec 2005 06:46:43 -0000	1.4
--- qm/test/classes/compiler_test.py	1 Dec 2005 07:29:30 -0000
*************** import os, string
*** 23,33 ****
  ########################################################################
  
  class CompilationStep:
      """A single compilation step."""
  
!     def __init__(self, mode, files, options, output, diagnostics):
          """Construct a new 'CompilationStep'.
  
          'mode' -- As for 'Compiler.Compile'.
  
          'files' -- As for 'Compiler.Compile'.
--- 23,34 ----
  ########################################################################
  
  class CompilationStep:
      """A single compilation step."""
  
!     def __init__(self, mode, files, options = [],
!                  output = None , diagnostics = []):
          """Construct a new 'CompilationStep'.
  
          'mode' -- As for 'Compiler.Compile'.
  
          'files' -- As for 'Compiler.Compile'.
*************** class CompilerBase:
*** 76,87 ****
          returns -- The name of the directory."""
  
          # Get the directory name.
          directory = self._GetDirectory(context)
          # Create it.
!         os.makedirs(directory)
! 
          return directory
  
  
      def _RemoveDirectory(self, context, result):
          """Remove the directory in which generated files are placed.
--- 77,88 ----
          returns -- The name of the directory."""
  
          # Get the directory name.
          directory = self._GetDirectory(context)
          # Create it.
!         if not os.path.exists(directory):
!             os.makedirs(directory)
          return directory
  
  
      def _RemoveDirectory(self, context, result):
          """Remove the directory in which generated files are placed.
*************** class CompilerTest(Test, CompilerBase):
*** 143,152 ****
--- 144,155 ----
          executable_path = None
          # See what we need to run this test.
          steps = self._GetCompilationSteps(context)
          # See if we need to run this test.
          is_execution_required = self._IsExecutionRequired()
+         # Create the temporary build directory.
+         self._MakeDirectory(context)
          
          # Keep track of which compilation step we are performing so
          # that we can annotate the result appropriately.
          step_index = 1
  
*************** class CompilerTest(Test, CompilerBase):
*** 164,174 ****
              # Run the compiler.
              timeout = context.get("CompilerTest.compilation_timeout", -1)
              (status, output) \
                  = compiler.ExecuteCommand(self._GetDirectory(context),
                                            command, timeout)
! 
               # Make sure that the output is OK.
              if not self._CheckOutput(context, result, prefix, output,
                                       step.diagnostics):
                  # If there were errors, do not try to run the program.
                  is_execution_required = 0
--- 167,179 ----
              # Run the compiler.
              timeout = context.get("CompilerTest.compilation_timeout", -1)
              (status, output) \
                  = compiler.ExecuteCommand(self._GetDirectory(context),
                                            command, timeout)
!             # Annotate the result with the output.
!             if output:
!                 result[prefix + "output"] = result.Quote(output)
               # Make sure that the output is OK.
              if not self._CheckOutput(context, result, prefix, output,
                                       step.diagnostics):
                  # If there were errors, do not try to run the program.
                  is_execution_required = 0
*************** class CompilerTest(Test, CompilerBase):
*** 232,241 ****
--- 237,266 ----
  
          returns -- True if the generated executable should be run."""
  
          return 0
          
+ 
+     def _GetExecutableArguments(self):
+         """Returns the arguments to the generated executable.
+ 
+         returns -- A list of strings, to be passed as argumensts to
+         the generated executable.""" 
+ 
+         return []
+ 
+     
+     def _MustExecutableExitSuccessfully(self):
+         """Returns true if the executable must exit with code zero.
+ 
+         returns -- True if the generated executable (if any) must exit
+         with code zero.  Note that the executable will not be run at
+         all (and so the return value of this function will be ignored)
+         if '_IsExecutionRequired' does not return true."""
+ 
+         return True
+         
          
      def _GetAnnotationPrefix(self):
          """Return the prefix to use for result annotations.
  
          returns -- The prefix to use for result annotations."""
*************** class CompilerTest(Test, CompilerBase):
*** 270,280 ****
  
          # Compute the result annotation prefix.
          prefix = self._GetAnnotationPrefix() + "execution_"
          # Record the command line.
          path = os.path.join(self._GetDirectory(context), path)
!         result[prefix + "command"] = "<tt>" + path + "</tt>"
  
          # Compute the environment.
          library_dirs = self._GetLibraryDirectories(context)
          if library_dirs:
              # Update LD_LIBRARY_PATH.  On IRIX 6, this variable
--- 295,307 ----
  
          # Compute the result annotation prefix.
          prefix = self._GetAnnotationPrefix() + "execution_"
          # Record the command line.
          path = os.path.join(self._GetDirectory(context), path)
!         arguments = self._GetExecutableArguments()
!         result[prefix + "command"] \
!            = "<tt>" + path + " " + " ".join(arguments) + "</tt>"
  
          # Compute the environment.
          library_dirs = self._GetLibraryDirectories(context)
          if library_dirs:
              # Update LD_LIBRARY_PATH.  On IRIX 6, this variable
*************** class CompilerTest(Test, CompilerBase):
*** 293,309 ****
              environment = None
  
          target = self._GetTarget(context)
          timeout = context.get("CompilerTest.execution_timeout", -1)
          status, output = target.UploadAndRun(path,
!                                              [],
                                               environment,
                                               timeout)
          # Record the output.
          result[prefix + "output"] = result.Quote(output)
          # Check the output status.
!         result.CheckExitStatus(prefix, "Executable", status)
  
  
      def _CheckOutput(self, context, result, prefix, output, diagnostics):
          """Check that the 'output' contains appropriate diagnostics.
  
--- 320,338 ----
              environment = None
  
          target = self._GetTarget(context)
          timeout = context.get("CompilerTest.execution_timeout", -1)
          status, output = target.UploadAndRun(path,
!                                              arguments,
                                               environment,
                                               timeout)
          # Record the output.
          result[prefix + "output"] = result.Quote(output)
+         self._CheckExecutableOutput(result, output)
          # Check the output status.
!         result.CheckExitStatus(prefix, "Executable", status,
!                                not self._MustExecutableExitSuccessfully())
  
  
      def _CheckOutput(self, context, result, prefix, output, diagnostics):
          """Check that the 'output' contains appropriate diagnostics.
  
*************** class CompilerTest(Test, CompilerBase):
*** 321,335 ****
          compilation.
  
          returns -- True if there were no errors so severe as to
          prevent execution of the test."""
  
-         # Annotate the result with the output.
-         if output:
-             result[prefix + "output"] \
-                 = result.Quote(output)
- 
          # Get the compiler to use to parse the output.
          compiler = self._GetCompiler(context)
          
          # Parse the output.
          emitted_diagnostics \
--- 350,359 ----
*************** class CompilerTest(Test, CompilerBase):
*** 383,403 ****
              else:
                  result.Fail("Spurious diagnostics.")
  
              # Add annotations showing the problem.
              if spurious_diagnostics:
!                 result[self._GetAnnotationPrefix() + "spurious_diagnostics"] \
!                   = self._DiagnosticsToString(spurious_diagnostics)
              if missing_diagnostics:
!                 result[self._GetAnnotationPrefix() + "missing_diagnostics"] \
!                   = self._DiagnosticsToString(missing_diagnostics)
  
          # If errors occurred, there is no point in trying to run
          # the executable.
          return not errors_occurred
  
  
      def _IsDiagnosticExpected(self, emitted, expected):
          """Returns true if 'emitted' matches 'expected'.
  
          'emitted' -- A 'Diagnostic emitted by the compiler.
          
--- 407,442 ----
              else:
                  result.Fail("Spurious diagnostics.")
  
              # Add annotations showing the problem.
              if spurious_diagnostics:
!                 self._DiagnosticsToString(result, 
!                                           "spurious_diagnostics",
!                                           spurious_diagnostics)
              if missing_diagnostics:
!                 self._DiagnosticsToString(result, 
!                                           "missing_diagnostics",
!                                           missing_diagnostics)
  
          # If errors occurred, there is no point in trying to run
          # the executable.
          return not errors_occurred
  
  
+     def _CheckExecutableOutput(self, result, output):
+         """Checks the output from the generated executable.
+ 
+         'result' -- The 'Result' object for this test.
+ 
+         'output' -- The output generated by the executable.
+ 
+         If the output is unsatisfactory, 'result' is modified
+         appropriately."""
+         
+         pass
+ 
+     
      def _IsDiagnosticExpected(self, emitted, expected):
          """Returns true if 'emitted' matches 'expected'.
  
          'emitted' -- A 'Diagnostic emitted by the compiler.
          
*************** class CompilerTest(Test, CompilerBase):
*** 430,446 ****
  
          # There's a match.
          return 1
  
  
!     def _DiagnosticsToString(self, diagnostics):
          """Return a string representing the 'diagnostics'.
  
          'diagnostics' -- A sequence of 'Diagnostic' instances.
  
          returns -- A string representing the 'Diagnostic's, with one
          diagnostic message per line."""
  
          # Compute the string representation of each diagnostic.
          diagnostic_strings = map(str, diagnostics)
          # Insert a newline between each string.
!         return Result.Quote("\n".join(diagnostic_strings))
--- 469,486 ----
  
          # There's a match.
          return 1
  
  
!     def _DiagnosticsToString(self, result, annotation, diagnostics):
          """Return a string representing the 'diagnostics'.
  
          'diagnostics' -- A sequence of 'Diagnostic' instances.
  
          returns -- A string representing the 'Diagnostic's, with one
          diagnostic message per line."""
  
          # Compute the string representation of each diagnostic.
          diagnostic_strings = map(str, diagnostics)
          # Insert a newline between each string.
!         result[self._GetAnnotationPrefix() + annotation] \
!             = result.Quote("\n".join(diagnostic_strings))



More information about the qmtest mailing list