Skip to content
This repository has been archived by the owner on Jul 12, 2024. It is now read-only.

Commit

Permalink
added initial implementation for BaselineManager
Browse files Browse the repository at this point in the history
  • Loading branch information
DirkMahler committed Jul 4, 2024
1 parent 85f1181 commit 6d93442
Show file tree
Hide file tree
Showing 3 changed files with 257 additions and 0 deletions.
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<>();

}

}
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;
}
}
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));
}
}
}

0 comments on commit 6d93442

Please sign in to comment.