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