This repository has been archived by the owner on Jul 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added initial implementation for BaselineManager
- Loading branch information
1 parent
85f1181
commit 6d93442
Showing
3 changed files
with
257 additions
and
0 deletions.
There are no files selected for viewing
42 changes: 42 additions & 0 deletions
42
analysis/src/main/java/com/buschmais/jqassistant/core/analysis/api/baseline/Baseline.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package com.buschmais.jqassistant.core.analysis.api.baseline; | ||
|
||
import java.util.SortedMap; | ||
import java.util.TreeMap; | ||
|
||
import lombok.Getter; | ||
import lombok.ToString; | ||
|
||
/** | ||
* Represents a baseline for analyze results. | ||
* <p> | ||
* Note that the data structures rely on {@link SortedMap}s to preserve the order of entries. This is required for creating diffs between baseline files which are generated from these structures. | ||
*/ | ||
@Getter | ||
@ToString | ||
public class Baseline { | ||
|
||
/** | ||
* The baseline per {@link com.buschmais.jqassistant.core.rule.api.model.Concept} id. | ||
*/ | ||
private SortedMap<String, RuleBaseline> concepts = new TreeMap<>(); | ||
|
||
/** | ||
* The baseline per {@link com.buschmais.jqassistant.core.rule.api.model.Constraint} id. | ||
*/ | ||
private SortedMap<String, RuleBaseline> constraints = new TreeMap<>(); | ||
|
||
/** | ||
* Represent a baseline for a specific {@link com.buschmais.jqassistant.core.rule.api.model.Concept} or {@link com.buschmais.jqassistant.core.rule.api.model.Constraint}. | ||
*/ | ||
@Getter | ||
@ToString | ||
public static class RuleBaseline { | ||
|
||
/** | ||
* Holds the row key as key and the columns (as human-readable labels) as values. | ||
*/ | ||
private final SortedMap<String, SortedMap<String, String>> rows = new TreeMap<>(); | ||
|
||
} | ||
|
||
} |
80 changes: 80 additions & 0 deletions
80
...s/src/main/java/com/buschmais/jqassistant/core/analysis/api/baseline/BaselineManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package com.buschmais.jqassistant.core.analysis.api.baseline; | ||
|
||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.SortedMap; | ||
import java.util.TreeMap; | ||
|
||
import com.buschmais.jqassistant.core.report.api.model.Column; | ||
import com.buschmais.jqassistant.core.report.api.model.Row; | ||
import com.buschmais.jqassistant.core.rule.api.model.Concept; | ||
import com.buschmais.jqassistant.core.rule.api.model.Constraint; | ||
import com.buschmais.jqassistant.core.rule.api.model.ExecutableRule; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
|
||
/** | ||
* Verifies result rows gathered for rule during an analyze task against a baseline | ||
* <p> | ||
* {@link Row}s of a constraint are considered as "new" if | ||
* <ul> | ||
* <li>No baseline exists.</li> | ||
* <li>A baseline exists but does not yet contain the {@link Row}.</li> | ||
* </ul> | ||
* <p> | ||
* Only {@link Row}s that have been validated using {@link #isNew(ExecutableRule, Row)} (Constraint, Row)} are copied to the new baseline. | ||
* This means that between two executions resolved {@link Constraint} violations are removed from the baseline. | ||
* <p> | ||
*/ | ||
@RequiredArgsConstructor | ||
public class BaselineManager { | ||
|
||
private final Optional<Baseline> optionalOldBaseline; | ||
|
||
private final Baseline newBaseline = new Baseline(); | ||
|
||
public boolean isNew(ExecutableRule<?> executableRule, Row row) { | ||
String ruleId = executableRule.getId(); | ||
String rowKey = row.getKey(); | ||
Map<String, Column<?>> columns = row.getColumns(); | ||
return optionalOldBaseline.map(oldBaseline -> { | ||
SortedMap<String, Baseline.RuleBaseline> ruleBaseline = getRows(oldBaseline, executableRule); | ||
Baseline.RuleBaseline oldRuleBaseline = ruleBaseline.get(ruleId); | ||
if (oldRuleBaseline != null && oldRuleBaseline.getRows() | ||
.containsKey(rowKey)) { | ||
add(ruleId, rowKey, columns); | ||
return false; | ||
} | ||
return true; | ||
}) | ||
.orElseGet(() -> { | ||
add(ruleId, rowKey, columns); | ||
return true; | ||
}); | ||
} | ||
|
||
private static SortedMap<String, Baseline.RuleBaseline> getRows(Baseline baseline, ExecutableRule<?> executableRule) { | ||
if (executableRule instanceof Concept) { | ||
return baseline.getConcepts(); | ||
} else if (executableRule instanceof Constraint) { | ||
return baseline.getConstraints(); | ||
} | ||
throw new IllegalArgumentException("Unsupported executable rule: " + executableRule); | ||
} | ||
|
||
private void add(String constraintId, String rowKey, Map<String, Column<?>> columns) { | ||
Baseline.RuleBaseline newRuleBaseline = newBaseline.getConstraints() | ||
.computeIfAbsent(constraintId, key -> new Baseline.RuleBaseline()); | ||
TreeMap<String, String> row = new TreeMap<>(); | ||
columns.entrySet() | ||
.stream() | ||
.forEach(entry -> row.put(entry.getKey(), entry.getValue() | ||
.getLabel())); | ||
newRuleBaseline.getRows() | ||
.put(rowKey, row); | ||
} | ||
|
||
public Baseline getNewBaseline() { | ||
return newBaseline; | ||
} | ||
} |
135 changes: 135 additions & 0 deletions
135
...c/test/java/com/buschmais/jqassistant/core/analysis/api/baseline/BaselineManagerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
package com.buschmais.jqassistant.core.analysis.api.baseline; | ||
|
||
import java.util.Map; | ||
import java.util.SortedMap; | ||
import java.util.TreeMap; | ||
import java.util.stream.Stream; | ||
|
||
import com.buschmais.jqassistant.core.report.api.model.Column; | ||
import com.buschmais.jqassistant.core.report.api.model.Row; | ||
import com.buschmais.jqassistant.core.rule.api.model.Concept; | ||
import com.buschmais.jqassistant.core.rule.api.model.Constraint; | ||
import com.buschmais.jqassistant.core.rule.api.model.ExecutableRule; | ||
|
||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.Arguments; | ||
import org.junit.jupiter.params.provider.MethodSource; | ||
|
||
import static java.util.Optional.empty; | ||
import static java.util.Optional.of; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
class BaselineManagerTest { | ||
|
||
private static Stream<Arguments> rules() { | ||
return Stream.of(Arguments.of(Concept.builder() | ||
.id("concept") | ||
.build()), Arguments.of(Constraint.builder() | ||
.id("constraint") | ||
.build())); | ||
} | ||
|
||
@ParameterizedTest | ||
@MethodSource("rules") | ||
void noBaselineWithNewConstraintViolation(ExecutableRule<?> executableRule) { | ||
Row row = Row.builder() | ||
.key("1") | ||
.columns(Map.of("c1", Column.builder() | ||
.label("1") | ||
.build())) | ||
.build(); | ||
BaselineManager baselineManager = new BaselineManager(empty()); | ||
|
||
assertThat(baselineManager.isNew(executableRule, row)).isTrue(); | ||
|
||
verifyNewBaseline(executableRule, baselineManager.getNewBaseline() | ||
.getConstraints(), "1"); | ||
} | ||
|
||
@ParameterizedTest | ||
@MethodSource("rules") | ||
void existingBaselineWithExistingConstraintViolation(ExecutableRule<?> executableRule) { | ||
Baseline oldBaseline = createOldBaseline(executableRule, "1"); | ||
Row row = Row.builder() | ||
.key("1") | ||
.columns(Map.of("c1", Column.builder() | ||
.label("1") | ||
.build())) | ||
.build(); | ||
BaselineManager baselineManager = new BaselineManager(of(oldBaseline)); | ||
|
||
assertThat(baselineManager.isNew(executableRule, row)).isFalse(); | ||
|
||
verifyNewBaseline(executableRule, baselineManager.getNewBaseline() | ||
.getConstraints(), "1"); | ||
} | ||
|
||
@ParameterizedTest | ||
@MethodSource("rules") | ||
void existingBaselineWithNewConstraintViolation(ExecutableRule<?> executableRule) { | ||
Baseline oldBaseline = createOldBaseline(executableRule, "1"); | ||
Row oldRow = Row.builder() | ||
.key("1") | ||
.columns(Map.of("c1", Column.builder() | ||
.label("1") | ||
.build())) | ||
.build(); | ||
Row newRow = Row.builder() | ||
.key("2") | ||
.columns(Map.of("c1", Column.builder() | ||
.label("2") | ||
.build())) | ||
.build(); | ||
BaselineManager baselineManager = new BaselineManager(of(oldBaseline)); | ||
|
||
assertThat(baselineManager.isNew(executableRule, oldRow)).isFalse(); | ||
assertThat(baselineManager.isNew(executableRule, newRow)).isTrue(); | ||
|
||
verifyNewBaseline(executableRule, baselineManager.getNewBaseline() | ||
.getConstraints(), "1"); | ||
} | ||
|
||
@ParameterizedTest | ||
@MethodSource("rules") | ||
void existingBaselineWithRemovedConstraintViolation(ExecutableRule<?> executableRule) { | ||
Baseline oldBaseline = createOldBaseline(executableRule, "1", "2"); | ||
Row row = Row.builder() | ||
.key("1") | ||
.columns(Map.of("c1", Column.builder() | ||
.label("1") | ||
.build())) | ||
.build(); | ||
BaselineManager baselineManager = new BaselineManager(of(oldBaseline)); | ||
|
||
assertThat(baselineManager.isNew(executableRule, row)).isFalse(); | ||
|
||
verifyNewBaseline(executableRule, baselineManager.getNewBaseline() | ||
.getConstraints(), "1"); | ||
} | ||
|
||
private static Baseline createOldBaseline(ExecutableRule<?> rule, String... rowKeys) { | ||
Baseline.RuleBaseline oldRuleBaseline = new Baseline.RuleBaseline(); | ||
for (String rowKey : rowKeys) { | ||
TreeMap<String, String> row = new TreeMap<>(); | ||
row.put("c1", rowKey); | ||
oldRuleBaseline.getRows() | ||
.put(rowKey, row); | ||
} | ||
Baseline oldBaseline = new Baseline(); | ||
SortedMap<String, Baseline.RuleBaseline> ruleBaselines = rule instanceof Concept ? oldBaseline.getConcepts() : oldBaseline.getConstraints(); | ||
ruleBaselines.put(rule.getId(), oldRuleBaseline); | ||
return oldBaseline; | ||
} | ||
|
||
private static void verifyNewBaseline(ExecutableRule<?> executableRule, SortedMap<String, Baseline.RuleBaseline> rulebaseLines, String... expectedRowKeys) { | ||
assertThat(rulebaseLines).hasSize(1) | ||
.containsKey(executableRule.getId()); | ||
Baseline.RuleBaseline ruleBaseline = rulebaseLines.get(executableRule.getId()); | ||
assertThat(ruleBaseline).isNotNull(); | ||
SortedMap<String, SortedMap<String, String>> rows = ruleBaseline.getRows(); | ||
assertThat(rows).hasSize(expectedRowKeys.length); | ||
for (String expectedRowKey : expectedRowKeys) { | ||
assertThat(rows.get(expectedRowKey)).containsExactly(Map.entry("c1", expectedRowKey)); | ||
} | ||
} | ||
} |