Home | History | Annotate | Download | only in unittests
      1 #!/usr/bin/env python
      2 # Copyright 2014 the V8 project authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 from collections import namedtuple
      7 import coverage
      8 import json
      9 from mock import DEFAULT
     10 from mock import MagicMock
     11 import os
     12 from os import path, sys
     13 import shutil
     14 import tempfile
     15 import unittest
     16 
     17 # Requires python-coverage and python-mock. Native python coverage
     18 # version >= 3.7.1 should be installed to get the best speed.
     19 
     20 TEST_WORKSPACE = path.join(tempfile.gettempdir(), "test-v8-run-perf")
     21 
     22 V8_JSON = {
     23   "path": ["."],
     24   "binary": "d7",
     25   "flags": ["--flag"],
     26   "main": "run.js",
     27   "run_count": 1,
     28   "results_regexp": "^%s: (.+)$",
     29   "tests": [
     30     {"name": "Richards"},
     31     {"name": "DeltaBlue"},
     32   ]
     33 }
     34 
     35 V8_NESTED_SUITES_JSON = {
     36   "path": ["."],
     37   "flags": ["--flag"],
     38   "run_count": 1,
     39   "units": "score",
     40   "tests": [
     41     {"name": "Richards",
     42      "path": ["richards"],
     43      "binary": "d7",
     44      "main": "run.js",
     45      "resources": ["file1.js", "file2.js"],
     46      "run_count": 2,
     47      "results_regexp": "^Richards: (.+)$"},
     48     {"name": "Sub",
     49      "path": ["sub"],
     50      "tests": [
     51        {"name": "Leaf",
     52         "path": ["leaf"],
     53         "run_count_x64": 3,
     54         "units": "ms",
     55         "main": "run.js",
     56         "results_regexp": "^Simple: (.+) ms.$"},
     57      ]
     58     },
     59     {"name": "DeltaBlue",
     60      "path": ["delta_blue"],
     61      "main": "run.js",
     62      "flags": ["--flag2"],
     63      "results_regexp": "^DeltaBlue: (.+)$"},
     64     {"name": "ShouldntRun",
     65      "path": ["."],
     66      "archs": ["arm"],
     67      "main": "run.js"},
     68   ]
     69 }
     70 
     71 V8_GENERIC_JSON = {
     72   "path": ["."],
     73   "binary": "cc",
     74   "flags": ["--flag"],
     75   "generic": True,
     76   "run_count": 1,
     77   "units": "ms",
     78 }
     79 
     80 Output = namedtuple("Output", "stdout, stderr, timed_out")
     81 
     82 class PerfTest(unittest.TestCase):
     83   @classmethod
     84   def setUpClass(cls):
     85     cls.base = path.dirname(path.dirname(path.abspath(__file__)))
     86     sys.path.append(cls.base)
     87     cls._cov = coverage.coverage(
     88         include=([os.path.join(cls.base, "run_perf.py")]))
     89     cls._cov.start()
     90     import run_perf
     91     from testrunner.local import commands
     92     global commands
     93     global run_perf
     94 
     95   @classmethod
     96   def tearDownClass(cls):
     97     cls._cov.stop()
     98     print ""
     99     print cls._cov.report()
    100 
    101   def setUp(self):
    102     self.maxDiff = None
    103     if path.exists(TEST_WORKSPACE):
    104       shutil.rmtree(TEST_WORKSPACE)
    105     os.makedirs(TEST_WORKSPACE)
    106 
    107   def tearDown(self):
    108     if path.exists(TEST_WORKSPACE):
    109       shutil.rmtree(TEST_WORKSPACE)
    110 
    111   def _WriteTestInput(self, json_content):
    112     self._test_input = path.join(TEST_WORKSPACE, "test.json")
    113     with open(self._test_input, "w") as f:
    114       f.write(json.dumps(json_content))
    115 
    116   def _MockCommand(self, *args, **kwargs):
    117     # Fake output for each test run.
    118     test_outputs = [Output(stdout=arg,
    119                            stderr=None,
    120                            timed_out=kwargs.get("timed_out", False))
    121                     for arg in args[1]]
    122     def execute(*args, **kwargs):
    123       return test_outputs.pop()
    124     commands.Execute = MagicMock(side_effect=execute)
    125 
    126     # Check that d8 is called from the correct cwd for each test run.
    127     dirs = [path.join(TEST_WORKSPACE, arg) for arg in args[0]]
    128     def chdir(*args, **kwargs):
    129       self.assertEquals(dirs.pop(), args[0])
    130     os.chdir = MagicMock(side_effect=chdir)
    131 
    132   def _CallMain(self, *args):
    133     self._test_output = path.join(TEST_WORKSPACE, "results.json")
    134     all_args=[
    135       "--json-test-results",
    136       self._test_output,
    137       self._test_input,
    138     ]
    139     all_args += args
    140     return run_perf.Main(all_args)
    141 
    142   def _LoadResults(self):
    143     with open(self._test_output) as f:
    144       return json.load(f)
    145 
    146   def _VerifyResults(self, suite, units, traces):
    147     self.assertEquals([
    148       {"units": units,
    149        "graphs": [suite, trace["name"]],
    150        "results": trace["results"],
    151        "stddev": trace["stddev"]} for trace in traces],
    152       self._LoadResults()["traces"])
    153 
    154   def _VerifyErrors(self, errors):
    155     self.assertEquals(errors, self._LoadResults()["errors"])
    156 
    157   def _VerifyMock(self, binary, *args, **kwargs):
    158     arg = [path.join(path.dirname(self.base), binary)]
    159     arg += args
    160     commands.Execute.assert_called_with(
    161         arg, timeout=kwargs.get("timeout", 60))
    162 
    163   def _VerifyMockMultiple(self, *args, **kwargs):
    164     expected = []
    165     for arg in args:
    166       a = [path.join(path.dirname(self.base), arg[0])]
    167       a += arg[1:]
    168       expected.append(((a,), {"timeout": kwargs.get("timeout", 60)}))
    169     self.assertEquals(expected, commands.Execute.call_args_list)
    170 
    171   def testOneRun(self):
    172     self._WriteTestInput(V8_JSON)
    173     self._MockCommand(["."], ["x\nRichards: 1.234\nDeltaBlue: 10657567\ny\n"])
    174     self.assertEquals(0, self._CallMain())
    175     self._VerifyResults("test", "score", [
    176       {"name": "Richards", "results": ["1.234"], "stddev": ""},
    177       {"name": "DeltaBlue", "results": ["10657567"], "stddev": ""},
    178     ])
    179     self._VerifyErrors([])
    180     self._VerifyMock(path.join("out", "x64.release", "d7"), "--flag", "run.js")
    181 
    182   def testTwoRuns_Units_SuiteName(self):
    183     test_input = dict(V8_JSON)
    184     test_input["run_count"] = 2
    185     test_input["name"] = "v8"
    186     test_input["units"] = "ms"
    187     self._WriteTestInput(test_input)
    188     self._MockCommand([".", "."],
    189                       ["Richards: 100\nDeltaBlue: 200\n",
    190                        "Richards: 50\nDeltaBlue: 300\n"])
    191     self.assertEquals(0, self._CallMain())
    192     self._VerifyResults("v8", "ms", [
    193       {"name": "Richards", "results": ["50", "100"], "stddev": ""},
    194       {"name": "DeltaBlue", "results": ["300", "200"], "stddev": ""},
    195     ])
    196     self._VerifyErrors([])
    197     self._VerifyMock(path.join("out", "x64.release", "d7"), "--flag", "run.js")
    198 
    199   def testTwoRuns_SubRegexp(self):
    200     test_input = dict(V8_JSON)
    201     test_input["run_count"] = 2
    202     del test_input["results_regexp"]
    203     test_input["tests"][0]["results_regexp"] = "^Richards: (.+)$"
    204     test_input["tests"][1]["results_regexp"] = "^DeltaBlue: (.+)$"
    205     self._WriteTestInput(test_input)
    206     self._MockCommand([".", "."],
    207                       ["Richards: 100\nDeltaBlue: 200\n",
    208                        "Richards: 50\nDeltaBlue: 300\n"])
    209     self.assertEquals(0, self._CallMain())
    210     self._VerifyResults("test", "score", [
    211       {"name": "Richards", "results": ["50", "100"], "stddev": ""},
    212       {"name": "DeltaBlue", "results": ["300", "200"], "stddev": ""},
    213     ])
    214     self._VerifyErrors([])
    215     self._VerifyMock(path.join("out", "x64.release", "d7"), "--flag", "run.js")
    216 
    217   def testNestedSuite(self):
    218     self._WriteTestInput(V8_NESTED_SUITES_JSON)
    219     self._MockCommand(["delta_blue", "sub/leaf", "richards"],
    220                       ["DeltaBlue: 200\n",
    221                        "Simple: 1 ms.\n",
    222                        "Simple: 2 ms.\n",
    223                        "Simple: 3 ms.\n",
    224                        "Richards: 100\n",
    225                        "Richards: 50\n"])
    226     self.assertEquals(0, self._CallMain())
    227     self.assertEquals([
    228       {"units": "score",
    229        "graphs": ["test", "Richards"],
    230        "results": ["50", "100"],
    231        "stddev": ""},
    232       {"units": "ms",
    233        "graphs": ["test", "Sub", "Leaf"],
    234        "results": ["3", "2", "1"],
    235        "stddev": ""},
    236       {"units": "score",
    237        "graphs": ["test", "DeltaBlue"],
    238        "results": ["200"],
    239        "stddev": ""},
    240       ], self._LoadResults()["traces"])
    241     self._VerifyErrors([])
    242     self._VerifyMockMultiple(
    243         (path.join("out", "x64.release", "d7"), "--flag", "file1.js",
    244          "file2.js", "run.js"),
    245         (path.join("out", "x64.release", "d7"), "--flag", "file1.js",
    246          "file2.js", "run.js"),
    247         (path.join("out", "x64.release", "d8"), "--flag", "run.js"),
    248         (path.join("out", "x64.release", "d8"), "--flag", "run.js"),
    249         (path.join("out", "x64.release", "d8"), "--flag", "run.js"),
    250         (path.join("out", "x64.release", "d8"), "--flag", "--flag2", "run.js"))
    251 
    252   def testOneRunStdDevRegExp(self):
    253     test_input = dict(V8_JSON)
    254     test_input["stddev_regexp"] = "^%s\-stddev: (.+)$"
    255     self._WriteTestInput(test_input)
    256     self._MockCommand(["."], ["Richards: 1.234\nRichards-stddev: 0.23\n"
    257                               "DeltaBlue: 10657567\nDeltaBlue-stddev: 106\n"])
    258     self.assertEquals(0, self._CallMain())
    259     self._VerifyResults("test", "score", [
    260       {"name": "Richards", "results": ["1.234"], "stddev": "0.23"},
    261       {"name": "DeltaBlue", "results": ["10657567"], "stddev": "106"},
    262     ])
    263     self._VerifyErrors([])
    264     self._VerifyMock(path.join("out", "x64.release", "d7"), "--flag", "run.js")
    265 
    266   def testTwoRunsStdDevRegExp(self):
    267     test_input = dict(V8_JSON)
    268     test_input["stddev_regexp"] = "^%s\-stddev: (.+)$"
    269     test_input["run_count"] = 2
    270     self._WriteTestInput(test_input)
    271     self._MockCommand(["."], ["Richards: 3\nRichards-stddev: 0.7\n"
    272                               "DeltaBlue: 6\nDeltaBlue-boom: 0.9\n",
    273                               "Richards: 2\nRichards-stddev: 0.5\n"
    274                               "DeltaBlue: 5\nDeltaBlue-stddev: 0.8\n"])
    275     self.assertEquals(1, self._CallMain())
    276     self._VerifyResults("test", "score", [
    277       {"name": "Richards", "results": ["2", "3"], "stddev": "0.7"},
    278       {"name": "DeltaBlue", "results": ["5", "6"], "stddev": "0.8"},
    279     ])
    280     self._VerifyErrors(
    281         ["Test Richards should only run once since a stddev is provided "
    282          "by the test.",
    283          "Test DeltaBlue should only run once since a stddev is provided "
    284          "by the test.",
    285          "Regexp \"^DeltaBlue\-stddev: (.+)$\" didn't match for test "
    286          "DeltaBlue."])
    287     self._VerifyMock(path.join("out", "x64.release", "d7"), "--flag", "run.js")
    288 
    289   def testBuildbot(self):
    290     self._WriteTestInput(V8_JSON)
    291     self._MockCommand(["."], ["Richards: 1.234\nDeltaBlue: 10657567\n"])
    292     self.assertEquals(0, self._CallMain("--buildbot"))
    293     self._VerifyResults("test", "score", [
    294       {"name": "Richards", "results": ["1.234"], "stddev": ""},
    295       {"name": "DeltaBlue", "results": ["10657567"], "stddev": ""},
    296     ])
    297     self._VerifyErrors([])
    298     self._VerifyMock(path.join("out", "Release", "d7"), "--flag", "run.js")
    299 
    300   def testBuildbotWithTotal(self):
    301     test_input = dict(V8_JSON)
    302     test_input["total"] = True
    303     self._WriteTestInput(test_input)
    304     self._MockCommand(["."], ["Richards: 1.234\nDeltaBlue: 10657567\n"])
    305     self.assertEquals(0, self._CallMain("--buildbot"))
    306     self._VerifyResults("test", "score", [
    307       {"name": "Richards", "results": ["1.234"], "stddev": ""},
    308       {"name": "DeltaBlue", "results": ["10657567"], "stddev": ""},
    309       {"name": "Total", "results": ["3626.49109719"], "stddev": ""},
    310     ])
    311     self._VerifyErrors([])
    312     self._VerifyMock(path.join("out", "Release", "d7"), "--flag", "run.js")
    313 
    314   def testBuildbotWithTotalAndErrors(self):
    315     test_input = dict(V8_JSON)
    316     test_input["total"] = True
    317     self._WriteTestInput(test_input)
    318     self._MockCommand(["."], ["x\nRichaards: 1.234\nDeltaBlue: 10657567\ny\n"])
    319     self.assertEquals(1, self._CallMain("--buildbot"))
    320     self._VerifyResults("test", "score", [
    321       {"name": "Richards", "results": [], "stddev": ""},
    322       {"name": "DeltaBlue", "results": ["10657567"], "stddev": ""},
    323     ])
    324     self._VerifyErrors(
    325         ["Regexp \"^Richards: (.+)$\" didn't match for test Richards.",
    326          "Not all traces have the same number of results."])
    327     self._VerifyMock(path.join("out", "Release", "d7"), "--flag", "run.js")
    328 
    329   def testRegexpNoMatch(self):
    330     self._WriteTestInput(V8_JSON)
    331     self._MockCommand(["."], ["x\nRichaards: 1.234\nDeltaBlue: 10657567\ny\n"])
    332     self.assertEquals(1, self._CallMain())
    333     self._VerifyResults("test", "score", [
    334       {"name": "Richards", "results": [], "stddev": ""},
    335       {"name": "DeltaBlue", "results": ["10657567"], "stddev": ""},
    336     ])
    337     self._VerifyErrors(
    338         ["Regexp \"^Richards: (.+)$\" didn't match for test Richards."])
    339     self._VerifyMock(path.join("out", "x64.release", "d7"), "--flag", "run.js")
    340 
    341   def testOneRunGeneric(self):
    342     test_input = dict(V8_GENERIC_JSON)
    343     self._WriteTestInput(test_input)
    344     self._MockCommand(["."], [
    345       "Trace(Test1), Result(1.234), StdDev(0.23)\n"
    346       "Trace(Test2), Result(10657567), StdDev(106)\n"])
    347     self.assertEquals(0, self._CallMain())
    348     self._VerifyResults("test", "ms", [
    349       {"name": "Test1", "results": ["1.234"], "stddev": "0.23"},
    350       {"name": "Test2", "results": ["10657567"], "stddev": "106"},
    351     ])
    352     self._VerifyErrors([])
    353     self._VerifyMock(path.join("out", "x64.release", "cc"), "--flag", "")
    354 
    355   def testOneRunTimingOut(self):
    356     test_input = dict(V8_JSON)
    357     test_input["timeout"] = 70
    358     self._WriteTestInput(test_input)
    359     self._MockCommand(["."], [""], timed_out=True)
    360     self.assertEquals(1, self._CallMain())
    361     self._VerifyResults("test", "score", [
    362       {"name": "Richards", "results": [], "stddev": ""},
    363       {"name": "DeltaBlue", "results": [], "stddev": ""},
    364     ])
    365     self._VerifyErrors([
    366       "Regexp \"^Richards: (.+)$\" didn't match for test Richards.",
    367       "Regexp \"^DeltaBlue: (.+)$\" didn't match for test DeltaBlue.",
    368     ])
    369     self._VerifyMock(
    370         path.join("out", "x64.release", "d7"), "--flag", "run.js", timeout=70)
    371