xref: /aosp_15_r20/external/toolchain-utils/crosperf/machine_manager_unittest.py (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
4# Copyright 2012 The ChromiumOS Authors
5# Use of this source code is governed by a BSD-style license that can be
6# found in the LICENSE file.
7
8"""Unittest for machine_manager."""
9
10
11import hashlib
12import os.path
13import time
14import unittest
15import unittest.mock as mock
16
17from benchmark import Benchmark
18from benchmark_run import MockBenchmarkRun
19from cros_utils import command_executer
20from cros_utils import logger
21import image_checksummer
22import label
23import machine_manager
24import test_flag
25
26
27# pylint: disable=protected-access
28
29
30class MyMachineManager(machine_manager.MachineManager):
31    """Machine manager for test."""
32
33    def __init__(self, chromeos_root):
34        super(MyMachineManager, self).__init__(chromeos_root, 0, "average", "")
35
36    def _TryToLockMachine(self, cros_machine):
37        self._machines.append(cros_machine)
38        cros_machine.checksum = ""
39
40    def AddMachine(self, machine_name):
41        with self._lock:
42            for m in self._all_machines:
43                assert m.name != machine_name, (
44                    "Tried to double-add %s" % machine_name
45                )
46            cm = machine_manager.MockCrosMachine(
47                machine_name, self.chromeos_root, "average"
48            )
49            assert cm.machine_checksum, (
50                "Could not find checksum for machine %s" % machine_name
51            )
52            self._all_machines.append(cm)
53
54
55CHROMEOS_ROOT = "/tmp/chromeos-root"
56MACHINE_NAMES = ["lumpy1", "lumpy2", "lumpy3", "daisy1", "daisy2"]
57LABEL_LUMPY = label.MockLabel(
58    "lumpy",
59    "build",
60    "lumpy_chromeos_image",
61    "autotest_dir",
62    "debug_dir",
63    CHROMEOS_ROOT,
64    "lumpy",
65    ["lumpy1", "lumpy2", "lumpy3", "lumpy4"],
66    "",
67    "",
68    False,
69    "average",
70    "gcc",
71    False,
72    None,
73)
74LABEL_MIX = label.MockLabel(
75    "mix",
76    "build",
77    "chromeos_image",
78    "autotest_dir",
79    "debug_dir",
80    CHROMEOS_ROOT,
81    "mix",
82    ["daisy1", "daisy2", "lumpy3", "lumpy4"],
83    "",
84    "",
85    False,
86    "average",
87    "gcc",
88    False,
89    None,
90)
91
92
93class MachineManagerTest(unittest.TestCase):
94    """Test for machine manager class."""
95
96    msgs = []
97    image_log = []
98    log_fatal_msgs = []
99    fake_logger_count = 0
100    fake_logger_msgs = []
101
102    mock_cmd_exec = mock.Mock(spec=command_executer.CommandExecuter)
103
104    mock_logger = mock.Mock(spec=logger.Logger)
105
106    mock_lumpy1 = mock.Mock(spec=machine_manager.CrosMachine)
107    mock_lumpy2 = mock.Mock(spec=machine_manager.CrosMachine)
108    mock_lumpy3 = mock.Mock(spec=machine_manager.CrosMachine)
109    mock_lumpy4 = mock.Mock(spec=machine_manager.CrosMachine)
110    mock_daisy1 = mock.Mock(spec=machine_manager.CrosMachine)
111    mock_daisy2 = mock.Mock(spec=machine_manager.CrosMachine)
112
113    @mock.patch.object(os.path, "isdir")
114
115    # pylint: disable=arguments-differ
116    def setUp(self, mock_isdir):
117
118        mock_isdir.return_value = True
119        self.mm = machine_manager.MachineManager(
120            "/usr/local/chromeos",
121            0,
122            "average",
123            None,
124            self.mock_cmd_exec,
125            self.mock_logger,
126        )
127
128        self.mock_lumpy1.name = "lumpy1"
129        self.mock_lumpy2.name = "lumpy2"
130        self.mock_lumpy3.name = "lumpy3"
131        self.mock_lumpy4.name = "lumpy4"
132        self.mock_daisy1.name = "daisy1"
133        self.mock_daisy2.name = "daisy2"
134        self.mock_lumpy1.machine_checksum = "lumpy123"
135        self.mock_lumpy2.machine_checksum = "lumpy123"
136        self.mock_lumpy3.machine_checksum = "lumpy123"
137        self.mock_lumpy4.machine_checksum = "lumpy123"
138        self.mock_daisy1.machine_checksum = "daisy12"
139        self.mock_daisy2.machine_checksum = "daisy12"
140        self.mock_lumpy1.checksum_string = "lumpy_checksum_str"
141        self.mock_lumpy2.checksum_string = "lumpy_checksum_str"
142        self.mock_lumpy3.checksum_string = "lumpy_checksum_str"
143        self.mock_lumpy4.checksum_string = "lumpy_checksum_str"
144        self.mock_daisy1.checksum_string = "daisy_checksum_str"
145        self.mock_daisy2.checksum_string = "daisy_checksum_str"
146        self.mock_lumpy1.cpuinfo = "lumpy_cpu_info"
147        self.mock_lumpy2.cpuinfo = "lumpy_cpu_info"
148        self.mock_lumpy3.cpuinfo = "lumpy_cpu_info"
149        self.mock_lumpy4.cpuinfo = "lumpy_cpu_info"
150        self.mock_daisy1.cpuinfo = "daisy_cpu_info"
151        self.mock_daisy2.cpuinfo = "daisy_cpu_info"
152        self.mm._all_machines.append(self.mock_daisy1)
153        self.mm._all_machines.append(self.mock_daisy2)
154        self.mm._all_machines.append(self.mock_lumpy1)
155        self.mm._all_machines.append(self.mock_lumpy2)
156        self.mm._all_machines.append(self.mock_lumpy3)
157
158    def testGetMachines(self):
159        manager = MyMachineManager(CHROMEOS_ROOT)
160        for m in MACHINE_NAMES:
161            manager.AddMachine(m)
162        names = [m.name for m in manager.GetMachines(LABEL_LUMPY)]
163        self.assertEqual(names, ["lumpy1", "lumpy2", "lumpy3"])
164
165    def testGetAvailableMachines(self):
166        manager = MyMachineManager(CHROMEOS_ROOT)
167        for m in MACHINE_NAMES:
168            manager.AddMachine(m)
169        for m in manager._all_machines:
170            if int(m.name[-1]) % 2:
171                manager._TryToLockMachine(m)
172        names = [m.name for m in manager.GetAvailableMachines(LABEL_LUMPY)]
173        self.assertEqual(names, ["lumpy1", "lumpy3"])
174
175    @mock.patch.object(time, "sleep")
176    @mock.patch.object(command_executer.CommandExecuter, "RunCommand")
177    @mock.patch.object(command_executer.CommandExecuter, "CrosRunCommand")
178    @mock.patch.object(image_checksummer.ImageChecksummer, "Checksum")
179    def test_image_machine(
180        self, mock_checksummer, mock_run_croscmd, mock_run_cmd, mock_sleep
181    ):
182        def FakeMD5Checksum(_input_str):
183            return "machine_fake_md5_checksum"
184
185        self.fake_logger_count = 0
186        self.fake_logger_msgs = []
187
188        def FakeLogOutput(msg):
189            self.fake_logger_count += 1
190            self.fake_logger_msgs.append(msg)
191
192        def ResetValues():
193            self.fake_logger_count = 0
194            self.fake_logger_msgs = []
195            mock_run_cmd.reset_mock()
196            mock_run_croscmd.reset_mock()
197            mock_checksummer.reset_mock()
198            mock_sleep.reset_mock()
199            machine.checksum = "fake_md5_checksum"
200            self.mm.checksum = None
201            self.mm.num_reimages = 0
202
203        self.mock_cmd_exec.CrosRunCommand = mock_run_croscmd
204        self.mock_cmd_exec.RunCommand = mock_run_cmd
205
206        self.mm.logger.LogOutput = FakeLogOutput
207        machine = self.mock_lumpy1
208        machine._GetMD5Checksum = FakeMD5Checksum
209        machine.checksum = "fake_md5_checksum"
210        mock_checksummer.return_value = "fake_md5_checksum"
211        self.mock_cmd_exec.log_level = "verbose"
212
213        test_flag.SetTestMode(True)
214        # Test 1: label.image_type == "local"
215        LABEL_LUMPY.image_type = "local"
216        self.mm.ImageMachine(machine, LABEL_LUMPY)
217        self.assertEqual(mock_run_cmd.call_count, 0)
218        self.assertEqual(mock_run_croscmd.call_count, 0)
219
220        # Test 2: label.image_type == "trybot"
221        ResetValues()
222        LABEL_LUMPY.image_type = "trybot"
223        mock_run_cmd.return_value = 0
224        self.mm.ImageMachine(machine, LABEL_LUMPY)
225        self.assertEqual(mock_run_croscmd.call_count, 0)
226        self.assertEqual(mock_checksummer.call_count, 0)
227
228        # Test 3: label.image_type is neither local nor trybot; retval from
229        # RunCommand is 1, i.e. image_chromeos fails...
230        ResetValues()
231        LABEL_LUMPY.image_type = "other"
232        mock_run_cmd.return_value = 1
233        try:
234            self.mm.ImageMachine(machine, LABEL_LUMPY)
235        except RuntimeError:
236            self.assertEqual(mock_checksummer.call_count, 0)
237            self.assertEqual(mock_run_cmd.call_count, 2)
238            self.assertEqual(mock_run_croscmd.call_count, 1)
239            self.assertEqual(mock_sleep.call_count, 1)
240            image_call_args_str = mock_run_cmd.call_args[0][0]
241            image_call_args = image_call_args_str.split(" ")
242            self.assertEqual(image_call_args[0], "python")
243            self.assertEqual(
244                image_call_args[1].split("/")[-1], "image_chromeos.pyc"
245            )
246            image_call_args = image_call_args[2:]
247            self.assertEqual(
248                image_call_args,
249                [
250                    "--chromeos_root=/tmp/chromeos-root",
251                    "--image=lumpy_chromeos_image",
252                    "--image_args=",
253                    "--remote=lumpy1",
254                    "--logging_level=average",
255                    "--board=lumpy",
256                ],
257            )
258            self.assertEqual(mock_run_croscmd.call_args[0][0], "reboot && exit")
259
260        # Test 4: Everything works properly. Trybot image type.
261        ResetValues()
262        LABEL_LUMPY.image_type = "trybot"
263        mock_run_cmd.return_value = 0
264        self.mm.ImageMachine(machine, LABEL_LUMPY)
265        self.assertEqual(mock_checksummer.call_count, 0)
266        self.assertEqual(mock_run_croscmd.call_count, 0)
267        self.assertEqual(mock_sleep.call_count, 0)
268
269    def test_compute_common_checksum(self):
270        self.mm.machine_checksum = {}
271        self.mm.ComputeCommonCheckSum(LABEL_LUMPY)
272        self.assertEqual(self.mm.machine_checksum["lumpy"], "lumpy123")
273        self.assertEqual(len(self.mm.machine_checksum), 1)
274
275        self.mm.machine_checksum = {}
276        self.assertRaisesRegex(
277            machine_manager.BadChecksum,
278            r"daisy.*\n.*lumpy",
279            self.mm.ComputeCommonCheckSum,
280            LABEL_MIX,
281        )
282
283    def test_compute_common_checksum_string(self):
284        self.mm.machine_checksum_string = {}
285        self.mm.ComputeCommonCheckSumString(LABEL_LUMPY)
286        self.assertEqual(len(self.mm.machine_checksum_string), 1)
287        self.assertEqual(
288            self.mm.machine_checksum_string["lumpy"], "lumpy_checksum_str"
289        )
290
291        self.mm.machine_checksum_string = {}
292        self.mm.ComputeCommonCheckSumString(LABEL_MIX)
293        self.assertEqual(len(self.mm.machine_checksum_string), 1)
294        self.assertEqual(
295            self.mm.machine_checksum_string["mix"], "daisy_checksum_str"
296        )
297
298    @mock.patch.object(
299        command_executer.CommandExecuter, "CrosRunCommandWOutput"
300    )
301    def test_try_to_lock_machine(self, mock_cros_runcmd):
302        mock_cros_runcmd.return_value = [0, "false_lock_checksum", ""]
303        self.mock_cmd_exec.CrosRunCommandWOutput = mock_cros_runcmd
304        self.mm._machines = []
305        self.mm._TryToLockMachine(self.mock_lumpy1)
306        self.assertEqual(len(self.mm._machines), 1)
307        self.assertEqual(self.mm._machines[0], self.mock_lumpy1)
308        self.assertEqual(self.mock_lumpy1.checksum, "false_lock_checksum")
309        self.assertEqual(mock_cros_runcmd.call_count, 1)
310        cmd_str = mock_cros_runcmd.call_args[0][0]
311        self.assertEqual(cmd_str, "cat /usr/local/osimage_checksum_file")
312        args_dict = mock_cros_runcmd.call_args[1]
313        self.assertEqual(len(args_dict), 2)
314        self.assertEqual(args_dict["machine"], self.mock_lumpy1.name)
315        self.assertEqual(args_dict["chromeos_root"], "/usr/local/chromeos")
316
317    @mock.patch.object(machine_manager, "CrosMachine")
318    def test_add_machine(self, mock_machine):
319
320        mock_machine.machine_checksum = "daisy123"
321        self.assertEqual(len(self.mm._all_machines), 5)
322        self.mm.AddMachine("daisy3")
323        self.assertEqual(len(self.mm._all_machines), 6)
324
325        self.assertRaises(Exception, self.mm.AddMachine, "lumpy1")
326
327    def test_remove_machine(self):
328        self.mm._machines = self.mm._all_machines
329        self.assertTrue(self.mock_lumpy2 in self.mm._machines)
330        self.mm.RemoveMachine(self.mock_lumpy2.name)
331        self.assertFalse(self.mock_lumpy2 in self.mm._machines)
332
333    def test_force_same_image_to_all_machines(self):
334        self.image_log = []
335
336        def FakeImageMachine(machine, label_arg):
337            image = label_arg.chromeos_image
338            self.image_log.append("Pushed %s onto %s" % (image, machine.name))
339
340        def FakeSetUpChecksumInfo():
341            pass
342
343        self.mm.ImageMachine = FakeImageMachine
344        self.mock_lumpy1.SetUpChecksumInfo = FakeSetUpChecksumInfo
345        self.mock_lumpy2.SetUpChecksumInfo = FakeSetUpChecksumInfo
346        self.mock_lumpy3.SetUpChecksumInfo = FakeSetUpChecksumInfo
347
348        self.mm.ForceSameImageToAllMachines(LABEL_LUMPY)
349        self.assertEqual(len(self.image_log), 3)
350        self.assertEqual(
351            self.image_log[0], "Pushed lumpy_chromeos_image onto lumpy1"
352        )
353        self.assertEqual(
354            self.image_log[1], "Pushed lumpy_chromeos_image onto lumpy2"
355        )
356        self.assertEqual(
357            self.image_log[2], "Pushed lumpy_chromeos_image onto lumpy3"
358        )
359
360    @mock.patch.object(image_checksummer.ImageChecksummer, "Checksum")
361    @mock.patch.object(hashlib, "md5")
362    def test_acquire_machine(self, mock_md5, mock_checksum):
363
364        self.msgs = []
365        self.log_fatal_msgs = []
366
367        def FakeLock(machine):
368            self.msgs.append("Tried to lock %s" % machine.name)
369
370        def FakeLogFatal(msg):
371            self.log_fatal_msgs.append(msg)
372
373        self.mm._TryToLockMachine = FakeLock
374        self.mm.logger.LogFatal = FakeLogFatal
375
376        mock_md5.return_value = "123456"
377        mock_checksum.return_value = "fake_md5_checksum"
378
379        self.mm._machines = self.mm._all_machines
380        self.mock_lumpy1.locked = True
381        self.mock_lumpy2.locked = True
382        self.mock_lumpy3.locked = False
383        self.mock_lumpy3.checksum = "fake_md5_checksum"
384        self.mock_daisy1.locked = True
385        self.mock_daisy2.locked = False
386        self.mock_daisy2.checksum = "fake_md5_checksum"
387
388        self.mock_lumpy1.released_time = time.time()
389        self.mock_lumpy2.released_time = time.time()
390        self.mock_lumpy3.released_time = time.time()
391        self.mock_daisy1.released_time = time.time()
392        self.mock_daisy2.released_time = time.time()
393
394        # Test 1. Basic test. Acquire lumpy3.
395        self.mm.AcquireMachine(LABEL_LUMPY)
396        m = self.mock_lumpy1
397        self.assertEqual(m, self.mock_lumpy1)
398        self.assertTrue(self.mock_lumpy1.locked)
399        self.assertEqual(mock_md5.call_count, 0)
400        self.assertEqual(
401            self.msgs,
402            [
403                "Tried to lock lumpy1",
404                "Tried to lock lumpy2",
405                "Tried to lock lumpy3",
406            ],
407        )
408
409        # Test the second return statment (machine is unlocked, has no checksum)
410        save_locked = self.mock_lumpy1.locked
411        self.mock_lumpy1.locked = False
412        self.mock_lumpy1.checksum = None
413        m = self.mm.AcquireMachine(LABEL_LUMPY)
414        self.assertEqual(m, self.mock_lumpy1)
415        self.assertTrue(self.mock_lumpy1.locked)
416
417        # Test the third return statement:
418        #   - machine is unlocked
419        #   - checksums don't match
420        #   - current time minus release time is > 20.
421        self.mock_lumpy1.locked = False
422        self.mock_lumpy1.checksum = "123"
423        self.mock_lumpy1.released_time = time.time() - 8
424        m = self.mm.AcquireMachine(LABEL_LUMPY)
425        self.assertEqual(m, self.mock_lumpy1)
426        self.assertTrue(self.mock_lumpy1.locked)
427
428        # Test all machines are already locked.
429        m = self.mm.AcquireMachine(LABEL_LUMPY)
430        self.assertIsNone(m)
431
432        # Restore values of mock_lumpy1, so other tests succeed.
433        self.mock_lumpy1.locked = save_locked
434        self.mock_lumpy1.checksum = "123"
435
436    def test_get_available_machines(self):
437        self.mm._machines = self.mm._all_machines
438
439        machine_list = self.mm.GetAvailableMachines()
440        self.assertEqual(machine_list, self.mm._all_machines)
441
442        machine_list = self.mm.GetAvailableMachines(LABEL_MIX)
443        self.assertEqual(
444            machine_list, [self.mock_daisy1, self.mock_daisy2, self.mock_lumpy3]
445        )
446
447        machine_list = self.mm.GetAvailableMachines(LABEL_LUMPY)
448        self.assertEqual(
449            machine_list, [self.mock_lumpy1, self.mock_lumpy2, self.mock_lumpy3]
450        )
451
452    def test_get_machines(self):
453        machine_list = self.mm.GetMachines()
454        self.assertEqual(machine_list, self.mm._all_machines)
455
456        machine_list = self.mm.GetMachines(LABEL_MIX)
457        self.assertEqual(
458            machine_list, [self.mock_daisy1, self.mock_daisy2, self.mock_lumpy3]
459        )
460
461        machine_list = self.mm.GetMachines(LABEL_LUMPY)
462        self.assertEqual(
463            machine_list, [self.mock_lumpy1, self.mock_lumpy2, self.mock_lumpy3]
464        )
465
466    def test_release_machines(self):
467
468        self.mm._machines = [self.mock_lumpy1, self.mock_daisy2]
469
470        self.mock_lumpy1.locked = True
471        self.mock_daisy2.locked = True
472
473        self.assertTrue(self.mock_lumpy1.locked)
474        self.mm.ReleaseMachine(self.mock_lumpy1)
475        self.assertFalse(self.mock_lumpy1.locked)
476        self.assertEqual(self.mock_lumpy1.status, "Available")
477
478        self.assertTrue(self.mock_daisy2.locked)
479        self.mm.ReleaseMachine(self.mock_daisy2)
480        self.assertFalse(self.mock_daisy2.locked)
481        self.assertEqual(self.mock_daisy2.status, "Available")
482
483        # Test double-relase...
484        self.assertRaises(
485            AssertionError, self.mm.ReleaseMachine, self.mock_lumpy1
486        )
487
488    def test_cleanup(self):
489        self.mock_logger.reset_mock()
490        self.mm.Cleanup()
491        self.assertEqual(self.mock_logger.call_count, 0)
492
493    OUTPUT_STR = (
494        "Machine Status:\nMachine                        Thread     "
495        "Lock Status                    Checksum"
496        "                        \nlumpy1                         test "
497        "run   True PENDING                   123"
498        "                             \nlumpy2                         "
499        "test run   False PENDING                   123"
500        "                             \nlumpy3                         "
501        "test run   False PENDING                   123"
502        "                             \ndaisy1                         "
503        "test run   False PENDING                   678"
504        "                             \ndaisy2                         "
505        "test run   True PENDING                   678"
506        "                             "
507    )
508
509    def test_as_string(self):
510
511        mock_logger = mock.Mock(spec=logger.Logger)
512
513        bench = Benchmark(
514            "page_cycler_v2.netsim.top_10",  # name
515            "page_cycler_v2.netsim.top_10",  # test_name
516            "",  # test_args
517            1,  # iteratins
518            False,  # rm_chroot_tmp
519            "",  # perf_args
520            suite="telemetry_Crosperf",
521        )  # suite
522
523        test_run = MockBenchmarkRun(
524            "test run",
525            bench,
526            LABEL_LUMPY,
527            1,
528            [],
529            self.mm,
530            mock_logger,
531            "verbose",
532            "",
533            {},
534        )
535
536        self.mm._machines = [
537            self.mock_lumpy1,
538            self.mock_lumpy2,
539            self.mock_lumpy3,
540            self.mock_daisy1,
541            self.mock_daisy2,
542        ]
543
544        self.mock_lumpy1.test_run = test_run
545        self.mock_lumpy2.test_run = test_run
546        self.mock_lumpy3.test_run = test_run
547        self.mock_daisy1.test_run = test_run
548        self.mock_daisy2.test_run = test_run
549
550        self.mock_lumpy1.locked = True
551        self.mock_lumpy2.locked = False
552        self.mock_lumpy3.locked = False
553        self.mock_daisy1.locked = False
554        self.mock_daisy2.locked = True
555
556        self.mock_lumpy1.checksum = "123"
557        self.mock_lumpy2.checksum = "123"
558        self.mock_lumpy3.checksum = "123"
559        self.mock_daisy1.checksum = "678"
560        self.mock_daisy2.checksum = "678"
561
562        output = self.mm.AsString()
563        self.assertEqual(output, self.OUTPUT_STR)
564
565    def test_get_all_cpu_info(self):
566        info = self.mm.GetAllCPUInfo([LABEL_LUMPY, LABEL_MIX])
567        self.assertEqual(
568            info,
569            "lumpy\n-------------------\nlumpy_cpu_info\n\n\nmix\n-"
570            "------------------\ndaisy_cpu_info\n\n\n",
571        )
572
573
574MEMINFO_STRING = """MemTotal:        3990332 kB
575MemFree:         2608396 kB
576Buffers:          147168 kB
577Cached:           811560 kB
578SwapCached:            0 kB
579Active:           503480 kB
580Inactive:         628572 kB
581Active(anon):     174532 kB
582Inactive(anon):    88576 kB
583Active(file):     328948 kB
584Inactive(file):   539996 kB
585Unevictable:           0 kB
586Mlocked:               0 kB
587SwapTotal:       5845212 kB
588SwapFree:        5845212 kB
589Dirty:              9384 kB
590Writeback:             0 kB
591AnonPages:        173408 kB
592Mapped:           146268 kB
593Shmem:             89676 kB
594Slab:             188260 kB
595SReclaimable:     169208 kB
596SUnreclaim:        19052 kB
597KernelStack:        2032 kB
598PageTables:         7120 kB
599NFS_Unstable:          0 kB
600Bounce:                0 kB
601WritebackTmp:          0 kB
602CommitLimit:     7840376 kB
603Committed_AS:    1082032 kB
604VmallocTotal:   34359738367 kB
605VmallocUsed:      364980 kB
606VmallocChunk:   34359369407 kB
607DirectMap4k:       45824 kB
608DirectMap2M:     4096000 kB
609"""
610
611CPUINFO_STRING = """processor: 0
612vendor_id: GenuineIntel
613cpu family: 6
614model: 42
615model name: Intel(R) Celeron(R) CPU 867 @ 1.30GHz
616stepping: 7
617microcode: 0x25
618cpu MHz: 1300.000
619cache size: 2048 KB
620physical id: 0
621siblings: 2
622core id: 0
623cpu cores: 2
624apicid: 0
625initial apicid: 0
626fpu: yes
627fpu_exception: yes
628cpuid level: 13
629wp: yes
630flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer xsave lahf_lm arat epb xsaveopt pln pts dts tpr_shadow vnmi flexpriority ept vpid
631bogomips: 2594.17
632clflush size: 64
633cache_alignment: 64
634address sizes: 36 bits physical, 48 bits virtual
635power management:
636
637processor: 1
638vendor_id: GenuineIntel
639cpu family: 6
640model: 42
641model name: Intel(R) Celeron(R) CPU 867 @ 1.30GHz
642stepping: 7
643microcode: 0x25
644cpu MHz: 1300.000
645cache size: 2048 KB
646physical id: 0
647siblings: 2
648core id: 1
649cpu cores: 2
650apicid: 2
651initial apicid: 2
652fpu: yes
653fpu_exception: yes
654cpuid level: 13
655wp: yes
656flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer xsave lahf_lm arat epb xsaveopt pln pts dts tpr_shadow vnmi flexpriority ept vpid
657bogomips: 2594.17
658clflush size: 64
659cache_alignment: 64
660address sizes: 36 bits physical, 48 bits virtual
661power management:
662"""
663
664CHECKSUM_STRING = (
665    "processor: 0vendor_id: GenuineIntelcpu family: 6model: "
666    "42model name: Intel(R) Celeron(R) CPU 867 @ "
667    "1.30GHzstepping: 7microcode: 0x25cache size: 2048 "
668    "KBphysical id: 0siblings: 2cpu cores: 2"
669    "fpu: yesfpu_exception: yescpuid level: "
670    "13wp: yesflags: fpu vme de pse tsc msr pae mce cx8 apic sep"
671    " mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse "
672    "sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc "
673    "arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc "
674    "aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 "
675    "ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt "
676    "tsc_deadline_timer xsave lahf_lm arat epb xsaveopt pln pts "
677    "dts tpr_shadow vnmi flexpriority ept vpidclflush size: "
678    "64cache_alignment: 64address sizes: 36 bits physical, 48 "
679    "bits virtualpower management:processor: 1vendor_id: "
680    "GenuineIntelcpu family: 6model: 42model name: Intel(R) "
681    "Celeron(R) CPU 867 @ 1.30GHzstepping: 7microcode: 0x25cache"
682    " size: 2048 KBphysical id: 0siblings: 2cpu cores:"
683    " 2fpu: yesfpu_exception: yescpuid"
684    " level: 13wp: yesflags: fpu vme de pse tsc msr pae mce cx8 "
685    "apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx "
686    "fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm "
687    "constant_tsc arch_perfmon pebs bts rep_good nopl xtopology "
688    "nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl "
689    "vmx est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic "
690    "popcnt tsc_deadline_timer xsave lahf_lm arat epb xsaveopt "
691    "pln pts dts tpr_shadow vnmi flexpriority ept vpidclflush "
692    "size: 64cache_alignment: 64address sizes: 36 bits physical,"
693    " 48 bits virtualpower management: 4194304"
694)
695
696DUMP_VPD_STRING = """
697"PBA_SN"="Pba.txt"
698"Product_S/N"="HT4L91SC300208"
699"serial_number"="HT4L91SC300208Z"
700"System_UUID"="12153006-1755-4f66-b410-c43758a71127"
701"shipping_country"="US"
702"initial_locale"="en-US"
703"keyboard_layout"="xkb:us::eng"
704"initial_timezone"="America/Los_Angeles"
705"MACAddress"=""
706"System_UUID"="29dd9c61-7fa1-4c83-b89a-502e7eb08afe"
707"ubind_attribute"="0c433ce7585f486730b682bb05626a12ce2d896e9b57665387f8ce2ccfdcc56d2e2f1483"
708"gbind_attribute"="7e9a851324088e269319347c6abb8d1572ec31022fa07e28998229afe8acb45c35a89b9d"
709"ActivateDate"="2013-38"
710"""
711
712IFCONFIG_STRING = """
713eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
714        inet 172.17.129.247  netmask 255.255.254.0  broadcast 172.17.129.255
715        inet6 2620:0:1000:3002:143:fed4:3ff6:279d  prefixlen 64  scopeid 0x0<global>
716        inet6 2620:0:1000:3002:4459:1399:1f02:9e4c  prefixlen 64  scopeid 0x0<global>
717        inet6 2620:0:1000:3002:d9e4:87b:d4ec:9a0e  prefixlen 64  scopeid 0x0<global>
718        inet6 2620:0:1000:3002:7d45:23f1:ea8a:9604  prefixlen 64  scopeid 0x0<global>
719        inet6 2620:0:1000:3002:250:b6ff:fe63:db65  prefixlen 64  scopeid 0x0<global>
720        inet6 fe80::250:b6ff:fe63:db65  prefixlen 64  scopeid 0x20<link>
721        ether 00:50:b6:63:db:65  txqueuelen 1000  (Ethernet)
722        RX packets 9817166  bytes 10865181708 (10.1 GiB)
723        RX errors 194  dropped 0  overruns 0  frame 194
724        TX packets 0  bytes 2265811903 (2.1 GiB)
725        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
726
727eth1: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
728        ether e8:03:9a:9c:50:3d  txqueuelen 1000  (Ethernet)
729        RX packets 0  bytes 0 (0.0 B)
730        RX errors 0  dropped 0  overruns 0  frame 0
731        TX packets 0  bytes 0 (0.0 B)
732        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
733
734lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 16436
735        inet 127.0.0.1  netmask 255.0.0.0
736        inet6 ::1  prefixlen 128  scopeid 0x10<host>
737        loop  txqueuelen 0  (Local Loopback)
738        RX packets 981004  bytes 1127468524 (1.0 GiB)
739        RX errors 0  dropped 0  overruns 0  frame 0
740        TX packets 981004  bytes 1127468524 (1.0 GiB)
741        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
742
743wlan0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
744        ether 44:6d:57:20:4a:c5  txqueuelen 1000  (Ethernet)
745        RX packets 0  bytes 0 (0.0 B)
746        RX errors 0  dropped 0  overruns 0  frame 0
747        TX packets 0  bytes 0 (0.0 B)
748        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
749"""
750
751
752class CrosMachineTest(unittest.TestCase):
753    """Test for CrosMachine class."""
754
755    mock_cmd_exec = mock.Mock(spec=command_executer.CommandExecuter)
756
757    @mock.patch.object(machine_manager.CrosMachine, "SetUpChecksumInfo")
758    def test_init(self, mock_setup):
759
760        cm = machine_manager.CrosMachine(
761            "daisy.cros", "/usr/local/chromeos", "average", self.mock_cmd_exec
762        )
763        self.assertEqual(mock_setup.call_count, 1)
764        self.assertEqual(cm.chromeos_root, "/usr/local/chromeos")
765        self.assertEqual(cm.log_level, "average")
766
767    @mock.patch.object(machine_manager.CrosMachine, "IsReachable")
768    @mock.patch.object(machine_manager.CrosMachine, "_GetMemoryInfo")
769    @mock.patch.object(machine_manager.CrosMachine, "_GetCPUInfo")
770    @mock.patch.object(
771        machine_manager.CrosMachine, "_ComputeMachineChecksumString"
772    )
773    @mock.patch.object(machine_manager.CrosMachine, "_GetMachineID")
774    @mock.patch.object(machine_manager.CrosMachine, "_GetMD5Checksum")
775    def test_setup_checksum_info(
776        self,
777        mock_md5sum,
778        mock_machineid,
779        mock_checkstring,
780        mock_cpuinfo,
781        mock_meminfo,
782        mock_isreachable,
783    ):
784
785        # Test 1. Machine is not reachable; SetUpChecksumInfo is called via
786        # __init__.
787        mock_isreachable.return_value = False
788        mock_md5sum.return_value = "md5_checksum"
789        cm = machine_manager.CrosMachine(
790            "daisy.cros", "/usr/local/chromeos", "average", self.mock_cmd_exec
791        )
792        cm.checksum_string = "This is a checksum string."
793        cm.machine_id = "machine_id1"
794        self.assertEqual(mock_isreachable.call_count, 1)
795        self.assertIsNone(cm.machine_checksum)
796        self.assertEqual(mock_meminfo.call_count, 0)
797
798        # Test 2. Machine is reachable. Call explicitly.
799        mock_isreachable.return_value = True
800        cm.checksum_string = "This is a checksum string."
801        cm.machine_id = "machine_id1"
802        cm.SetUpChecksumInfo()
803        self.assertEqual(mock_isreachable.call_count, 2)
804        self.assertEqual(mock_meminfo.call_count, 1)
805        self.assertEqual(mock_cpuinfo.call_count, 1)
806        self.assertEqual(mock_checkstring.call_count, 1)
807        self.assertEqual(mock_machineid.call_count, 1)
808        self.assertEqual(mock_md5sum.call_count, 2)
809        self.assertEqual(cm.machine_checksum, "md5_checksum")
810        self.assertEqual(cm.machine_id_checksum, "md5_checksum")
811        self.assertEqual(
812            mock_md5sum.call_args_list[0][0][0], "This is a checksum string."
813        )
814        self.assertEqual(mock_md5sum.call_args_list[1][0][0], "machine_id1")
815
816    @mock.patch.object(command_executer.CommandExecuter, "CrosRunCommand")
817    @mock.patch.object(machine_manager.CrosMachine, "SetUpChecksumInfo")
818    def test_is_reachable(self, mock_setup, mock_run_cmd):
819
820        cm = machine_manager.CrosMachine(
821            "daisy.cros", "/usr/local/chromeos", "average", self.mock_cmd_exec
822        )
823        self.mock_cmd_exec.CrosRunCommand = mock_run_cmd
824
825        # Test 1. CrosRunCommand returns 1 (fail)
826        mock_run_cmd.return_value = 1
827        result = cm.IsReachable()
828        self.assertFalse(result)
829        self.assertEqual(mock_setup.call_count, 1)
830        self.assertEqual(mock_run_cmd.call_count, 1)
831
832        # Test 2. CrosRunCommand returns 0 (success)
833        mock_run_cmd.return_value = 0
834        result = cm.IsReachable()
835        self.assertTrue(result)
836        self.assertEqual(mock_run_cmd.call_count, 2)
837        first_args = mock_run_cmd.call_args_list[0]
838        second_args = mock_run_cmd.call_args_list[1]
839        self.assertEqual(first_args[0], second_args[0])
840        self.assertEqual(first_args[1], second_args[1])
841        self.assertEqual(len(first_args[0]), 1)
842        self.assertEqual(len(first_args[1]), 2)
843        self.assertEqual(first_args[0][0], "ls")
844        args_dict = first_args[1]
845        self.assertEqual(args_dict["machine"], "daisy.cros")
846        self.assertEqual(args_dict["chromeos_root"], "/usr/local/chromeos")
847
848    @mock.patch.object(machine_manager.CrosMachine, "SetUpChecksumInfo")
849    def test_parse_memory_info(self, _mock_setup):
850        cm = machine_manager.CrosMachine(
851            "daisy.cros", "/usr/local/chromeos", "average", self.mock_cmd_exec
852        )
853        cm.meminfo = MEMINFO_STRING
854        cm._ParseMemoryInfo()
855        self.assertEqual(cm.phys_kbytes, 4194304)
856
857    @mock.patch.object(
858        command_executer.CommandExecuter, "CrosRunCommandWOutput"
859    )
860    @mock.patch.object(machine_manager.CrosMachine, "SetUpChecksumInfo")
861    def test_get_memory_info(self, _mock_setup, mock_run_cmd):
862        cm = machine_manager.CrosMachine(
863            "daisy.cros", "/usr/local/chromeos", "average", self.mock_cmd_exec
864        )
865        self.mock_cmd_exec.CrosRunCommandWOutput = mock_run_cmd
866        mock_run_cmd.return_value = [0, MEMINFO_STRING, ""]
867        cm._GetMemoryInfo()
868        self.assertEqual(mock_run_cmd.call_count, 1)
869        call_args = mock_run_cmd.call_args_list[0]
870        self.assertEqual(call_args[0][0], "cat /proc/meminfo")
871        args_dict = call_args[1]
872        self.assertEqual(args_dict["machine"], "daisy.cros")
873        self.assertEqual(args_dict["chromeos_root"], "/usr/local/chromeos")
874        self.assertEqual(cm.meminfo, MEMINFO_STRING)
875        self.assertEqual(cm.phys_kbytes, 4194304)
876
877        mock_run_cmd.return_value = [1, MEMINFO_STRING, ""]
878        self.assertRaises(Exception, cm._GetMemoryInfo)
879
880    @mock.patch.object(
881        command_executer.CommandExecuter, "CrosRunCommandWOutput"
882    )
883    @mock.patch.object(machine_manager.CrosMachine, "SetUpChecksumInfo")
884    def test_get_cpu_info(self, _mock_setup, mock_run_cmd):
885        cm = machine_manager.CrosMachine(
886            "daisy.cros", "/usr/local/chromeos", "average", self.mock_cmd_exec
887        )
888        self.mock_cmd_exec.CrosRunCommandWOutput = mock_run_cmd
889        mock_run_cmd.return_value = [0, CPUINFO_STRING, ""]
890        cm._GetCPUInfo()
891        self.assertEqual(mock_run_cmd.call_count, 1)
892        call_args = mock_run_cmd.call_args_list[0]
893        self.assertEqual(call_args[0][0], "cat /proc/cpuinfo")
894        args_dict = call_args[1]
895        self.assertEqual(args_dict["machine"], "daisy.cros")
896        self.assertEqual(args_dict["chromeos_root"], "/usr/local/chromeos")
897        self.assertEqual(cm.cpuinfo, CPUINFO_STRING)
898
899    @mock.patch.object(machine_manager.CrosMachine, "SetUpChecksumInfo")
900    def test_compute_machine_checksum_string(self, _mock_setup):
901        cm = machine_manager.CrosMachine(
902            "daisy.cros", "/usr/local/chromeos", "average", self.mock_cmd_exec
903        )
904        cm.cpuinfo = CPUINFO_STRING
905        cm.meminfo = MEMINFO_STRING
906        cm._ParseMemoryInfo()
907        cm._ComputeMachineChecksumString()
908        self.assertEqual(cm.checksum_string, CHECKSUM_STRING)
909
910    @mock.patch.object(machine_manager.CrosMachine, "SetUpChecksumInfo")
911    def test_get_md5_checksum(self, _mock_setup):
912        cm = machine_manager.CrosMachine(
913            "daisy.cros", "/usr/local/chromeos", "average", self.mock_cmd_exec
914        )
915        temp_str = "abcde"
916        checksum_str = cm._GetMD5Checksum(temp_str)
917        self.assertEqual(checksum_str, "ab56b4d92b40713acc5af89985d4b786")
918
919        temp_str = ""
920        checksum_str = cm._GetMD5Checksum(temp_str)
921        self.assertEqual(checksum_str, "")
922
923    @mock.patch.object(
924        command_executer.CommandExecuter, "CrosRunCommandWOutput"
925    )
926    @mock.patch.object(machine_manager.CrosMachine, "SetUpChecksumInfo")
927    def test_get_machine_id(self, _mock_setup, mock_run_cmd):
928        cm = machine_manager.CrosMachine(
929            "daisy.cros", "/usr/local/chromeos", "average", self.mock_cmd_exec
930        )
931        self.mock_cmd_exec.CrosRunCommandWOutput = mock_run_cmd
932        mock_run_cmd.return_value = [0, DUMP_VPD_STRING, ""]
933
934        cm._GetMachineID()
935        self.assertEqual(cm.machine_id, '"Product_S/N"="HT4L91SC300208"')
936
937        mock_run_cmd.return_value = [0, IFCONFIG_STRING, ""]
938        cm._GetMachineID()
939        self.assertEqual(
940            cm.machine_id,
941            "        ether 00:50:b6:63:db:65  txqueuelen 1000  (Ethernet)_        "
942            "ether e8:03:9a:9c:50:3d  txqueuelen 1000  (Ethernet)_        ether "
943            "44:6d:57:20:4a:c5  txqueuelen 1000  (Ethernet)",
944        )
945
946        mock_run_cmd.return_value = [0, "invalid hardware config", ""]
947        self.assertRaises(Exception, cm._GetMachineID)
948
949    def test_add_cooldown_waittime(self):
950        cm = machine_manager.CrosMachine(
951            "1.2.3.4.cros", "/usr/local/chromeos", "average"
952        )
953        self.assertEqual(cm.GetCooldownWaitTime(), 0)
954        cm.AddCooldownWaitTime(250)
955        self.assertEqual(cm.GetCooldownWaitTime(), 250)
956        cm.AddCooldownWaitTime(1)
957        self.assertEqual(cm.GetCooldownWaitTime(), 251)
958
959
960if __name__ == "__main__":
961    unittest.main()
962