| /** |
| * ******************************************************************************* |
| * Copyright (c) 2015-2021 Robert Bosch GmbH and others. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Robert Bosch GmbH - initial API and implementation |
| * ******************************************************************************* |
| */ |
| |
| package org.eclipse.app4mc.amalthea.validations.standard.tests |
| |
| import java.util.List |
| import org.eclipse.app4mc.amalthea.model.Amalthea |
| import org.eclipse.app4mc.amalthea.model.InterruptController |
| import org.eclipse.app4mc.amalthea.model.ProcessingUnit |
| import org.eclipse.app4mc.amalthea.model.ProcessingUnitDefinition |
| import org.eclipse.app4mc.amalthea.model.SchedulerAllocation |
| import org.eclipse.app4mc.amalthea.model.Task |
| import org.eclipse.app4mc.amalthea.model.TaskScheduler |
| import org.eclipse.app4mc.amalthea.model.builder.AmaltheaBuilder |
| import org.eclipse.app4mc.amalthea.model.builder.HardwareBuilder |
| import org.eclipse.app4mc.amalthea.model.builder.MappingBuilder |
| import org.eclipse.app4mc.amalthea.model.builder.OperatingSystemBuilder |
| import org.eclipse.app4mc.amalthea.model.builder.SoftwareBuilder |
| import org.eclipse.app4mc.amalthea.validations.standard.EMFProfile |
| import org.eclipse.app4mc.amalthea.validations.standard.MappingProfile |
| import org.eclipse.app4mc.validation.core.Severity |
| import org.eclipse.app4mc.validation.core.ValidationDiagnostic |
| import org.eclipse.app4mc.validation.util.ValidationExecutor |
| import org.junit.Test |
| |
| import static org.junit.Assert.assertFalse |
| import static org.junit.Assert.assertTrue |
| import org.eclipse.app4mc.amalthea.model.HwStructure |
| import org.eclipse.app4mc.amalthea.model.util.HardwareUtil |
| |
| class MappingModelTests { |
| |
| extension AmaltheaBuilder b1 = new AmaltheaBuilder |
| extension SoftwareBuilder b2 = new SoftwareBuilder |
| extension OperatingSystemBuilder b3 = new OperatingSystemBuilder |
| extension HardwareBuilder b4 = new HardwareBuilder |
| extension MappingBuilder b5 = new MappingBuilder |
| |
| val executor = new ValidationExecutor( #[MappingProfile, EMFProfile] ) |
| |
| def List<ValidationDiagnostic> runExecutor(Amalthea model) { |
| executor.validate(model) |
| executor.results |
| } |
| |
| def Amalthea createValidTestModel() { |
| amalthea [ |
| softwareModel [ |
| task [ name = "TestTask" ] |
| ] |
| hardwareModel [ |
| definition_ProcessingUnit [ name = "TestCoreDef" ] |
| structure [ |
| name = "System" |
| module_ProcessingUnit [ |
| name = "TestCore" |
| definition = _find(ProcessingUnitDefinition, "TestCoreDef") |
| ] |
| ] |
| ] |
| osModel [ |
| operatingSystem [ name = "TestOS" |
| taskScheduler [ name = "TestScheduler" ] |
| ] |
| ] |
| mappingModel [ |
| taskAllocation [ |
| task = _find(Task, "TestTask") |
| scheduler = _find(TaskScheduler, "TestScheduler") |
| ] |
| schedulerAllocation [ |
| scheduler = _find(TaskScheduler, "TestScheduler") |
| responsibility += _find(ProcessingUnit, "TestCore") |
| ] |
| ] |
| ] |
| } |
| |
| @Test |
| def void testTaskToSchedulerToCoreMapping() { |
| val model = createValidTestModel() |
| |
| val validationResult = runExecutor(model) |
| |
| val result = validationResult.filter[it.severityLevel == Severity.ERROR].map[it.message].toList |
| assertTrue(result.isEmpty) |
| } |
| |
| @Test |
| def void testTaskToSchedulerToCoreMapping_UnmappedTasks() { |
| val model = createValidTestModel() |
| // add unmapped task |
| model.swModel.task [ name = "TestTask_left"] |
| |
| val validationResult = runExecutor(model) |
| |
| val result = validationResult.filter[it.severityLevel == Severity.WARNING].map[it.message].toList |
| assertTrue(result.contains("Unmapped task found: \"TestTask_left\"")) |
| } |
| |
| @Test |
| def void testTaskToSchedulerToCoreMapping_UnmappedScheduler() { |
| val model = createValidTestModel() |
| // add unmapped scheduler |
| model.osModel.operatingSystems.head.taskScheduler [ name = "TestScheduler_left"] |
| |
| val validationResult = runExecutor(model) |
| |
| val result = validationResult.filter[it.severityLevel == Severity.WARNING].map[it.message].toList |
| assertTrue(result.contains("Scheduler not responsible for any core: \"TestScheduler_left\"")) |
| } |
| |
| @Test |
| def void testTaskToSchedulerToCoreMapping_MissingSchedulerInTaskAlloc() { |
| val model = createValidTestModel() |
| // task allocation: remove reference to scheduler |
| model.mappingModel.taskAllocation.head.scheduler = null |
| |
| val validationResult = runExecutor(model) |
| |
| val result = validationResult.filter[it.severityLevel == Severity.ERROR].map[it.message].toList |
| assertTrue(result.contains("The required feature 'scheduler' of 'TaskAllocation' must be set")) |
| } |
| |
| @Test |
| def void testTaskToSchedulerToCoreMapping_MissingTaskInTaskAlloc() { |
| val model = createValidTestModel() |
| // task allocation: remove reference to task |
| model.mappingModel.taskAllocation.head.task = null |
| |
| val validationResult = runExecutor(model) |
| |
| val errors = validationResult.filter[it.severityLevel == Severity.ERROR].map[it.message].toList |
| val warnings = validationResult.filter[it.severityLevel == Severity.WARNING].map[it.message].toList |
| |
| assertTrue(errors.contains("The required feature 'task' of 'TaskAllocation' must be set")) |
| assertTrue(warnings.contains("Unmapped task found: \"TestTask\"")) |
| } |
| |
| @Test |
| def void testTaskToSchedulerToCoreMapping_MissingTaskAndSchedulerInTaskAlloc() { |
| val model = createValidTestModel() |
| // task allocation: remove references to scheduler and task |
| model.mappingModel.taskAllocation.head.scheduler = null |
| model.mappingModel.taskAllocation.head.task = null |
| |
| val validationResult = runExecutor(model) |
| |
| val errors = validationResult.filter[it.severityLevel == Severity.ERROR].map[it.message].toList |
| val warnings = validationResult.filter[it.severityLevel == Severity.WARNING].map[it.message].toList |
| |
| assertTrue(errors.contains("The required feature 'scheduler' of 'TaskAllocation' must be set")) |
| assertTrue(errors.contains("The required feature 'task' of 'TaskAllocation' must be set")) |
| assertTrue(warnings.contains("Unmapped task found: \"TestTask\"")) |
| } |
| |
| @Test |
| def void testTaskToSchedulerToCoreMapping_MissingSchedulerInSchedulerAlloc() { |
| val model = createValidTestModel() |
| // scheduler allocation: remove references to scheduler |
| model.mappingModel.schedulerAllocation.head.scheduler = null |
| |
| val validationResult = runExecutor(model) |
| |
| val errors = validationResult.filter[it.severityLevel == Severity.ERROR].map[it.message].toList |
| val warnings = validationResult.filter[it.severityLevel == Severity.WARNING].map[it.message].toList |
| |
| assertTrue(errors.contains("The required feature 'scheduler' of 'SchedulerAllocation' must be set")) |
| assertTrue(warnings.contains("Scheduler not responsible for any core: \"TestScheduler\"")) |
| } |
| |
| @Test |
| def void testTaskToSchedulerToCoreMapping_MissingCoreInSchedulerAlloc() { |
| val model = createValidTestModel() |
| // scheduler allocation: remove responsibilities |
| model.mappingModel.schedulerAllocation.head.responsibility.clear |
| |
| val validationResult = runExecutor(model) |
| |
| val result = validationResult.filter[it.severityLevel == Severity.ERROR].map[it.message].toList |
| assertTrue(result.contains("The feature 'responsibility' of 'SchedulerAllocation' with 0 values must have at least 1 values")) |
| } |
| |
| @Test |
| def void testTaskToSchedulerToCoreMapping_MissingSchedulerAndCoreInSchedulerAlloc() { |
| val model = createValidTestModel() |
| // scheduler allocation: remove references to scheduler and responsibilities |
| model.mappingModel.schedulerAllocation.head.scheduler = null |
| model.mappingModel.schedulerAllocation.head.responsibility.clear |
| |
| val validationResult = runExecutor(model) |
| |
| val errors = validationResult.filter[it.severityLevel == Severity.ERROR].map[it.message].toList |
| val warnings = validationResult.filter[it.severityLevel == Severity.WARNING].map[it.message].toList |
| |
| assertTrue(errors.contains("The required feature 'scheduler' of 'SchedulerAllocation' must be set")) |
| assertTrue(errors.contains("The feature 'responsibility' of 'SchedulerAllocation' with 0 values must have at least 1 values")) |
| assertTrue(warnings.contains("Scheduler not responsible for any core: \"TestScheduler\"")) |
| } |
| |
| @Test |
| def void testSchedulerAllocation_MultipleTopLevelSchedulerResponsibilities() { |
| val model = amalthea [ |
| hardwareModel [ |
| structure [ |
| name = "System" |
| module_ProcessingUnit [ name = "core1" ] |
| module_ProcessingUnit [ name = "core2" ] |
| ] |
| ] |
| osModel [ |
| operatingSystem [ name = "TestOS" |
| taskScheduler [ name = "scheduler_ok" ] |
| taskScheduler [ name = "child_scheduler_ok" |
| parentAssociation [ |
| parent = _find(TaskScheduler, "scheduler_ok") |
| ] |
| ] |
| taskScheduler [ name = "scheduler_notOk" ] |
| interruptController [ name = "ic_ok" ] |
| interruptController [ name = "ic_notOk" ] |
| ] |
| ] |
| mappingModel [ |
| schedulerAllocation [ |
| scheduler = _find(TaskScheduler, "scheduler_ok") |
| responsibility += #[_find(ProcessingUnit, "core1"), _find(ProcessingUnit, "core2")] |
| ] |
| schedulerAllocation [ |
| scheduler = _find(TaskScheduler, "child_scheduler_ok") |
| responsibility += _find(ProcessingUnit, "core2") |
| ] |
| schedulerAllocation [ |
| scheduler = _find(TaskScheduler, "scheduler_notOk") |
| responsibility += _find(ProcessingUnit, "core2") |
| ] |
| schedulerAllocation [ |
| scheduler = _find(InterruptController, "ic_ok") |
| responsibility += #[_find(ProcessingUnit, "core1"), _find(ProcessingUnit, "core2")] |
| ] |
| schedulerAllocation [ |
| scheduler = _find(InterruptController, "ic_notOk") |
| responsibility += _find(ProcessingUnit, "core1") |
| ] |
| ] |
| ] |
| |
| val tlRespIssues = runExecutor(model).filter[it.validationID == "AM-Mapping-Scheduler-Allocation-Top-Level-Responsibility"] |
| |
| assertTrue(tlRespIssues.size == 2) |
| |
| val tlSchedResp = tlRespIssues.filter[(it.targetObject as SchedulerAllocation).scheduler instanceof TaskScheduler].head |
| val tlSchedName = (tlSchedResp.targetObject as SchedulerAllocation).scheduler.name |
| |
| assertFalse(tlSchedName == "child_scheduler_ok") |
| assertTrue(tlSchedResp.message == "Processing Unit \"core2\" should have at most one top level scheduler that is responsible for it" |
| && tlSchedName == "scheduler_notOk" |
| ) |
| |
| val iCResp = tlRespIssues.filter[(it.targetObject as SchedulerAllocation).scheduler instanceof InterruptController].head |
| val iCName = (iCResp.targetObject as SchedulerAllocation).scheduler.name |
| |
| assertTrue(iCResp.message == "Processing Unit \"core1\" should have at most one interrupt controller that is responsible for it" |
| && iCName == "ic_notOk" |
| ) |
| } |
| |
| @Test |
| def void testSchedulerAllocation_NestedResponsibilities_SimpleHierarchy() { |
| val model = amalthea [ |
| hardwareModel [ |
| structure [ |
| name = "System" |
| module_ProcessingUnit [ name = "core1" ] |
| module_ProcessingUnit [ name = "core2" ] |
| module_ProcessingUnit [ name = "core3" ] |
| module_ProcessingUnit [ name = "core4" ] |
| ] |
| ] |
| osModel [ |
| operatingSystem [ name = "TestOS" |
| taskScheduler [ name = "parent_scheduler_ok" ] |
| taskScheduler [ name = "child1_scheduler_ok" |
| parentAssociation [ |
| parent = _find(TaskScheduler, "parent_scheduler_ok") |
| ] |
| ] |
| taskScheduler [ name = "child2_scheduler_ok" |
| parentAssociation [ |
| parent = _find(TaskScheduler, "parent_scheduler_ok") |
| ] |
| ] |
| taskScheduler [ name = "child3_scheduler_notOk" |
| parentAssociation [ |
| parent = _find(TaskScheduler, "parent_scheduler_ok") |
| ] |
| ] |
| ] |
| ] |
| mappingModel [ |
| schedulerAllocation [ |
| scheduler = _find(TaskScheduler, "parent_scheduler_ok") |
| responsibility += #[_find(ProcessingUnit, "core1"), _find(ProcessingUnit, "core2"), _find(ProcessingUnit, "core3")] |
| ] |
| schedulerAllocation [ |
| scheduler = _find(TaskScheduler, "child1_scheduler_ok") |
| responsibility += #[_find(ProcessingUnit, "core1"), _find(ProcessingUnit, "core2")] |
| ] |
| schedulerAllocation [ |
| scheduler = _find(TaskScheduler, "child2_scheduler_ok") |
| responsibility += #[_find(ProcessingUnit, "core2"), _find(ProcessingUnit, "core3")] |
| ] |
| schedulerAllocation [ |
| scheduler = _find(TaskScheduler, "child3_scheduler_notOk") |
| responsibility += #[_find(ProcessingUnit, "core3"), _find(ProcessingUnit, "core4")] |
| ] |
| ] |
| ] |
| |
| val hierarchyIssues = runExecutor(model).filter[it.validationID == "AM-Mapping-Scheduler-Allocation-Hierarchy"] |
| |
| assertTrue(hierarchyIssues.size == 1) |
| assertTrue(hierarchyIssues.head.message == "Scheduler Allocation for child Task Scheduler \"child3_scheduler_notOk\" " |
| + "should only be responsible for a subset of processing units of its parent schedulers" |
| ) |
| } |
| |
| @Test |
| def void testSchedulerAllocation_NestedResponsibilities_DeeperHierarchy() { |
| val model = amalthea [ |
| hardwareModel [ |
| structure [ |
| name = "System" |
| module_ProcessingUnit [ name = "core1" ] |
| module_ProcessingUnit [ name = "core2" ] |
| module_ProcessingUnit [ name = "core3" ] |
| module_ProcessingUnit [ name = "core4" ] |
| module_ProcessingUnit [ name = "core5" ] |
| module_ProcessingUnit [ name = "core6" ] |
| ] |
| ] |
| osModel [ |
| operatingSystem [ name = "TestOS" |
| /* |
| * -- a1 -- a2 |
| * / \ | |
| * b1 b2 b3 |
| * / \ / | \ | \ |
| * c1 c2 c3 c4 c5 c6 c7 |
| * / \ ~~~~ | ~~~~ |
| * d1 d2 d3 |
| * ~~~~ |
| */ |
| taskScheduler [ name = "a1_scheduler_ok" ] |
| taskScheduler [ name = "b1_scheduler_ok" |
| parentAssociation [ |
| parent = _find(TaskScheduler, "a1_scheduler_ok") |
| ] |
| ] |
| taskScheduler [name = "c1_scheduler_ok" |
| parentAssociation [ |
| parent = _find(TaskScheduler, "b1_scheduler_ok") |
| ] |
| ] |
| taskScheduler [name = "d1_scheduler_ok" |
| parentAssociation [ |
| parent = _find(TaskScheduler, "c1_scheduler_ok") |
| ] |
| ] |
| taskScheduler [name = "d2_scheduler_notOk" |
| parentAssociation [ |
| parent = _find(TaskScheduler, "c1_scheduler_ok") |
| ] |
| ] |
| taskScheduler [ name = "c2_scheduler_ok" |
| parentAssociation [ |
| parent = _find(TaskScheduler, "b1_scheduler_ok") |
| ] |
| ] |
| taskScheduler [ name = "b2_scheduler_ok" |
| parentAssociation [ |
| parent = _find(TaskScheduler, "a1_scheduler_ok") |
| ] |
| ] |
| taskScheduler [ name = "c3_scheduler_notOk" |
| parentAssociation [ |
| parent = _find(TaskScheduler, "b2_scheduler_ok") |
| ] |
| ] |
| taskScheduler [ name = "c4_scheduler_ok" |
| parentAssociation [ |
| parent = _find(TaskScheduler, "b2_scheduler_ok") |
| ] |
| ] |
| taskScheduler [name = "d3_scheduler_ok" |
| parentAssociation [ |
| parent = _find(TaskScheduler, "c4_scheduler_ok") |
| ] |
| ] |
| taskScheduler [ name = "c5_scheduler_ok" |
| parentAssociation [ |
| parent = _find(TaskScheduler, "b2_scheduler_ok") |
| ] |
| ] |
| taskScheduler [ name = "a2_scheduler_ok" ] |
| taskScheduler [ name = "b3_scheduler_ok" |
| parentAssociation [ |
| parent = _find(TaskScheduler, "a2_scheduler_ok") |
| ] |
| ] |
| taskScheduler [ name = "c6_scheduler_notOk" |
| parentAssociation [ |
| parent = _find(TaskScheduler, "b3_scheduler_ok") |
| ] |
| ] |
| taskScheduler [ name = "c7_scheduler_ok" |
| parentAssociation [ |
| parent = _find(TaskScheduler, "b3_scheduler_ok") |
| ] |
| ] |
| ] |
| ] |
| mappingModel [ |
| schedulerAllocation [ |
| scheduler = _find(TaskScheduler, "a1_scheduler_ok") |
| responsibility += HardwareUtil.getModulesFromHWStructure(ProcessingUnit, _find(HwStructure, "System")) |
| responsibility -= _find(ProcessingUnit, "core6") |
| ] |
| schedulerAllocation [ |
| scheduler = _find(TaskScheduler, "b1_scheduler_ok") |
| responsibility += #[_find(ProcessingUnit, "core1"), _find(ProcessingUnit, "core2")] |
| ] |
| schedulerAllocation [ |
| scheduler = _find(TaskScheduler, "c2_scheduler_ok") |
| responsibility += #[_find(ProcessingUnit, "core2"), _find(ProcessingUnit, "core3")] |
| ] |
| schedulerAllocation [ |
| scheduler = _find(TaskScheduler, "c3_scheduler_notOk") // ancestors do not schedule core6 |
| responsibility += #[_find(ProcessingUnit, "core4"), _find(ProcessingUnit, "core6")] |
| ] |
| schedulerAllocation [ |
| scheduler = _find(TaskScheduler, "c5_scheduler_ok") |
| responsibility += #[_find(ProcessingUnit, "core4"), _find(ProcessingUnit, "core5")] |
| ] |
| schedulerAllocation [ |
| scheduler = _find(TaskScheduler, "d1_scheduler_ok") |
| responsibility += _find(ProcessingUnit, "core3") |
| ] |
| schedulerAllocation [ |
| scheduler = _find(TaskScheduler, "d2_scheduler_notOk") // ancestors do not schedule core6 |
| responsibility += _find(ProcessingUnit, "core6") |
| ] |
| schedulerAllocation [ |
| scheduler = _find(TaskScheduler, "a2_scheduler_ok") |
| responsibility += _find(ProcessingUnit, "core6") |
| ] |
| schedulerAllocation [ |
| scheduler = _find(TaskScheduler, "c6_scheduler_notOk") // ancestors do not schedule core5 |
| responsibility += #[_find(ProcessingUnit, "core5"), _find(ProcessingUnit, "core6")] |
| ] |
| schedulerAllocation [ |
| scheduler = _find(TaskScheduler, "c7_scheduler_ok") |
| responsibility += _find(ProcessingUnit, "core6") |
| ] |
| ] |
| ] |
| |
| val hierarchyIssues = runExecutor(model).filter[it.validationID == "AM-Mapping-Scheduler-Allocation-Hierarchy"].map[it.message].toList |
| |
| assertTrue(hierarchyIssues.size == 3) |
| assertTrue(hierarchyIssues.contains("Scheduler Allocation for child Task Scheduler \"c3_scheduler_notOk\" " |
| + "should only be responsible for a subset of processing units of its parent schedulers") |
| ) |
| assertTrue(hierarchyIssues.contains("Scheduler Allocation for child Task Scheduler \"d2_scheduler_notOk\" " |
| + "should only be responsible for a subset of processing units of its parent schedulers") |
| ) |
| assertTrue(hierarchyIssues.contains("Scheduler Allocation for child Task Scheduler \"c6_scheduler_notOk\" " |
| + "should only be responsible for a subset of processing units of its parent schedulers") |
| ) |
| } |
| } |