lisa/selftests/test_transformer.py

211 строки
7.3 KiB
Python

# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Dict, List, Type
from unittest import TestCase
import yaml
from dataclasses_json import dataclass_json
from lisa import LisaException, constants, schema, transformer
from lisa.parameter_parser.runbook import RunbookBuilder
from lisa.transformer import Transformer
from lisa.variable import VariableEntry
MOCK = "mock"
@dataclass_json
@dataclass
class TestTransformerSchema(schema.Transformer):
items: Dict[str, str] = field(default_factory=dict)
class TestTransformer(Transformer):
"""
This transformer transfer string to a list, so the combinator can use it.
"""
@classmethod
def type_name(cls) -> str:
return MOCK
@classmethod
def type_schema(cls) -> Type[schema.TypedSchema]:
return TestTransformerSchema
@property
def _output_names(self) -> List[str]:
runbook: TestTransformerSchema = self.runbook
return [x for x in runbook.items]
def _internal_run(self) -> Dict[str, Any]:
runbook: TestTransformerSchema = self.runbook
result: Dict[str, Any] = dict()
for name, value in runbook.items.items():
result[name] = f"{value} processed"
return result
class TestTransformerCase(TestCase):
def test_transformer_ordered(self) -> None:
# transformers are sorted by the dependent order
transformers = self._generate_transformers_runbook(3)
transformers[0].depends_on = ["t1", "t2"]
transformers[1].depends_on = ["t2"]
transformers = transformer._sort(transformers)
self.assertEqual("t2", transformers[0].name)
self.assertEqual("t1", transformers[1].name)
self.assertEqual("t0", transformers[2].name)
def test_transformer_cycle_detection(self) -> None:
# cycle reference and raise exception
transformers = self._generate_transformers_runbook(3)
transformers[0].depends_on = ["t1", "t2"]
transformers[1].depends_on = ["t0"]
with self.assertRaises(LisaException) as cm:
transformers = transformer._sort(transformers)
self.assertEqual(
"found cycle dependent transformers: 't1' and 't0'", str(cm.exception)
)
def test_transformer_keep_all_values(self) -> None:
# no value is overridden, all values are kept
transformers = self._generate_transformers_runbook(2)
runbook_builder = self._generate_runbook_builder(transformers)
result = transformer._run_transformers(runbook_builder)
self._validate_variables(
{
"v0": "original",
"va": "original",
"t0_v0": "0_0 processed",
"t1_v0": "1_0 processed",
"t1_v1": "1_1 processed",
},
result,
)
def test_transformer_overridden_values(self) -> None:
# value is overridden
transformers = self._generate_transformers_runbook(2)
transformers[0].rename = {"t0_v0": "v0"}
transformers[1].rename = {"t1_v0": "v0", "t1_v1": "v1"}
runbook_builder = self._generate_runbook_builder(transformers)
result = transformer._run_transformers(runbook_builder)
self._validate_variables(
{
"va": "original",
"v0": "1_0 processed",
"v1": "1_1 processed",
},
result,
)
def test_transformer_rename_not_exist(self) -> None:
# not exist name raise exception
transformers = self._generate_transformers_runbook(1)
transformers[0].rename = {"v0": "v0_1"}
runbook_builder = self._generate_runbook_builder(transformers)
with self.assertRaises(LisaException) as cm:
transformer._run_transformers(runbook_builder)
self.assertEqual("unmatched rename variable: {'v0': 'v0_1'}", str(cm.exception))
def test_transformer_customized_prefix(self) -> None:
# modified prefix
transformers = self._generate_transformers_runbook(1)
transformers[0].prefix = "v0_1"
runbook_builder = self._generate_runbook_builder(transformers)
result = transformer._run_transformers(runbook_builder)
self._validate_variables(
{
"v0": "original",
"va": "original",
"v0_1_v0": "0_0 processed",
},
result,
)
def test_transformer_no_name(self) -> None:
# no name, the type will be used. in name
transformers_data: List[Any] = [{"type": MOCK, "items": {"v0": "v0_1"}}]
transformers = schema.load_by_type_many(schema.Transformer, transformers_data)
runbook_builder = self._generate_runbook_builder(transformers)
result = transformer._run_transformers(runbook_builder)
self._validate_variables(
{
"v0": "original",
"va": "original",
"mock_v0": "v0_1 processed",
},
result,
)
def test_transformer_skip_disabled(self) -> None:
# the second transformer should be skipped, so the value is original.
transformers = self._generate_transformers_runbook(2)
transformers[0].rename = {"t0_v0": "v0"}
transformers[0].enabled = False
runbook_builder = self._generate_runbook_builder(transformers)
result = transformer._run_transformers(runbook_builder)
self._validate_variables(
{
"v0": "original",
"va": "original",
"t1_v0": "1_0 processed",
"t1_v1": "1_1 processed",
},
result,
)
def _validate_variables(
self, expected: Dict[str, str], actual: Dict[str, VariableEntry]
) -> None:
actual_pairs = {name: value.data for name, value in actual.items()}
self.assertDictEqual(expected, actual_pairs)
def _generate_runbook_builder(
self, transformers: List[schema.Transformer]
) -> RunbookBuilder:
transformers_data: List[Any] = [
x.to_dict() for x in transformers # type:ignore
]
test_runbook_path = Path(__file__).parent / "test_runbook.yml"
runbook_builder = RunbookBuilder(test_runbook_path)
runbook_builder._raw_data = {
constants.TRANSFORMER: transformers_data,
}
runbook_builder._variables = {
"v0": VariableEntry("v0", "original"),
"va": VariableEntry("va", "original"),
}
# write to file for reloading in runbook.derive method.
with open(test_runbook_path, "w") as file:
yaml.dump(runbook_builder._raw_data, file)
return runbook_builder
def _generate_transformers_runbook(self, count: int) -> List[schema.Transformer]:
results: List[schema.Transformer] = []
for index in range(count):
items: Dict[str, str] = dict()
for item_index in range(index + 1):
items[f"v{item_index}"] = f"{index}_{item_index}"
runbook: schema.Transformer = schema.load_by_type(
schema.Transformer, {"type": MOCK, "name": f"t{index}", "items": items}
)
results.append(runbook)
return results