misc: Merge branch 'release-staging-v20.1.0.0' into develop
[gem5.git] / tests / gem5 / fixture.py
index e84b89f6992ecf6c1b561a915dc69de682c5e51d..bb911dde0ff28fe796ba3f1d30e8baa9dc4a9254 100644 (file)
 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-# Authors: Sean Wilson
 
 import os
 import tempfile
 import shutil
+import sys
+import socket
 import threading
 
+from six.moves import urllib
+
 from testlib.fixture import Fixture
-from testlib.config import config, constants
+from testlib.configuration import config, constants
 from testlib.helper import log_call, cacheresult, joinpath, absdirpath
 import testlib.log as log
+from testlib.state import Result
 
 
 class VariableFixture(Fixture):
@@ -64,14 +67,27 @@ class TempdirFixture(Fixture):
     def setup(self, testitem):
         self.path = tempfile.mkdtemp(prefix='gem5out')
 
+    def post_test_procedure(self, testitem):
+        suiteUID = testitem.metadata.uid.suite
+        testUID = testitem.metadata.name
+        testing_result_folder = os.path.join(config.result_path,
+                                             "SuiteUID:" + suiteUID,
+                                             "TestUID:" + testUID)
+
+        # Copy the output files of the run from /tmp to testing-results
+        # We want to wipe the entire result folder for this test first. Why?
+        #   If the result folder exists (probably from the previous run), if
+        #   this run emits fewer files, there'll be files from the previous
+        #   run in this folder, which would cause confusion if one does not
+        #   check the timestamp of the file.
+        if os.path.exists(testing_result_folder):
+            shutil.rmtree(testing_result_folder)
+        shutil.copytree(self.path, testing_result_folder)
+
     def teardown(self, testitem):
-        if self.path is not None:
+        if testitem.result == Result.Passed:
             shutil.rmtree(self.path)
 
-    def skip_cleanup(self):
-        # Set path to none so it's not deleted
-        self.path = None
-
 class UniqueFixture(Fixture):
     '''
     Base class for fixtures that generate a target in the
@@ -154,7 +170,7 @@ class SConsFixture(UniqueFixture):
         command.extend(self.targets)
         if self.options:
             command.extend(self.options)
-        log_call(log.test_log, command)
+        log_call(log.test_log, command, stderr=sys.stderr)
 
 class Gem5Fixture(SConsFixture):
     def __new__(cls, isa, variant, protocol=None):
@@ -192,7 +208,7 @@ class MakeFixture(Fixture):
         targets = set(self.required_by)
         command = ['make', '-C', self.directory]
         command.extend([target.target for target in targets])
-        log_call(command)
+        log_call(log.test_log, command, stderr=sys.stderr)
 
 
 class MakeTarget(Fixture):
@@ -224,7 +240,7 @@ class MakeTarget(Fixture):
 
 class TestProgram(MakeTarget):
     def __init__(self, program, isa, os, recompile=False):
-        make_dir = joinpath('test-progs', program)
+        make_dir = joinpath(config.bin_dir, program)
         make_fixture = MakeFixture(make_dir)
         target = joinpath('bin', isa, os, program)
         super(TestProgram, self).__init__(target, make_fixture)
@@ -239,63 +255,90 @@ class TestProgram(MakeTarget):
         elif not os.path.exists(self.path):
             super(MakeTarget, self).setup()
 
-class DownloadedProgram(Fixture):
+class DownloadedProgram(UniqueFixture):
     """ Like TestProgram, but checks the version in the gem5 binary repository
         and downloads an updated version if it is needed.
     """
-    urlbase = "http://gem5.org/dist/current/"
 
-    def __init__(self, path, program, **kwargs):
+    def __new__(cls, url, path, filename):
+        target = joinpath(path, filename)
+        return super(DownloadedProgram, cls).__new__(cls, target)
+
+    def _init(self, url, path, filename, **kwargs):
         """
+        url: string
+            The url of the archive
         path: string
-            The path to the directory containing the binary relative to
-            $GEM5_BASE/tests
-        program: string
-            The name of the binary file
+            The absolute path of the directory containing the archive
+        filename: string
+            The name of the archive
         """
-        super(DownloadedProgram, self).__init__("download-" + program,
-                                                build_once=True, **kwargs)
 
-        self.program_dir = path
-        relative_path = joinpath(self.program_dir, program)
-        self.url = self.urlbase + relative_path
-        self.path = os.path.realpath(
-                        joinpath(absdirpath(__file__), '../', relative_path)
-                    )
+        self.url = url
+        self.path = path
+        self.filename = joinpath(path, filename)
+        self.name = "Downloaded:" + self.filename
 
     def _download(self):
-        import urllib
         import errno
         log.test_log.debug("Downloading " + self.url + " to " + self.path)
-        if not os.path.exists(self.program_dir):
+        if not os.path.exists(self.path):
             try:
-                os.makedirs(self.program_dir)
+                os.makedirs(self.path)
             except OSError as e:
                 if e.errno != errno.EEXIST:
                     raise
-        urllib.urlretrieve(self.url, self.path)
+        urllib.request.urlretrieve(self.url, self.filename)
 
     def _getremotetime(self):
-        import  urllib2, datetime, time
+        import datetime, time
         import _strptime # Needed for python threading bug
 
-        u = urllib2.urlopen(self.url)
+        u = urllib.request.urlopen(self.url, timeout=10)
+
         return time.mktime(datetime.datetime.strptime( \
-                    u.info().getheaders("Last-Modified")[0],
+                    u.info()["Last-Modified"],
                     "%a, %d %b %Y %X GMT").timetuple())
 
-    def setup(self, testitem):
-        import urllib2
+    def _setup(self, testitem):
         # Check to see if there is a file downloaded
-        if not os.path.exists(self.path):
+        if not os.path.exists(self.filename):
+            self._download()
+        else:
+            try:
+                t = self._getremotetime()
+            except (urllib.error.URLError, socket.timeout):
+                # Problem checking the server, use the old files.
+                log.test_log.debug("Could not contact server. Binaries may be old.")
+                return
+            # If the server version is more recent, download it
+            if t > os.path.getmtime(self.filename):
+                self._download()
+
+class DownloadedArchive(DownloadedProgram):
+    """ Like TestProgram, but checks the version in the gem5 binary repository
+        and downloads an updated version if it is needed.
+    """
+
+    def _extract(self):
+        import tarfile
+        with tarfile.open(self.filename) as tf:
+            tf.extractall(self.path)
+
+    def _setup(self, testitem):
+        # Check to see if there is a file downloaded
+        if not os.path.exists(self.filename):
             self._download()
+            self._extract()
         else:
             try:
                 t = self._getremotetime()
-            except urllib2.URLError:
+            except (urllib.error.URLError, socket.timeout):
                 # Problem checking the server, use the old files.
-                log.debug("Could not contact server. Binaries may be old.")
+                log.test_log.debug("Could not contact server. "
+                                   "Binaries may be old.")
                 return
             # If the server version is more recent, download it
-            if t > os.path.getmtime(self.path):
+            if t > os.path.getmtime(self.filename):
                 self._download()
+                self._extract()