310 lines
11 KiB
Python
Executable File
310 lines
11 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
# Copyright 2015 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
"""Unit tests for the MachineImageManager class."""
|
|
|
|
from __future__ import print_function
|
|
|
|
import random
|
|
import unittest
|
|
|
|
from machine_image_manager import MachineImageManager
|
|
|
|
|
|
class MockLabel(object):
|
|
"""Class for generating a mock Label."""
|
|
|
|
def __init__(self, name, remotes=None):
|
|
self.name = name
|
|
self.remote = remotes
|
|
|
|
def __hash__(self):
|
|
"""Provide hash function for label.
|
|
|
|
This is required because Label object is used inside a dict as key.
|
|
"""
|
|
return hash(self.name)
|
|
|
|
def __eq__(self, other):
|
|
"""Provide eq function for label.
|
|
|
|
This is required because Label object is used inside a dict as key.
|
|
"""
|
|
return isinstance(other, MockLabel) and other.name == self.name
|
|
|
|
|
|
class MockDut(object):
|
|
"""Class for creating a mock Device-Under-Test (DUT)."""
|
|
|
|
def __init__(self, name, label=None):
|
|
self.name = name
|
|
self.label_ = label
|
|
|
|
|
|
class MachineImageManagerTester(unittest.TestCase):
|
|
"""Class for testing MachineImageManager."""
|
|
|
|
def gen_duts_by_name(self, *names):
|
|
duts = []
|
|
for n in names:
|
|
duts.append(MockDut(n))
|
|
return duts
|
|
|
|
def create_labels_and_duts_from_pattern(self, pattern):
|
|
labels = []
|
|
duts = []
|
|
for i, r in enumerate(pattern):
|
|
l = MockLabel('l{}'.format(i), [])
|
|
for j, v in enumerate(r.split()):
|
|
if v == '.':
|
|
l.remote.append('m{}'.format(j))
|
|
if i == 0:
|
|
duts.append(MockDut('m{}'.format(j)))
|
|
labels.append(l)
|
|
return labels, duts
|
|
|
|
def check_matrix_against_pattern(self, matrix, pattern):
|
|
for i, s in enumerate(pattern):
|
|
for j, v in enumerate(s.split()):
|
|
self.assertTrue(v == '.' and matrix[i][j] == ' ' or v == matrix[i][j])
|
|
|
|
def pattern_based_test(self, inp, output):
|
|
labels, duts = self.create_labels_and_duts_from_pattern(inp)
|
|
mim = MachineImageManager(labels, duts)
|
|
self.assertTrue(mim.compute_initial_allocation())
|
|
self.check_matrix_against_pattern(mim.matrix_, output)
|
|
return mim
|
|
|
|
def test_single_dut(self):
|
|
labels = [MockLabel('l1'), MockLabel('l2'), MockLabel('l3')]
|
|
dut = MockDut('m1')
|
|
mim = MachineImageManager(labels, [dut])
|
|
mim.compute_initial_allocation()
|
|
self.assertTrue(mim.matrix_ == [['Y'], ['Y'], ['Y']])
|
|
|
|
def test_single_label(self):
|
|
labels = [MockLabel('l1')]
|
|
duts = self.gen_duts_by_name('m1', 'm2', 'm3')
|
|
mim = MachineImageManager(labels, duts)
|
|
mim.compute_initial_allocation()
|
|
self.assertTrue(mim.matrix_ == [['Y', 'Y', 'Y']])
|
|
|
|
def test_case1(self):
|
|
labels = [
|
|
MockLabel('l1', ['m1', 'm2']),
|
|
MockLabel('l2', ['m2', 'm3']),
|
|
MockLabel('l3', ['m1'])
|
|
]
|
|
duts = [MockDut('m1'), MockDut('m2'), MockDut('m3')]
|
|
mim = MachineImageManager(labels, duts)
|
|
self.assertTrue(
|
|
mim.matrix_ == [[' ', ' ', 'X'], ['X', ' ', ' '], [' ', 'X', 'X']])
|
|
mim.compute_initial_allocation()
|
|
self.assertTrue(
|
|
mim.matrix_ == [[' ', 'Y', 'X'], ['X', ' ', 'Y'], ['Y', 'X', 'X']])
|
|
|
|
def test_case2(self):
|
|
labels = [
|
|
MockLabel('l1', ['m1', 'm2']),
|
|
MockLabel('l2', ['m2', 'm3']),
|
|
MockLabel('l3', ['m1'])
|
|
]
|
|
duts = [MockDut('m1'), MockDut('m2'), MockDut('m3')]
|
|
mim = MachineImageManager(labels, duts)
|
|
self.assertTrue(
|
|
mim.matrix_ == [[' ', ' ', 'X'], ['X', ' ', ' '], [' ', 'X', 'X']])
|
|
mim.compute_initial_allocation()
|
|
self.assertTrue(
|
|
mim.matrix_ == [[' ', 'Y', 'X'], ['X', ' ', 'Y'], ['Y', 'X', 'X']])
|
|
|
|
def test_case3(self):
|
|
labels = [
|
|
MockLabel('l1', ['m1', 'm2']),
|
|
MockLabel('l2', ['m2', 'm3']),
|
|
MockLabel('l3', ['m1'])
|
|
]
|
|
duts = [MockDut('m1', labels[0]), MockDut('m2'), MockDut('m3')]
|
|
mim = MachineImageManager(labels, duts)
|
|
mim.compute_initial_allocation()
|
|
self.assertTrue(
|
|
mim.matrix_ == [[' ', 'Y', 'X'], ['X', ' ', 'Y'], ['Y', 'X', 'X']])
|
|
|
|
def test_case4(self):
|
|
labels = [
|
|
MockLabel('l1', ['m1', 'm2']),
|
|
MockLabel('l2', ['m2', 'm3']),
|
|
MockLabel('l3', ['m1'])
|
|
]
|
|
duts = [MockDut('m1'), MockDut('m2', labels[0]), MockDut('m3')]
|
|
mim = MachineImageManager(labels, duts)
|
|
mim.compute_initial_allocation()
|
|
self.assertTrue(
|
|
mim.matrix_ == [[' ', 'Y', 'X'], ['X', ' ', 'Y'], ['Y', 'X', 'X']])
|
|
|
|
def test_case5(self):
|
|
labels = [
|
|
MockLabel('l1', ['m3']),
|
|
MockLabel('l2', ['m3']),
|
|
MockLabel('l3', ['m1'])
|
|
]
|
|
duts = self.gen_duts_by_name('m1', 'm2', 'm3')
|
|
mim = MachineImageManager(labels, duts)
|
|
self.assertTrue(mim.compute_initial_allocation())
|
|
self.assertTrue(
|
|
mim.matrix_ == [['X', 'X', 'Y'], ['X', 'X', 'Y'], ['Y', 'X', 'X']])
|
|
|
|
def test_2x2_with_allocation(self):
|
|
labels = [MockLabel('l0'), MockLabel('l1')]
|
|
duts = [MockDut('m0'), MockDut('m1')]
|
|
mim = MachineImageManager(labels, duts)
|
|
self.assertTrue(mim.compute_initial_allocation())
|
|
self.assertTrue(mim.allocate(duts[0]) == labels[0])
|
|
self.assertTrue(mim.allocate(duts[0]) == labels[1])
|
|
self.assertTrue(mim.allocate(duts[0]) is None)
|
|
self.assertTrue(mim.matrix_[0][0] == '_')
|
|
self.assertTrue(mim.matrix_[1][0] == '_')
|
|
self.assertTrue(mim.allocate(duts[1]) == labels[1])
|
|
|
|
def test_10x10_general(self):
|
|
"""Gen 10x10 matrix."""
|
|
n = 10
|
|
labels = []
|
|
duts = []
|
|
for i in range(n):
|
|
labels.append(MockLabel('l{}'.format(i)))
|
|
duts.append(MockDut('m{}'.format(i)))
|
|
mim = MachineImageManager(labels, duts)
|
|
self.assertTrue(mim.compute_initial_allocation())
|
|
for i in range(n):
|
|
for j in range(n):
|
|
if i == j:
|
|
self.assertTrue(mim.matrix_[i][j] == 'Y')
|
|
else:
|
|
self.assertTrue(mim.matrix_[i][j] == ' ')
|
|
self.assertTrue(mim.allocate(duts[3]).name == 'l3')
|
|
|
|
def test_random_generated(self):
|
|
n = 10
|
|
labels = []
|
|
duts = []
|
|
for i in range(10):
|
|
# generate 3-5 machines that is compatible with this label
|
|
l = MockLabel('l{}'.format(i), [])
|
|
r = random.random()
|
|
for _ in range(4):
|
|
t = int(r * 10) % n
|
|
r *= 10
|
|
l.remote.append('m{}'.format(t))
|
|
labels.append(l)
|
|
duts.append(MockDut('m{}'.format(i)))
|
|
mim = MachineImageManager(labels, duts)
|
|
self.assertTrue(mim.compute_initial_allocation())
|
|
|
|
def test_10x10_fully_random(self):
|
|
inp = [
|
|
'X . . . X X . X X .', 'X X . X . X . X X .',
|
|
'X X X . . X . X . X', 'X . X X . . X X . X',
|
|
'X X X X . . . X . .', 'X X . X . X . . X .',
|
|
'. X . X . X X X . .', '. X . X X . X X . .',
|
|
'X X . . . X X X . .', '. X X X X . . . . X'
|
|
]
|
|
output = [
|
|
'X Y . . X X . X X .', 'X X Y X . X . X X .',
|
|
'X X X Y . X . X . X', 'X . X X Y . X X . X',
|
|
'X X X X . Y . X . .', 'X X . X . X Y . X .',
|
|
'Y X . X . X X X . .', '. X . X X . X X Y .',
|
|
'X X . . . X X X . Y', '. X X X X . . Y . X'
|
|
]
|
|
self.pattern_based_test(inp, output)
|
|
|
|
def test_10x10_fully_random2(self):
|
|
inp = [
|
|
'X . X . . X . X X X', 'X X X X X X . . X .',
|
|
'X . X X X X X . . X', 'X X X . X . X X . .',
|
|
'. X . X . X X X X X', 'X X X X X X X . . X',
|
|
'X . X X X X X . . X', 'X X X . X X X X . .',
|
|
'X X X . . . X X X X', '. X X . X X X . X X'
|
|
]
|
|
output = [
|
|
'X . X Y . X . X X X', 'X X X X X X Y . X .',
|
|
'X Y X X X X X . . X', 'X X X . X Y X X . .',
|
|
'. X Y X . X X X X X', 'X X X X X X X Y . X',
|
|
'X . X X X X X . Y X', 'X X X . X X X X . Y',
|
|
'X X X . Y . X X X X', 'Y X X . X X X . X X'
|
|
]
|
|
self.pattern_based_test(inp, output)
|
|
|
|
def test_3x4_with_allocation(self):
|
|
inp = ['X X . .', '. . X .', 'X . X .']
|
|
output = ['X X Y .', 'Y . X .', 'X Y X .']
|
|
mim = self.pattern_based_test(inp, output)
|
|
self.assertTrue(mim.allocate(mim.duts_[2]) == mim.labels_[0])
|
|
self.assertTrue(mim.allocate(mim.duts_[3]) == mim.labels_[2])
|
|
self.assertTrue(mim.allocate(mim.duts_[0]) == mim.labels_[1])
|
|
self.assertTrue(mim.allocate(mim.duts_[1]) == mim.labels_[2])
|
|
self.assertTrue(mim.allocate(mim.duts_[3]) == mim.labels_[1])
|
|
self.assertTrue(mim.allocate(mim.duts_[3]) == mim.labels_[0])
|
|
self.assertTrue(mim.allocate(mim.duts_[3]) is None)
|
|
self.assertTrue(mim.allocate(mim.duts_[2]) is None)
|
|
self.assertTrue(mim.allocate(mim.duts_[1]) == mim.labels_[1])
|
|
self.assertTrue(mim.allocate(mim.duts_[1]) is None)
|
|
self.assertTrue(mim.allocate(mim.duts_[0]) is None)
|
|
self.assertTrue(mim.label_duts_[0] == [2, 3])
|
|
self.assertTrue(mim.label_duts_[1] == [0, 3, 1])
|
|
self.assertTrue(mim.label_duts_[2] == [3, 1])
|
|
self.assertListEqual(mim.allocate_log_, [(0, 2), (2, 3), (1, 0), (2, 1),
|
|
(1, 3), (0, 3), (1, 1)])
|
|
|
|
def test_cornercase_1(self):
|
|
"""This corner case is brought up by Caroline.
|
|
|
|
The description is -
|
|
|
|
If you have multiple labels and multiple machines, (so we don't
|
|
automatically fall into the 1 dut or 1 label case), but all of the
|
|
labels specify the same 1 remote, then instead of assigning the same
|
|
machine to all the labels, your algorithm fails to assign any...
|
|
|
|
So first step is to create an initial matrix like below, l0, l1 and l2
|
|
all specify the same 1 remote - m0.
|
|
|
|
m0 m1 m2
|
|
l0 . X X
|
|
|
|
l1 . X X
|
|
|
|
l2 . X X
|
|
|
|
The search process will be like this -
|
|
a) try to find a solution with at most 1 'Y's per column (but ensure at
|
|
least 1 Y per row), fail
|
|
b) try to find a solution with at most 2 'Y's per column (but ensure at
|
|
least 1 Y per row), fail
|
|
c) try to find a solution with at most 3 'Y's per column (but ensure at
|
|
least 1 Y per row), succeed, so we end up having this solution
|
|
|
|
m0 m1 m2
|
|
l0 Y X X
|
|
|
|
l1 Y X X
|
|
|
|
l2 Y X X
|
|
"""
|
|
|
|
inp = ['. X X', '. X X', '. X X']
|
|
output = ['Y X X', 'Y X X', 'Y X X']
|
|
mim = self.pattern_based_test(inp, output)
|
|
self.assertTrue(mim.allocate(mim.duts_[1]) is None)
|
|
self.assertTrue(mim.allocate(mim.duts_[2]) is None)
|
|
self.assertTrue(mim.allocate(mim.duts_[0]) == mim.labels_[0])
|
|
self.assertTrue(mim.allocate(mim.duts_[0]) == mim.labels_[1])
|
|
self.assertTrue(mim.allocate(mim.duts_[0]) == mim.labels_[2])
|
|
self.assertTrue(mim.allocate(mim.duts_[0]) is None)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|