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