main.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. # -*- coding: utf-8 -*-
  2. """!@package grass.gunittest.main
  3. @brief GRASS Python testing framework module for running from command line
  4. Copyright (C) 2014 by the GRASS Development Team
  5. This program is free software under the GNU General Public
  6. License (>=v2). Read the file COPYING that comes with GRASS GIS
  7. for details.
  8. @author Vaclav Petras
  9. """
  10. import os
  11. import sys
  12. from unittest.main import TestProgram, USAGE_AS_MAIN
  13. TestProgram.USAGE = USAGE_AS_MAIN
  14. from .loader import GrassTestLoader
  15. from .runner import (GrassTestRunner, MultiTestResult,
  16. TextTestResult, KeyValueTestResult)
  17. from .invoker import GrassTestFilesInvoker
  18. from .utils import silent_rmtree
  19. import grass.script.core as gcore
  20. class GrassTestProgram(TestProgram):
  21. """A class to be used by individual test files (wrapped in the function)"""
  22. def __init__(self, exit_at_end, grass_location, clean_outputs=True,
  23. unittest_argv=None, module=None,
  24. verbosity=1,
  25. failfast=None, catchbreak=None):
  26. """Prepares the tests in GRASS way and then runs the tests.
  27. :param bool clean_outputs: if outputs in mapset and in ?
  28. """
  29. self.test = None
  30. self.grass_location = grass_location
  31. # it is unclear what the exact behavior is in unittest
  32. # buffer stdout and stderr during tests
  33. buffer_stdout_stderr = False
  34. grass_loader = GrassTestLoader(grass_location=self.grass_location)
  35. text_result = TextTestResult(stream=sys.stderr,
  36. descriptions=True,
  37. verbosity=verbosity)
  38. keyval_file = open('test_keyvalue_result.txt', 'w')
  39. keyval_result = KeyValueTestResult(stream=keyval_file)
  40. result = MultiTestResult(results=[text_result, keyval_result])
  41. grass_runner = GrassTestRunner(verbosity=verbosity,
  42. failfast=failfast,
  43. buffer=buffer_stdout_stderr,
  44. result=result)
  45. super(GrassTestProgram, self).__init__(module=module,
  46. argv=unittest_argv,
  47. testLoader=grass_loader,
  48. testRunner=grass_runner,
  49. exit=exit_at_end,
  50. verbosity=verbosity,
  51. failfast=failfast,
  52. catchbreak=catchbreak,
  53. buffer=buffer_stdout_stderr)
  54. keyval_file.close()
  55. def test():
  56. """Run a test of a module.
  57. """
  58. # TODO: put the link to to the report only if available
  59. # TODO: how to disable Python code coverage for module and C tests?
  60. # TODO: we probably need to have different test functions for C, Python modules, and Python code
  61. # TODO: combine the results using python -m coverage --help | grep combine
  62. # TODO: function to anonymize/beautify file names (in content and actual filenames)
  63. doing_coverage = False
  64. try:
  65. import coverage
  66. doing_coverage = True
  67. cov = coverage.coverage(omit="*testsuite*")
  68. cov.start()
  69. except ImportError:
  70. pass
  71. # TODO: add some message somewhere
  72. # TODO: enable passing omit to exclude also gunittest or nothing
  73. program = GrassTestProgram(module='__main__', exit_at_end=False, grass_location='all')
  74. # TODO: check if we are in the directory where the test file is
  75. # this will ensure that data directory is available when it is requested
  76. if doing_coverage:
  77. cov.stop()
  78. cov.html_report(directory='testcodecoverage')
  79. # TODO: is sys.exit the right thing here
  80. sys.exit(not program.result.wasSuccessful())
  81. # TODO: test or main? test looks more general
  82. # unittest has main() but doctest has testmod()
  83. main = test
  84. def discovery():
  85. """Recursively find all tests in testsuite directories and run them
  86. Everything is imported and runs in this process.
  87. Runs using::
  88. python main.py discovery [start_directory]
  89. """
  90. doing_coverage = False
  91. try:
  92. import coverage
  93. doing_coverage = True
  94. cov = coverage.coverage(omit="*testsuite*")
  95. cov.start()
  96. except ImportError:
  97. pass
  98. # TODO: add some message somewhere
  99. program = GrassTestProgram(grass_location='nc', exit_at_end=False)
  100. if doing_coverage:
  101. cov.stop()
  102. cov.html_report(directory='testcodecoverage')
  103. sys.exit(not program.result.wasSuccessful())
  104. # TODO: makefile rule should depend on the whole build
  105. # TODO: create a full interface (using grass parser or argparse)
  106. if __name__ == '__main__':
  107. if len(sys.argv) == 4:
  108. gisdbase = sys.argv[1]
  109. location = sys.argv[2]
  110. location_shortcut = sys.argv[3]
  111. elif len(sys.argv) == 3:
  112. location = sys.argv[1]
  113. location_shortcut = sys.argv[2]
  114. gisdbase = gcore.gisenv()['GISDBASE']
  115. else:
  116. sys.stderr.write("Usage: %s [gisdbase] location location_shortcut\n" % sys.argv[0])
  117. sys.exit(1)
  118. assert gisdbase
  119. if not os.path.exists(gisdbase):
  120. sys.stderr.write("GISDBASE <%s> does not exist\n" % gisdbase)
  121. sys.exit(1)
  122. results_dir = 'testreport'
  123. silent_rmtree(results_dir) # TODO: too brute force?
  124. invoker = GrassTestFilesInvoker(start_dir='.')
  125. # we can just iterate over all locations available in database
  126. # but the we don't know the right location label/shortcut
  127. invoker.run_in_location(gisdbase=gisdbase,
  128. location=location,
  129. location_shortcut=location_shortcut,
  130. results_dir=results_dir)