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