blob: be84a0033abc844375e1f7c5c469b36900739cf4 [file] [log] [blame]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright 2013 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Unit test for experiment_factory.py"""
import io
import os
import socket
import unittest
import unittest.mock as mock
import benchmark
from cros_utils import command_executer
from cros_utils.file_utils import FileUtils
import experiment_factory
from experiment_factory import ExperimentFactory
from experiment_file import ExperimentFile
import settings_factory
import test_flag
EXPERIMENT_FILE_1 = """
board: x86-alex
remote: chromeos-alex3
locks_dir: /tmp
benchmark: PageCycler {
iterations: 3
}
benchmark: webrtc {
iterations: 1
test_args: --story-filter=datachannel
}
image1 {
chromeos_image: /usr/local/google/cros_image1.bin
}
image2 {
chromeos_image: /usr/local/google/cros_image2.bin
}
"""
EXPERIMENT_FILE_2 = """
board: x86-alex
remote: chromeos-alex3
locks_dir: /tmp
cwp_dso: kallsyms
benchmark: Octane {
iterations: 1
suite: telemetry_Crosperf
run_local: False
weight: 0.8
}
benchmark: Kraken {
iterations: 1
suite: telemetry_Crosperf
run_local: False
weight: 0.2
}
image1 {
chromeos_image: /usr/local/google/cros_image1.bin
}
"""
# pylint: disable=too-many-function-args
class ExperimentFactoryTest(unittest.TestCase):
"""Class for running experiment factory unittests."""
def setUp(self):
self.append_benchmark_call_args = []
def testLoadExperimentFile1(self):
experiment_file = ExperimentFile(io.StringIO(EXPERIMENT_FILE_1))
exp = ExperimentFactory().GetExperiment(
experiment_file, working_directory="", log_dir=""
)
self.assertEqual(exp.remote, ["chromeos-alex3"])
self.assertEqual(len(exp.benchmarks), 2)
self.assertEqual(exp.benchmarks[0].name, "PageCycler")
self.assertEqual(exp.benchmarks[0].test_name, "PageCycler")
self.assertEqual(exp.benchmarks[0].iterations, 3)
self.assertEqual(exp.benchmarks[1].name, "webrtc@@datachannel")
self.assertEqual(exp.benchmarks[1].test_name, "webrtc")
self.assertEqual(exp.benchmarks[1].iterations, 1)
self.assertEqual(len(exp.labels), 2)
self.assertEqual(
exp.labels[0].chromeos_image, "/usr/local/google/cros_image1.bin"
)
self.assertEqual(exp.labels[0].board, "x86-alex")
def testLoadExperimentFile2CWP(self):
experiment_file = ExperimentFile(io.StringIO(EXPERIMENT_FILE_2))
exp = ExperimentFactory().GetExperiment(
experiment_file, working_directory="", log_dir=""
)
self.assertEqual(exp.cwp_dso, "kallsyms")
self.assertEqual(len(exp.benchmarks), 2)
self.assertEqual(exp.benchmarks[0].weight, 0.8)
self.assertEqual(exp.benchmarks[1].weight, 0.2)
def testDuplecateBenchmark(self):
mock_experiment_file = ExperimentFile(io.StringIO(EXPERIMENT_FILE_1))
mock_experiment_file.all_settings = []
benchmark_settings1 = settings_factory.BenchmarkSettings("name")
mock_experiment_file.all_settings.append(benchmark_settings1)
benchmark_settings2 = settings_factory.BenchmarkSettings("name")
mock_experiment_file.all_settings.append(benchmark_settings2)
with self.assertRaises(SyntaxError):
ef = ExperimentFactory()
ef.GetExperiment(mock_experiment_file, "", "")
def testCWPExceptions(self):
mock_experiment_file = ExperimentFile(io.StringIO(""))
mock_experiment_file.all_settings = []
global_settings = settings_factory.GlobalSettings("test_name")
global_settings.SetField("locks_dir", "/tmp")
# Test 1: DSO type not supported
global_settings.SetField("cwp_dso", "test")
self.assertEqual(global_settings.GetField("cwp_dso"), "test")
mock_experiment_file.global_settings = global_settings
with self.assertRaises(RuntimeError) as msg:
ef = ExperimentFactory()
ef.GetExperiment(mock_experiment_file, "", "")
self.assertEqual(
"The DSO specified is not supported", str(msg.exception)
)
# Test 2: No weight after DSO specified
global_settings.SetField("cwp_dso", "kallsyms")
mock_experiment_file.global_settings = global_settings
benchmark_settings = settings_factory.BenchmarkSettings("name")
mock_experiment_file.all_settings.append(benchmark_settings)
with self.assertRaises(RuntimeError) as msg:
ef = ExperimentFactory()
ef.GetExperiment(mock_experiment_file, "", "")
self.assertEqual(
"With DSO specified, each benchmark should have a weight",
str(msg.exception),
)
# Test 3: Weight is set, but no dso specified
global_settings.SetField("cwp_dso", "")
mock_experiment_file.global_settings = global_settings
benchmark_settings = settings_factory.BenchmarkSettings("name")
benchmark_settings.SetField("weight", "0.8")
mock_experiment_file.all_settings = []
mock_experiment_file.all_settings.append(benchmark_settings)
with self.assertRaises(RuntimeError) as msg:
ef = ExperimentFactory()
ef.GetExperiment(mock_experiment_file, "", "")
self.assertEqual(
"Weight can only be set when DSO specified", str(msg.exception)
)
# Test 4: cwp_dso only works for telemetry_Crosperf benchmarks
global_settings.SetField("cwp_dso", "kallsyms")
mock_experiment_file.global_settings = global_settings
benchmark_settings = settings_factory.BenchmarkSettings("name")
benchmark_settings.SetField("weight", "0.8")
mock_experiment_file.all_settings = []
mock_experiment_file.all_settings.append(benchmark_settings)
with self.assertRaises(RuntimeError) as msg:
ef = ExperimentFactory()
ef.GetExperiment(mock_experiment_file, "", "")
self.assertEqual(
"CWP approximation weight only works with "
"telemetry_Crosperf suite",
str(msg.exception),
)
# Test 5: cwp_dso does not work for local run
benchmark_settings = settings_factory.BenchmarkSettings("name")
benchmark_settings.SetField("weight", "0.8")
benchmark_settings.SetField("suite", "telemetry_Crosperf")
benchmark_settings.SetField("run_local", "True")
mock_experiment_file.all_settings = []
mock_experiment_file.all_settings.append(benchmark_settings)
with self.assertRaises(RuntimeError) as msg:
ef = ExperimentFactory()
ef.GetExperiment(mock_experiment_file, "", "")
self.assertEqual(
"run_local must be set to False to use CWP approximation",
str(msg.exception),
)
# Test 6: weight should be float >=0
benchmark_settings = settings_factory.BenchmarkSettings("name")
benchmark_settings.SetField("weight", "-1.2")
benchmark_settings.SetField("suite", "telemetry_Crosperf")
benchmark_settings.SetField("run_local", "False")
mock_experiment_file.all_settings = []
mock_experiment_file.all_settings.append(benchmark_settings)
with self.assertRaises(RuntimeError) as msg:
ef = ExperimentFactory()
ef.GetExperiment(mock_experiment_file, "", "")
self.assertEqual("Weight should be a float >=0", str(msg.exception))
# Test 7: more than one story tag in test_args
benchmark_settings = settings_factory.BenchmarkSettings("name")
benchmark_settings.SetField(
"test_args", "--story-filter=a --story-tag-filter=b"
)
benchmark_settings.SetField("weight", "1.2")
benchmark_settings.SetField("suite", "telemetry_Crosperf")
mock_experiment_file.all_settings = []
mock_experiment_file.all_settings.append(benchmark_settings)
with self.assertRaises(RuntimeError) as msg:
ef = ExperimentFactory()
ef.GetExperiment(mock_experiment_file, "", "")
self.assertEqual(
"Only one story or story-tag filter allowed in a single "
"benchmark run",
str(msg.exception),
)
# Test 8: Iterations of each benchmark run are not same in cwp mode
mock_experiment_file.all_settings = []
benchmark_settings = settings_factory.BenchmarkSettings("name1")
benchmark_settings.SetField("iterations", "4")
benchmark_settings.SetField("weight", "1.2")
benchmark_settings.SetField("suite", "telemetry_Crosperf")
benchmark_settings.SetField("run_local", "False")
mock_experiment_file.all_settings.append(benchmark_settings)
benchmark_settings = settings_factory.BenchmarkSettings("name2")
benchmark_settings.SetField("iterations", "3")
benchmark_settings.SetField("weight", "1.2")
benchmark_settings.SetField("suite", "telemetry_Crosperf")
benchmark_settings.SetField("run_local", "False")
mock_experiment_file.all_settings.append(benchmark_settings)
with self.assertRaises(RuntimeError) as msg:
ef = ExperimentFactory()
ef.GetExperiment(mock_experiment_file, "", "")
self.assertEqual(
"Iterations of each benchmark run are not the same",
str(msg.exception),
)
def test_append_benchmark_set(self):
ef = ExperimentFactory()
bench_list = []
ef.AppendBenchmarkSet(
bench_list,
experiment_factory.telemetry_perfv2_tests,
"",
1,
False,
"",
"telemetry_Crosperf",
False,
0,
False,
"",
0,
)
self.assertEqual(
len(bench_list), len(experiment_factory.telemetry_perfv2_tests)
)
self.assertTrue(isinstance(bench_list[0], benchmark.Benchmark))
bench_list = []
ef.AppendBenchmarkSet(
bench_list,
experiment_factory.telemetry_pagecycler_tests,
"",
1,
False,
"",
"telemetry_Crosperf",
False,
0,
False,
"",
0,
)
self.assertEqual(
len(bench_list), len(experiment_factory.telemetry_pagecycler_tests)
)
self.assertTrue(isinstance(bench_list[0], benchmark.Benchmark))
bench_list = []
ef.AppendBenchmarkSet(
bench_list,
experiment_factory.telemetry_toolchain_perf_tests,
"",
1,
False,
"",
"telemetry_Crosperf",
False,
0,
False,
"",
0,
)
self.assertEqual(
len(bench_list),
len(experiment_factory.telemetry_toolchain_perf_tests),
)
self.assertTrue(isinstance(bench_list[0], benchmark.Benchmark))
@mock.patch.object(socket, "gethostname")
def test_get_experiment(self, mock_socket):
test_flag.SetTestMode(False)
self.append_benchmark_call_args = []
def FakeAppendBenchmarkSet(
bench_list, set_list, args, iters, rm_ch, perf_args, suite, show_all
):
"Helper function for test_get_experiment"
arg_list = [
bench_list,
set_list,
args,
iters,
rm_ch,
perf_args,
suite,
show_all,
]
self.append_benchmark_call_args.append(arg_list)
def FakeGetDefaultRemotes(board):
if not board:
return []
return [
"fake_chromeos_machine1.cros",
"fake_chromeos_machine2.cros",
]
def FakeGetXbuddyPath(
build, autotest_dir, debug_dir, board, chroot, log_level, perf_args
):
autotest_path = autotest_dir
if not autotest_path:
autotest_path = "fake_autotest_path"
debug_path = debug_dir
if not debug_path and perf_args:
debug_path = "fake_debug_path"
if not build or not board or not chroot or not log_level:
return "", autotest_path, debug_path
return "fake_image_path", autotest_path, debug_path
ef = ExperimentFactory()
ef.AppendBenchmarkSet = FakeAppendBenchmarkSet
ef.GetDefaultRemotes = FakeGetDefaultRemotes
label_settings = settings_factory.LabelSettings("image_label")
benchmark_settings = settings_factory.BenchmarkSettings("bench_test")
global_settings = settings_factory.GlobalSettings("test_name")
label_settings.GetXbuddyPath = FakeGetXbuddyPath
mock_experiment_file = ExperimentFile(io.StringIO(""))
mock_experiment_file.all_settings = []
test_flag.SetTestMode(True)
# Basic test.
global_settings.SetField("name", "unittest_test")
global_settings.SetField("board", "lumpy")
global_settings.SetField("locks_dir", "/tmp")
global_settings.SetField("remote", "123.45.67.89 123.45.76.80")
benchmark_settings.SetField("test_name", "kraken")
benchmark_settings.SetField("suite", "telemetry_Crosperf")
benchmark_settings.SetField("iterations", 1)
label_settings.SetField(
"chromeos_image",
"chromeos/src/build/images/lumpy/latest/chromiumos_test_image.bin",
)
label_settings.SetField(
"chrome_src", "/usr/local/google/home/chrome-top"
)
label_settings.SetField("autotest_path", "/tmp/autotest")
mock_experiment_file.global_settings = global_settings
mock_experiment_file.all_settings.append(label_settings)
mock_experiment_file.all_settings.append(benchmark_settings)
mock_experiment_file.all_settings.append(global_settings)
mock_socket.return_value = ""
# First test. General test.
exp = ef.GetExperiment(mock_experiment_file, "", "")
self.assertCountEqual(exp.remote, ["123.45.67.89", "123.45.76.80"])
self.assertEqual(exp.cache_conditions, [0, 2, 1])
self.assertEqual(exp.log_level, "average")
self.assertEqual(len(exp.benchmarks), 1)
self.assertEqual(exp.benchmarks[0].name, "bench_test")
self.assertEqual(exp.benchmarks[0].test_name, "kraken")
self.assertEqual(exp.benchmarks[0].iterations, 1)
self.assertEqual(exp.benchmarks[0].suite, "telemetry_Crosperf")
self.assertFalse(exp.benchmarks[0].show_all_results)
self.assertEqual(len(exp.labels), 1)
self.assertEqual(
exp.labels[0].chromeos_image,
"chromeos/src/build/images/lumpy/latest/"
"chromiumos_test_image.bin",
)
self.assertEqual(exp.labels[0].autotest_path, "/tmp/autotest")
self.assertEqual(exp.labels[0].board, "lumpy")
# Second test: Remotes listed in labels.
test_flag.SetTestMode(True)
label_settings.SetField("remote", "chromeos1.cros chromeos2.cros")
exp = ef.GetExperiment(mock_experiment_file, "", "")
self.assertCountEqual(
exp.remote,
[
"123.45.67.89",
"123.45.76.80",
"chromeos1.cros",
"chromeos2.cros",
],
)
# Third test: Automatic fixing of bad logging_level param:
global_settings.SetField("logging_level", "really loud!")
exp = ef.GetExperiment(mock_experiment_file, "", "")
self.assertEqual(exp.log_level, "verbose")
# Fourth test: Setting cache conditions; only 1 remote with "same_machine"
global_settings.SetField("rerun_if_failed", "true")
global_settings.SetField("rerun", "true")
global_settings.SetField("same_machine", "true")
global_settings.SetField("same_specs", "true")
self.assertRaises(
Exception, ef.GetExperiment, mock_experiment_file, "", ""
)
label_settings.SetField("remote", "")
global_settings.SetField("remote", "123.45.67.89")
exp = ef.GetExperiment(mock_experiment_file, "", "")
self.assertEqual(exp.cache_conditions, [0, 2, 3, 4, 6, 1])
# Fifth Test: Adding a second label; calling GetXbuddyPath; omitting all
# remotes (Call GetDefaultRemotes).
mock_socket.return_value = "test.corp.google.com"
global_settings.SetField("remote", "")
global_settings.SetField("same_machine", "false")
label_settings_2 = settings_factory.LabelSettings(
"official_image_label"
)
label_settings_2.SetField("chromeos_root", "chromeos")
label_settings_2.SetField("build", "official-dev")
label_settings_2.SetField("autotest_path", "")
label_settings_2.GetXbuddyPath = FakeGetXbuddyPath
mock_experiment_file.all_settings.append(label_settings_2)
exp = ef.GetExperiment(mock_experiment_file, "", "")
self.assertEqual(len(exp.labels), 2)
self.assertEqual(exp.labels[1].chromeos_image, "fake_image_path")
self.assertEqual(exp.labels[1].autotest_path, "fake_autotest_path")
self.assertCountEqual(
exp.remote,
["fake_chromeos_machine1.cros", "fake_chromeos_machine2.cros"],
)
def test_get_default_remotes(self):
board_list = [
"bob",
"chell",
"coral",
"elm",
"kefka",
"nautilus",
"snappy",
]
ef = ExperimentFactory()
self.assertRaises(Exception, ef.GetDefaultRemotes, "bad-board")
# Verify that we have entries for every board
for b in board_list:
remotes = ef.GetDefaultRemotes(b)
self.assertGreaterEqual(len(remotes), 1)
@mock.patch.object(command_executer.CommandExecuter, "RunCommand")
@mock.patch.object(os.path, "exists")
def test_check_crosfleet_tool(self, mock_exists, mock_runcmd):
ef = ExperimentFactory()
chromeos_root = "/tmp/chromeos"
log_level = "average"
mock_exists.return_value = True
ret = ef.CheckCrosfleetTool(chromeos_root, log_level)
self.assertTrue(ret)
mock_exists.return_value = False
mock_runcmd.return_value = 1
with self.assertRaises(RuntimeError) as err:
ef.CheckCrosfleetTool(chromeos_root, log_level)
self.assertEqual(mock_runcmd.call_count, 1)
self.assertEqual(
str(err.exception),
"Crosfleet tool not installed "
"correctly, please try to manually install it from "
"/tmp/chromeos/chromeos-admin/lab-tools/setup_lab_tools",
)
mock_runcmd.return_value = 0
mock_runcmd.call_count = 0
ret = ef.CheckCrosfleetTool(chromeos_root, log_level)
self.assertEqual(mock_runcmd.call_count, 1)
self.assertFalse(ret)
if __name__ == "__main__":
FileUtils.Configure(True)
test_flag.SetTestMode(True)
unittest.main()