xref: /aosp_15_r20/external/toolchain-utils/crosperf/experiment_runner_unittest.py (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
1*760c253cSXin Li#!/usr/bin/env python3
2*760c253cSXin Li# -*- coding: utf-8 -*-
3*760c253cSXin Li#
4*760c253cSXin Li# Copyright 2014 The ChromiumOS Authors
5*760c253cSXin Li# Use of this source code is governed by a BSD-style license that can be
6*760c253cSXin Li# found in the LICENSE file.
7*760c253cSXin Li
8*760c253cSXin Li"""Tests for the experiment runner module."""
9*760c253cSXin Li
10*760c253cSXin Li
11*760c253cSXin Liimport getpass
12*760c253cSXin Liimport io
13*760c253cSXin Liimport os
14*760c253cSXin Liimport time
15*760c253cSXin Liimport unittest
16*760c253cSXin Liimport unittest.mock as mock
17*760c253cSXin Li
18*760c253cSXin Lifrom cros_utils import command_executer
19*760c253cSXin Lifrom cros_utils.email_sender import EmailSender
20*760c253cSXin Lifrom cros_utils.file_utils import FileUtils
21*760c253cSXin Lifrom experiment_factory import ExperimentFactory
22*760c253cSXin Lifrom experiment_file import ExperimentFile
23*760c253cSXin Liimport experiment_runner
24*760c253cSXin Liimport experiment_status
25*760c253cSXin Liimport machine_manager
26*760c253cSXin Lifrom results_cache import Result
27*760c253cSXin Lifrom results_report import HTMLResultsReport
28*760c253cSXin Lifrom results_report import TextResultsReport
29*760c253cSXin Liimport test_flag
30*760c253cSXin Li
31*760c253cSXin Liimport config
32*760c253cSXin Li
33*760c253cSXin Li
34*760c253cSXin LiEXPERIMENT_FILE_1 = """
35*760c253cSXin Li  board: parrot
36*760c253cSXin Li  remote: chromeos-parrot1.cros chromreos-parrot2.cros
37*760c253cSXin Li  locks_dir: /tmp
38*760c253cSXin Li
39*760c253cSXin Li  benchmark: kraken {
40*760c253cSXin Li    suite: telemetry_Crosperf
41*760c253cSXin Li    iterations: 3
42*760c253cSXin Li  }
43*760c253cSXin Li
44*760c253cSXin Li  image1 {
45*760c253cSXin Li    chromeos_root: /usr/local/google/chromeos
46*760c253cSXin Li    chromeos_image: /usr/local/google/chromeos/src/build/images/parrot/latest/cros_image1.bin
47*760c253cSXin Li  }
48*760c253cSXin Li
49*760c253cSXin Li  image2 {
50*760c253cSXin Li    chromeos_image: /usr/local/google/chromeos/src/build/imaages/parrot/latest/cros_image2.bin
51*760c253cSXin Li  }
52*760c253cSXin Li  """
53*760c253cSXin Li
54*760c253cSXin Li# pylint: disable=protected-access
55*760c253cSXin Li
56*760c253cSXin Li
57*760c253cSXin Liclass FakeLogger(object):
58*760c253cSXin Li    """Fake logger for tests."""
59*760c253cSXin Li
60*760c253cSXin Li    def __init__(self):
61*760c253cSXin Li        self.LogOutputCount = 0
62*760c253cSXin Li        self.LogErrorCount = 0
63*760c253cSXin Li        self.output_msgs = []
64*760c253cSXin Li        self.error_msgs = []
65*760c253cSXin Li        self.dot_count = 0
66*760c253cSXin Li        self.LogStartDotsCount = 0
67*760c253cSXin Li        self.LogEndDotsCount = 0
68*760c253cSXin Li        self.LogAppendDotCount = 0
69*760c253cSXin Li
70*760c253cSXin Li    def LogOutput(self, msg):
71*760c253cSXin Li        self.LogOutputCount += 1
72*760c253cSXin Li        self.output_msgs.append(msg)
73*760c253cSXin Li
74*760c253cSXin Li    def LogError(self, msg):
75*760c253cSXin Li        self.LogErrorCount += 1
76*760c253cSXin Li        self.error_msgs.append(msg)
77*760c253cSXin Li
78*760c253cSXin Li    def LogStartDots(self):
79*760c253cSXin Li        self.LogStartDotsCount += 1
80*760c253cSXin Li        self.dot_count += 1
81*760c253cSXin Li
82*760c253cSXin Li    def LogAppendDot(self):
83*760c253cSXin Li        self.LogAppendDotCount += 1
84*760c253cSXin Li        self.dot_count += 1
85*760c253cSXin Li
86*760c253cSXin Li    def LogEndDots(self):
87*760c253cSXin Li        self.LogEndDotsCount += 1
88*760c253cSXin Li
89*760c253cSXin Li    def Reset(self):
90*760c253cSXin Li        self.LogOutputCount = 0
91*760c253cSXin Li        self.LogErrorCount = 0
92*760c253cSXin Li        self.output_msgs = []
93*760c253cSXin Li        self.error_msgs = []
94*760c253cSXin Li        self.dot_count = 0
95*760c253cSXin Li        self.LogStartDotsCount = 0
96*760c253cSXin Li        self.LogEndDotsCount = 0
97*760c253cSXin Li        self.LogAppendDotCount = 0
98*760c253cSXin Li
99*760c253cSXin Li
100*760c253cSXin Liclass ExperimentRunnerTest(unittest.TestCase):
101*760c253cSXin Li    """Test for experiment runner class."""
102*760c253cSXin Li
103*760c253cSXin Li    run_count = 0
104*760c253cSXin Li    is_complete_count = 0
105*760c253cSXin Li    mock_logger = FakeLogger()
106*760c253cSXin Li    mock_cmd_exec = mock.Mock(spec=command_executer.CommandExecuter)
107*760c253cSXin Li
108*760c253cSXin Li    def make_fake_experiment(self):
109*760c253cSXin Li        test_flag.SetTestMode(True)
110*760c253cSXin Li        experiment_file = ExperimentFile(io.StringIO(EXPERIMENT_FILE_1))
111*760c253cSXin Li        experiment = ExperimentFactory().GetExperiment(
112*760c253cSXin Li            experiment_file, working_directory="", log_dir=""
113*760c253cSXin Li        )
114*760c253cSXin Li        return experiment
115*760c253cSXin Li
116*760c253cSXin Li    @mock.patch.object(machine_manager.MachineManager, "AddMachine")
117*760c253cSXin Li    @mock.patch.object(os.path, "isfile")
118*760c253cSXin Li
119*760c253cSXin Li    # pylint: disable=arguments-differ
120*760c253cSXin Li    def setUp(self, mock_isfile, _mock_addmachine):
121*760c253cSXin Li        mock_isfile.return_value = True
122*760c253cSXin Li        self.exp = self.make_fake_experiment()
123*760c253cSXin Li
124*760c253cSXin Li    def test_init(self):
125*760c253cSXin Li        er = experiment_runner.ExperimentRunner(
126*760c253cSXin Li            self.exp,
127*760c253cSXin Li            json_report=False,
128*760c253cSXin Li            using_schedv2=False,
129*760c253cSXin Li            log=self.mock_logger,
130*760c253cSXin Li            cmd_exec=self.mock_cmd_exec,
131*760c253cSXin Li        )
132*760c253cSXin Li        self.assertFalse(er._terminated)
133*760c253cSXin Li        self.assertEqual(er.STATUS_TIME_DELAY, 10)
134*760c253cSXin Li
135*760c253cSXin Li        self.exp.log_level = "verbose"
136*760c253cSXin Li        er = experiment_runner.ExperimentRunner(
137*760c253cSXin Li            self.exp,
138*760c253cSXin Li            json_report=False,
139*760c253cSXin Li            using_schedv2=False,
140*760c253cSXin Li            log=self.mock_logger,
141*760c253cSXin Li            cmd_exec=self.mock_cmd_exec,
142*760c253cSXin Li        )
143*760c253cSXin Li        self.assertEqual(er.STATUS_TIME_DELAY, 30)
144*760c253cSXin Li
145*760c253cSXin Li    @mock.patch.object(time, "time")
146*760c253cSXin Li    @mock.patch.object(time, "sleep")
147*760c253cSXin Li    @mock.patch.object(experiment_status.ExperimentStatus, "GetStatusString")
148*760c253cSXin Li    @mock.patch.object(experiment_status.ExperimentStatus, "GetProgressString")
149*760c253cSXin Li    def test_run(
150*760c253cSXin Li        self, mock_progress_string, mock_status_string, mock_sleep, mock_time
151*760c253cSXin Li    ):
152*760c253cSXin Li
153*760c253cSXin Li        self.run_count = 0
154*760c253cSXin Li        self.is_complete_count = 0
155*760c253cSXin Li        mock_sleep.return_value = None
156*760c253cSXin Li        # pylint: disable=range-builtin-not-iterating
157*760c253cSXin Li        mock_time.side_effect = range(1, 50, 1)
158*760c253cSXin Li
159*760c253cSXin Li        def reset():
160*760c253cSXin Li            self.run_count = 0
161*760c253cSXin Li            self.is_complete_count = 0
162*760c253cSXin Li
163*760c253cSXin Li        def FakeRun():
164*760c253cSXin Li            self.run_count += 1
165*760c253cSXin Li            return 0
166*760c253cSXin Li
167*760c253cSXin Li        def FakeIsComplete():
168*760c253cSXin Li            self.is_complete_count += 1
169*760c253cSXin Li            if self.is_complete_count < 6:
170*760c253cSXin Li                return False
171*760c253cSXin Li            else:
172*760c253cSXin Li                return True
173*760c253cSXin Li
174*760c253cSXin Li        self.mock_logger.Reset()
175*760c253cSXin Li        self.exp.Run = FakeRun
176*760c253cSXin Li        self.exp.IsComplete = FakeIsComplete
177*760c253cSXin Li
178*760c253cSXin Li        # Test 1: log_level == "quiet"
179*760c253cSXin Li        self.exp.log_level = "quiet"
180*760c253cSXin Li        er = experiment_runner.ExperimentRunner(
181*760c253cSXin Li            self.exp,
182*760c253cSXin Li            json_report=False,
183*760c253cSXin Li            using_schedv2=False,
184*760c253cSXin Li            log=self.mock_logger,
185*760c253cSXin Li            cmd_exec=self.mock_cmd_exec,
186*760c253cSXin Li        )
187*760c253cSXin Li        er.STATUS_TIME_DELAY = 2
188*760c253cSXin Li        mock_status_string.return_value = "Fake status string"
189*760c253cSXin Li        er._Run(self.exp)
190*760c253cSXin Li        self.assertEqual(self.run_count, 1)
191*760c253cSXin Li        self.assertTrue(self.is_complete_count > 0)
192*760c253cSXin Li        self.assertEqual(self.mock_logger.LogStartDotsCount, 1)
193*760c253cSXin Li        self.assertEqual(self.mock_logger.LogAppendDotCount, 1)
194*760c253cSXin Li        self.assertEqual(self.mock_logger.LogEndDotsCount, 1)
195*760c253cSXin Li        self.assertEqual(self.mock_logger.dot_count, 2)
196*760c253cSXin Li        self.assertEqual(mock_progress_string.call_count, 0)
197*760c253cSXin Li        self.assertEqual(mock_status_string.call_count, 2)
198*760c253cSXin Li        self.assertEqual(
199*760c253cSXin Li            self.mock_logger.output_msgs,
200*760c253cSXin Li            [
201*760c253cSXin Li                "==============================",
202*760c253cSXin Li                "Fake status string",
203*760c253cSXin Li                "==============================",
204*760c253cSXin Li            ],
205*760c253cSXin Li        )
206*760c253cSXin Li        self.assertEqual(len(self.mock_logger.error_msgs), 0)
207*760c253cSXin Li
208*760c253cSXin Li        # Test 2: log_level == "average"
209*760c253cSXin Li        self.mock_logger.Reset()
210*760c253cSXin Li        reset()
211*760c253cSXin Li        self.exp.log_level = "average"
212*760c253cSXin Li        mock_status_string.call_count = 0
213*760c253cSXin Li        er = experiment_runner.ExperimentRunner(
214*760c253cSXin Li            self.exp,
215*760c253cSXin Li            json_report=False,
216*760c253cSXin Li            using_schedv2=False,
217*760c253cSXin Li            log=self.mock_logger,
218*760c253cSXin Li            cmd_exec=self.mock_cmd_exec,
219*760c253cSXin Li        )
220*760c253cSXin Li        er.STATUS_TIME_DELAY = 2
221*760c253cSXin Li        mock_status_string.return_value = "Fake status string"
222*760c253cSXin Li        er._Run(self.exp)
223*760c253cSXin Li        self.assertEqual(self.run_count, 1)
224*760c253cSXin Li        self.assertTrue(self.is_complete_count > 0)
225*760c253cSXin Li        self.assertEqual(self.mock_logger.LogStartDotsCount, 1)
226*760c253cSXin Li        self.assertEqual(self.mock_logger.LogAppendDotCount, 1)
227*760c253cSXin Li        self.assertEqual(self.mock_logger.LogEndDotsCount, 1)
228*760c253cSXin Li        self.assertEqual(self.mock_logger.dot_count, 2)
229*760c253cSXin Li        self.assertEqual(mock_progress_string.call_count, 0)
230*760c253cSXin Li        self.assertEqual(mock_status_string.call_count, 2)
231*760c253cSXin Li        self.assertEqual(
232*760c253cSXin Li            self.mock_logger.output_msgs,
233*760c253cSXin Li            [
234*760c253cSXin Li                "==============================",
235*760c253cSXin Li                "Fake status string",
236*760c253cSXin Li                "==============================",
237*760c253cSXin Li            ],
238*760c253cSXin Li        )
239*760c253cSXin Li        self.assertEqual(len(self.mock_logger.error_msgs), 0)
240*760c253cSXin Li
241*760c253cSXin Li        # Test 3: log_level == "verbose"
242*760c253cSXin Li        self.mock_logger.Reset()
243*760c253cSXin Li        reset()
244*760c253cSXin Li        self.exp.log_level = "verbose"
245*760c253cSXin Li        mock_status_string.call_count = 0
246*760c253cSXin Li        er = experiment_runner.ExperimentRunner(
247*760c253cSXin Li            self.exp,
248*760c253cSXin Li            json_report=False,
249*760c253cSXin Li            using_schedv2=False,
250*760c253cSXin Li            log=self.mock_logger,
251*760c253cSXin Li            cmd_exec=self.mock_cmd_exec,
252*760c253cSXin Li        )
253*760c253cSXin Li        er.STATUS_TIME_DELAY = 2
254*760c253cSXin Li        mock_status_string.return_value = "Fake status string"
255*760c253cSXin Li        mock_progress_string.return_value = "Fake progress string"
256*760c253cSXin Li        er._Run(self.exp)
257*760c253cSXin Li        self.assertEqual(self.run_count, 1)
258*760c253cSXin Li        self.assertTrue(self.is_complete_count > 0)
259*760c253cSXin Li        self.assertEqual(self.mock_logger.LogStartDotsCount, 0)
260*760c253cSXin Li        self.assertEqual(self.mock_logger.LogAppendDotCount, 0)
261*760c253cSXin Li        self.assertEqual(self.mock_logger.LogEndDotsCount, 0)
262*760c253cSXin Li        self.assertEqual(self.mock_logger.dot_count, 0)
263*760c253cSXin Li        self.assertEqual(mock_progress_string.call_count, 2)
264*760c253cSXin Li        self.assertEqual(mock_status_string.call_count, 2)
265*760c253cSXin Li        self.assertEqual(
266*760c253cSXin Li            self.mock_logger.output_msgs,
267*760c253cSXin Li            [
268*760c253cSXin Li                "==============================",
269*760c253cSXin Li                "Fake progress string",
270*760c253cSXin Li                "Fake status string",
271*760c253cSXin Li                "==============================",
272*760c253cSXin Li                "==============================",
273*760c253cSXin Li                "Fake progress string",
274*760c253cSXin Li                "Fake status string",
275*760c253cSXin Li                "==============================",
276*760c253cSXin Li            ],
277*760c253cSXin Li        )
278*760c253cSXin Li        self.assertEqual(len(self.mock_logger.error_msgs), 0)
279*760c253cSXin Li
280*760c253cSXin Li    @mock.patch.object(TextResultsReport, "GetReport")
281*760c253cSXin Li    def test_print_table(self, mock_report):
282*760c253cSXin Li        self.mock_logger.Reset()
283*760c253cSXin Li        mock_report.return_value = "This is a fake experiment report."
284*760c253cSXin Li        er = experiment_runner.ExperimentRunner(
285*760c253cSXin Li            self.exp,
286*760c253cSXin Li            json_report=False,
287*760c253cSXin Li            using_schedv2=False,
288*760c253cSXin Li            log=self.mock_logger,
289*760c253cSXin Li            cmd_exec=self.mock_cmd_exec,
290*760c253cSXin Li        )
291*760c253cSXin Li        er._PrintTable(self.exp)
292*760c253cSXin Li        self.assertEqual(mock_report.call_count, 1)
293*760c253cSXin Li        self.assertEqual(
294*760c253cSXin Li            self.mock_logger.output_msgs, ["This is a fake experiment report."]
295*760c253cSXin Li        )
296*760c253cSXin Li
297*760c253cSXin Li    @mock.patch.object(HTMLResultsReport, "GetReport")
298*760c253cSXin Li    @mock.patch.object(TextResultsReport, "GetReport")
299*760c253cSXin Li    @mock.patch.object(EmailSender, "Attachment")
300*760c253cSXin Li    @mock.patch.object(EmailSender, "SendEmail")
301*760c253cSXin Li    @mock.patch.object(getpass, "getuser")
302*760c253cSXin Li    def test_email(
303*760c253cSXin Li        self,
304*760c253cSXin Li        mock_getuser,
305*760c253cSXin Li        mock_emailer,
306*760c253cSXin Li        mock_attachment,
307*760c253cSXin Li        mock_text_report,
308*760c253cSXin Li        mock_html_report,
309*760c253cSXin Li    ):
310*760c253cSXin Li
311*760c253cSXin Li        mock_getuser.return_value = "[email protected]"
312*760c253cSXin Li        mock_text_report.return_value = "This is a fake text report."
313*760c253cSXin Li        mock_html_report.return_value = "This is a fake html report."
314*760c253cSXin Li
315*760c253cSXin Li        self.mock_logger.Reset()
316*760c253cSXin Li        config.AddConfig("no_email", True)
317*760c253cSXin Li        self.exp.email_to = ["[email protected]"]
318*760c253cSXin Li        er = experiment_runner.ExperimentRunner(
319*760c253cSXin Li            self.exp,
320*760c253cSXin Li            json_report=False,
321*760c253cSXin Li            using_schedv2=False,
322*760c253cSXin Li            log=self.mock_logger,
323*760c253cSXin Li            cmd_exec=self.mock_cmd_exec,
324*760c253cSXin Li        )
325*760c253cSXin Li        # Test 1. Config:no_email; exp.email_to set ==> no email sent
326*760c253cSXin Li        er._Email(self.exp)
327*760c253cSXin Li        self.assertEqual(mock_getuser.call_count, 0)
328*760c253cSXin Li        self.assertEqual(mock_emailer.call_count, 0)
329*760c253cSXin Li        self.assertEqual(mock_attachment.call_count, 0)
330*760c253cSXin Li        self.assertEqual(mock_text_report.call_count, 0)
331*760c253cSXin Li        self.assertEqual(mock_html_report.call_count, 0)
332*760c253cSXin Li
333*760c253cSXin Li        # Test 2. Config: email. exp.email_to set; cache hit.  => send email
334*760c253cSXin Li        self.mock_logger.Reset()
335*760c253cSXin Li        config.AddConfig("no_email", False)
336*760c253cSXin Li        for r in self.exp.benchmark_runs:
337*760c253cSXin Li            r.cache_hit = True
338*760c253cSXin Li        er._Email(self.exp)
339*760c253cSXin Li        self.assertEqual(mock_getuser.call_count, 1)
340*760c253cSXin Li        self.assertEqual(mock_emailer.call_count, 1)
341*760c253cSXin Li        self.assertEqual(mock_attachment.call_count, 1)
342*760c253cSXin Li        self.assertEqual(mock_text_report.call_count, 1)
343*760c253cSXin Li        self.assertEqual(mock_html_report.call_count, 1)
344*760c253cSXin Li        self.assertEqual(len(mock_emailer.call_args), 2)
345*760c253cSXin Li        self.assertEqual(
346*760c253cSXin Li            mock_emailer.call_args[0],
347*760c253cSXin Li            (
348*760c253cSXin Li                ["[email protected]", "[email protected]"],
349*760c253cSXin Li                ": image1 vs. image2",
350*760c253cSXin Li                "<pre style='font-size: 13px'>This is a fake text "
351*760c253cSXin Li                "report.\nResults are stored in _results.\n</pre>",
352*760c253cSXin Li            ),
353*760c253cSXin Li        )
354*760c253cSXin Li        self.assertTrue(isinstance(mock_emailer.call_args[1], dict))
355*760c253cSXin Li        self.assertEqual(len(mock_emailer.call_args[1]), 2)
356*760c253cSXin Li        self.assertTrue("attachments" in mock_emailer.call_args[1].keys())
357*760c253cSXin Li        self.assertEqual(mock_emailer.call_args[1]["msg_type"], "html")
358*760c253cSXin Li
359*760c253cSXin Li        mock_attachment.assert_called_with(
360*760c253cSXin Li            "report.html", "This is a fake html report."
361*760c253cSXin Li        )
362*760c253cSXin Li
363*760c253cSXin Li        # Test 3. Config: email; exp.mail_to set; no cache hit.  => send email
364*760c253cSXin Li        self.mock_logger.Reset()
365*760c253cSXin Li        mock_getuser.reset_mock()
366*760c253cSXin Li        mock_emailer.reset_mock()
367*760c253cSXin Li        mock_attachment.reset_mock()
368*760c253cSXin Li        mock_text_report.reset_mock()
369*760c253cSXin Li        mock_html_report.reset_mock()
370*760c253cSXin Li        config.AddConfig("no_email", False)
371*760c253cSXin Li        for r in self.exp.benchmark_runs:
372*760c253cSXin Li            r.cache_hit = False
373*760c253cSXin Li        er._Email(self.exp)
374*760c253cSXin Li        self.assertEqual(mock_getuser.call_count, 1)
375*760c253cSXin Li        self.assertEqual(mock_emailer.call_count, 1)
376*760c253cSXin Li        self.assertEqual(mock_attachment.call_count, 1)
377*760c253cSXin Li        self.assertEqual(mock_text_report.call_count, 1)
378*760c253cSXin Li        self.assertEqual(mock_html_report.call_count, 1)
379*760c253cSXin Li        self.assertEqual(len(mock_emailer.call_args), 2)
380*760c253cSXin Li        self.assertEqual(
381*760c253cSXin Li            mock_emailer.call_args[0],
382*760c253cSXin Li            (
383*760c253cSXin Li                [
384*760c253cSXin Li                    "[email protected]",
385*760c253cSXin Li                    "[email protected]",
386*760c253cSXin Li                    "[email protected]",
387*760c253cSXin Li                ],
388*760c253cSXin Li                ": image1 vs. image2",
389*760c253cSXin Li                "<pre style='font-size: 13px'>This is a fake text "
390*760c253cSXin Li                "report.\nResults are stored in _results.\n</pre>",
391*760c253cSXin Li            ),
392*760c253cSXin Li        )
393*760c253cSXin Li        self.assertTrue(isinstance(mock_emailer.call_args[1], dict))
394*760c253cSXin Li        self.assertEqual(len(mock_emailer.call_args[1]), 2)
395*760c253cSXin Li        self.assertTrue("attachments" in mock_emailer.call_args[1].keys())
396*760c253cSXin Li        self.assertEqual(mock_emailer.call_args[1]["msg_type"], "html")
397*760c253cSXin Li
398*760c253cSXin Li        mock_attachment.assert_called_with(
399*760c253cSXin Li            "report.html", "This is a fake html report."
400*760c253cSXin Li        )
401*760c253cSXin Li
402*760c253cSXin Li        # Test 4. Config: email; exp.mail_to = None; no cache hit. => send email
403*760c253cSXin Li        self.mock_logger.Reset()
404*760c253cSXin Li        mock_getuser.reset_mock()
405*760c253cSXin Li        mock_emailer.reset_mock()
406*760c253cSXin Li        mock_attachment.reset_mock()
407*760c253cSXin Li        mock_text_report.reset_mock()
408*760c253cSXin Li        mock_html_report.reset_mock()
409*760c253cSXin Li        self.exp.email_to = []
410*760c253cSXin Li        er._Email(self.exp)
411*760c253cSXin Li        self.assertEqual(mock_getuser.call_count, 1)
412*760c253cSXin Li        self.assertEqual(mock_emailer.call_count, 1)
413*760c253cSXin Li        self.assertEqual(mock_attachment.call_count, 1)
414*760c253cSXin Li        self.assertEqual(mock_text_report.call_count, 1)
415*760c253cSXin Li        self.assertEqual(mock_html_report.call_count, 1)
416*760c253cSXin Li        self.assertEqual(len(mock_emailer.call_args), 2)
417*760c253cSXin Li        self.assertEqual(
418*760c253cSXin Li            mock_emailer.call_args[0],
419*760c253cSXin Li            (
420*760c253cSXin Li                ["[email protected]"],
421*760c253cSXin Li                ": image1 vs. image2",
422*760c253cSXin Li                "<pre style='font-size: 13px'>This is a fake text "
423*760c253cSXin Li                "report.\nResults are stored in _results.\n</pre>",
424*760c253cSXin Li            ),
425*760c253cSXin Li        )
426*760c253cSXin Li        self.assertTrue(isinstance(mock_emailer.call_args[1], dict))
427*760c253cSXin Li        self.assertEqual(len(mock_emailer.call_args[1]), 2)
428*760c253cSXin Li        self.assertTrue("attachments" in mock_emailer.call_args[1].keys())
429*760c253cSXin Li        self.assertEqual(mock_emailer.call_args[1]["msg_type"], "html")
430*760c253cSXin Li
431*760c253cSXin Li        mock_attachment.assert_called_with(
432*760c253cSXin Li            "report.html", "This is a fake html report."
433*760c253cSXin Li        )
434*760c253cSXin Li
435*760c253cSXin Li        # Test 5. Config: email; exp.mail_to = None; cache hit => no email sent
436*760c253cSXin Li        self.mock_logger.Reset()
437*760c253cSXin Li        mock_getuser.reset_mock()
438*760c253cSXin Li        mock_emailer.reset_mock()
439*760c253cSXin Li        mock_attachment.reset_mock()
440*760c253cSXin Li        mock_text_report.reset_mock()
441*760c253cSXin Li        mock_html_report.reset_mock()
442*760c253cSXin Li        for r in self.exp.benchmark_runs:
443*760c253cSXin Li            r.cache_hit = True
444*760c253cSXin Li        er._Email(self.exp)
445*760c253cSXin Li        self.assertEqual(mock_getuser.call_count, 0)
446*760c253cSXin Li        self.assertEqual(mock_emailer.call_count, 0)
447*760c253cSXin Li        self.assertEqual(mock_attachment.call_count, 0)
448*760c253cSXin Li        self.assertEqual(mock_text_report.call_count, 0)
449*760c253cSXin Li        self.assertEqual(mock_html_report.call_count, 0)
450*760c253cSXin Li
451*760c253cSXin Li    @mock.patch.object(FileUtils, "RmDir")
452*760c253cSXin Li    @mock.patch.object(FileUtils, "MkDirP")
453*760c253cSXin Li    @mock.patch.object(FileUtils, "WriteFile")
454*760c253cSXin Li    @mock.patch.object(HTMLResultsReport, "FromExperiment")
455*760c253cSXin Li    @mock.patch.object(TextResultsReport, "FromExperiment")
456*760c253cSXin Li    @mock.patch.object(Result, "CompressResultsTo")
457*760c253cSXin Li    @mock.patch.object(Result, "CopyResultsTo")
458*760c253cSXin Li    @mock.patch.object(Result, "CleanUp")
459*760c253cSXin Li    @mock.patch.object(Result, "FormatStringTopCommands")
460*760c253cSXin Li    @mock.patch("builtins.open", new_callable=mock.mock_open)
461*760c253cSXin Li    def test_store_results(
462*760c253cSXin Li        self,
463*760c253cSXin Li        mock_open,
464*760c253cSXin Li        mock_top_commands,
465*760c253cSXin Li        mock_cleanup,
466*760c253cSXin Li        mock_copy,
467*760c253cSXin Li        mock_compress,
468*760c253cSXin Li        _mock_text_report,
469*760c253cSXin Li        mock_report,
470*760c253cSXin Li        mock_writefile,
471*760c253cSXin Li        mock_mkdir,
472*760c253cSXin Li        mock_rmdir,
473*760c253cSXin Li    ):
474*760c253cSXin Li
475*760c253cSXin Li        self.mock_logger.Reset()
476*760c253cSXin Li        self.exp.results_directory = "/usr/local/crosperf-results"
477*760c253cSXin Li        bench_run = self.exp.benchmark_runs[5]
478*760c253cSXin Li        bench_path = "/usr/local/crosperf-results/" + "".join(
479*760c253cSXin Li            ch for ch in bench_run.name if ch.isalnum()
480*760c253cSXin Li        )
481*760c253cSXin Li        self.assertEqual(len(self.exp.benchmark_runs), 6)
482*760c253cSXin Li
483*760c253cSXin Li        er = experiment_runner.ExperimentRunner(
484*760c253cSXin Li            self.exp,
485*760c253cSXin Li            json_report=False,
486*760c253cSXin Li            using_schedv2=False,
487*760c253cSXin Li            log=self.mock_logger,
488*760c253cSXin Li            cmd_exec=self.mock_cmd_exec,
489*760c253cSXin Li        )
490*760c253cSXin Li
491*760c253cSXin Li        # Test 1. Make sure nothing is done if _terminated is true.
492*760c253cSXin Li        er._terminated = True
493*760c253cSXin Li        er._StoreResults(self.exp)
494*760c253cSXin Li        self.assertEqual(mock_cleanup.call_count, 0)
495*760c253cSXin Li        self.assertEqual(mock_copy.call_count, 0)
496*760c253cSXin Li        self.assertEqual(mock_compress.call_count, 0)
497*760c253cSXin Li        self.assertEqual(mock_report.call_count, 0)
498*760c253cSXin Li        self.assertEqual(mock_writefile.call_count, 0)
499*760c253cSXin Li        self.assertEqual(mock_mkdir.call_count, 0)
500*760c253cSXin Li        self.assertEqual(mock_rmdir.call_count, 0)
501*760c253cSXin Li        self.assertEqual(self.mock_logger.LogOutputCount, 0)
502*760c253cSXin Li        self.assertEqual(mock_open.call_count, 0)
503*760c253cSXin Li        self.assertEqual(mock_top_commands.call_count, 0)
504*760c253cSXin Li
505*760c253cSXin Li        # Test 2. _terminated is false; everything works properly.
506*760c253cSXin Li        fake_result = Result(
507*760c253cSXin Li            self.mock_logger, self.exp.labels[0], "average", "daisy1"
508*760c253cSXin Li        )
509*760c253cSXin Li        for r in self.exp.benchmark_runs:
510*760c253cSXin Li            r.result = fake_result
511*760c253cSXin Li        er._terminated = False
512*760c253cSXin Li        self.exp.compress_results = False
513*760c253cSXin Li        er._StoreResults(self.exp)
514*760c253cSXin Li        self.assertEqual(mock_cleanup.call_count, 6)
515*760c253cSXin Li        mock_cleanup.assert_called_with(bench_run.benchmark.rm_chroot_tmp)
516*760c253cSXin Li        self.assertEqual(mock_copy.call_count, 6)
517*760c253cSXin Li        mock_copy.assert_called_with(bench_path)
518*760c253cSXin Li        self.assertEqual(mock_writefile.call_count, 3)
519*760c253cSXin Li        self.assertEqual(len(mock_writefile.call_args_list), 3)
520*760c253cSXin Li        first_args = mock_writefile.call_args_list[0]
521*760c253cSXin Li        second_args = mock_writefile.call_args_list[1]
522*760c253cSXin Li        self.assertEqual(
523*760c253cSXin Li            first_args[0][0], "/usr/local/crosperf-results/experiment.exp"
524*760c253cSXin Li        )
525*760c253cSXin Li        self.assertEqual(
526*760c253cSXin Li            second_args[0][0], "/usr/local/crosperf-results/results.html"
527*760c253cSXin Li        )
528*760c253cSXin Li        self.assertEqual(mock_mkdir.call_count, 1)
529*760c253cSXin Li        mock_mkdir.assert_called_with("/usr/local/crosperf-results")
530*760c253cSXin Li        self.assertEqual(mock_rmdir.call_count, 1)
531*760c253cSXin Li        mock_rmdir.assert_called_with("/usr/local/crosperf-results")
532*760c253cSXin Li        self.assertEqual(self.mock_logger.LogOutputCount, 5)
533*760c253cSXin Li        self.assertEqual(
534*760c253cSXin Li            self.mock_logger.output_msgs,
535*760c253cSXin Li            [
536*760c253cSXin Li                "Storing experiment file in /usr/local/crosperf-results.",
537*760c253cSXin Li                "Storing top statistics of each benchmark run into"
538*760c253cSXin Li                " /usr/local/crosperf-results/topstats.log.",
539*760c253cSXin Li                "Storing results of each benchmark run.",
540*760c253cSXin Li                "Storing results report in /usr/local/crosperf-results.",
541*760c253cSXin Li                "Storing email message body in /usr/local/crosperf-results.",
542*760c253cSXin Li            ],
543*760c253cSXin Li        )
544*760c253cSXin Li        self.assertEqual(mock_open.call_count, 1)
545*760c253cSXin Li        # Check write to a topstats.log file.
546*760c253cSXin Li        mock_open.assert_called_with(
547*760c253cSXin Li            "/usr/local/crosperf-results/topstats.log", "w"
548*760c253cSXin Li        )
549*760c253cSXin Li        mock_open().write.assert_called()
550*760c253cSXin Li
551*760c253cSXin Li        # Check top calls with no arguments.
552*760c253cSXin Li        topcalls = [mock.call()] * 6
553*760c253cSXin Li        self.assertEqual(mock_top_commands.call_args_list, topcalls)
554*760c253cSXin Li
555*760c253cSXin Li        # Test 3. Test compress_results.
556*760c253cSXin Li        self.exp.compress_results = True
557*760c253cSXin Li        mock_copy.call_count = 0
558*760c253cSXin Li        mock_compress.call_count = 0
559*760c253cSXin Li        er._StoreResults(self.exp)
560*760c253cSXin Li        self.assertEqual(mock_copy.call_count, 0)
561*760c253cSXin Li        mock_copy.assert_called_with(bench_path)
562*760c253cSXin Li        self.assertEqual(mock_compress.call_count, 6)
563*760c253cSXin Li        mock_compress.assert_called_with(bench_path)
564*760c253cSXin Li
565*760c253cSXin Li
566*760c253cSXin Liif __name__ == "__main__":
567*760c253cSXin Li    unittest.main()
568