blob: f217cb7fa045b288deed836b60e9bb79de4c4734 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2019 EclipseSource Muenchen GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Christian W. Damus - initial API and implementation
******************************************************************************/
package org.eclipse.emf.ecp.view.model.test;
import static java.util.Collections.emptyList;
import static org.hamcrest.CoreMatchers.both;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecp.view.spi.model.VDiagnostic;
import org.eclipse.emf.ecp.view.spi.model.VViewFactory;
import org.eclipse.emf.ecp.view.spi.model.impl.VDiagnosticImpl;
import org.eclipse.emf.emfstore.bowling.BowlingFactory;
import org.eclipse.emf.emfstore.bowling.BowlingPackage;
import org.eclipse.emf.emfstore.bowling.Gender;
import org.eclipse.emf.emfstore.bowling.League;
import org.eclipse.emf.emfstore.bowling.Player;
import org.hamcrest.CoreMatchers;
import org.hamcrest.Description;
import org.hamcrest.FeatureMatcher;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Before;
import org.junit.Test;
/**
* Test cases for the custom code in the {@link VDiagnosticImpl} class.
*/
public class VDiagnostic_Test {
private static final EStructuralFeature NAME = BowlingPackage.Literals.PLAYER__NAME;
private static final EStructuralFeature DOB = BowlingPackage.Literals.PLAYER__DATE_OF_BIRTH;
private static final EStructuralFeature PLAYERS = BowlingPackage.Literals.LEAGUE__PLAYERS;
private final VDiagnostic fixture = VViewFactory.eINSTANCE.createDiagnostic();
private League league;
private Player alice;
private Player brenda;
private Player cathy;
/**
* Test uniqueness of the diagnostic list.
*/
@Test
public void getDiagnostics() {
final List<Object> diagnostics = fixture.getDiagnostics();
assertThat(diagnostics.size(), is(0));
diagnostics.add(warning(alice, NAME, "first"));
diagnostics.add(ok(brenda, NAME, "brenda"));
assertThat(diagnostics.size(), is(2));
assertThat(diagnostics.add(warning(cathy, NAME, "second")), is(true));
assertThat(diagnostics.add(warning(cathy, NAME, "second")), is(false));
diagnostics.addAll(Arrays.asList(
warning(alice, NAME, "first"), // dupe
warning(alice, NAME, "second"), // new
ok(brenda, NAME, "brenda"), // dupe
error(brenda, NAME, "brenda"), // new
warning(cathy, DOB, "second") // new
));
assertThat(diagnostics.size(), is(6));
assertThat(diagnostics, hasItems(
warning(alice, NAME, "first"),
ok(brenda, NAME, "brenda"),
warning(cathy, NAME, "second"),
warning(alice, NAME, "second"),
error(brenda, NAME, "brenda"),
warning(cathy, DOB, "second")));
}
/**
* Test severity query.
*
* @see VDiagnostic#getHighestSeverity()
*/
@Test
public void getHighestSeverity() {
final List<Object> diagnostics = fixture.getDiagnostics();
assertThat(fixture.getHighestSeverity(), is(Diagnostic.OK));
diagnostics.add(ok(brenda, NAME, "first"));
assertThat(fixture.getHighestSeverity(), is(Diagnostic.OK));
diagnostics.add(warning(alice, NAME, "first"));
assertThat(fixture.getHighestSeverity(), is(Diagnostic.WARNING));
diagnostics.add(error(cathy, NAME, "first"));
assertThat(fixture.getHighestSeverity(), is(Diagnostic.ERROR));
diagnostics.remove(2);
assertThat(fixture.getHighestSeverity(), is(Diagnostic.WARNING));
}
/**
* Test message query.
*
* @see VDiagnostic#getMessage()
*/
@Test
public void getMessage() {
final List<Object> diagnostics = fixture.getDiagnostics();
assertThat(fixture.getMessage(), is(""));
diagnostics.add(ok(brenda, NAME, "first"));
assertThat(fixture.getMessage(), is(""));
diagnostics.add(warning(alice, NAME, "second"));
assertThat(fixture.getMessage(), is("second"));
diagnostics.add(error(cathy, NAME, "third"));
assertThat(fixture.getMessage(), is("third\nsecond")); // Severity order
}
/**
* Test mappings of diagnostics {@linkplain VDiagnostic#getDiagnostics(EObject) by object}.
*
* @see VDiagnostic#getDiagnostics(EObject)
*/
@Test
public void getDiagnostics_EObject() {
final List<Object> diagnostics = fixture.getDiagnostics();
assertThat(fixture.getDiagnostics(alice), is(emptyList()));
assertThat(fixture.getDiagnostics(league), is(emptyList()));
// OKs are filtered out
diagnostics.add(ok(alice, NAME, "first"));
assertThat(fixture.getDiagnostics(alice), is(emptyList()));
assertThat(fixture.getDiagnostics(league), is(emptyList()));
diagnostics.add(warning(alice, DOB, "second"));
diagnostics.add(error(alice, NAME, "third"));
assertThat(fixture.getDiagnostics(alice), hasInOrder(
error(alice, NAME, "third"), warning(alice, DOB, "second")));
assertThat(fixture.getDiagnostics(league), hasInOrder(
error(alice, NAME, "third"), warning(alice, DOB, "second")));
diagnostics.add(warning(cathy, DOB, "third"));
assertThat(fixture.getDiagnostics(alice), hasInOrder(
error(alice, NAME, "third"), warning(alice, DOB, "second")));
assertThat(fixture.getDiagnostics(alice), not(hasItem(isAbout(cathy))));
assertThat(fixture.getDiagnostics(cathy), hasItem(warning(cathy, DOB, "third")));
assertThat(fixture.getDiagnostics(cathy), not(hasItem(isAbout(alice))));
assertThat(fixture.getDiagnostics(league), hasInOrder(
// Warnings are alphabetical by message
error(alice, NAME, "third"), warning(alice, DOB, "second"), warning(cathy, DOB, "third")));
diagnostics.add(error(league, PLAYERS, "too few"));
assertThat(fixture.getDiagnostics(league), hasItem(isAbout(league)));
assertThat(fixture.getDiagnostics(alice), not(hasItem(isAbout(league))));
assertThat(fixture.getDiagnostics(cathy), not(hasItem(isAbout(league))));
// Now remove the error(alice, NAME, "third")
diagnostics.remove(2);
assertThat(fixture.getDiagnostics(alice), not(hasItem(error(alice, NAME, "third"))));
assertThat(fixture.getDiagnostics(league), not(hasItem(error(alice, NAME, "third"))));
// And replace warning(alice, DOB, "second")
diagnostics.set(1, error(brenda, NAME, "new"));
assertThat(fixture.getDiagnostics(alice), not(hasItem(warning(alice, DOB, "second"))));
assertThat(fixture.getDiagnostics(league), both(hasItem(error(brenda, NAME, "new"))).and(
not(hasItem(warning(alice, DOB, "second")))));
}
/**
* Test mappings of diagnostics {@linkplain VDiagnostic#getDiagnostics(EObject) by object and feature}.
*
* @see VDiagnostic#getDiagnostic(EObject, EStructuralFeature)
*/
@Test
public void getDiagnostic_EObject_EStructuralFeature() {
final List<Object> diagnostics = fixture.getDiagnostics();
assertThat(fixture.getDiagnostic(cathy, NAME), is(emptyList()));
assertThat(fixture.getDiagnostic(league, NAME), is(emptyList()));
// OKs are filtered out
diagnostics.add(ok(cathy, NAME, "first"));
assertThat(fixture.getDiagnostic(cathy, NAME), is(emptyList()));
assertThat(fixture.getDiagnostic(league, NAME), is(emptyList()));
diagnostics.add(warning(cathy, NAME, "second"));
diagnostics.add(error(cathy, DOB, "third"));
assertThat(fixture.getDiagnostic(cathy, NAME), hasItem(
warning(cathy, NAME, "second")));
assertThat(fixture.getDiagnostic(cathy, NAME), not(hasItem(hasFeature(DOB))));
assertThat(fixture.getDiagnostic(league, NAME), hasItem(
warning(cathy, NAME, "second")));
assertThat(fixture.getDiagnostic(league, NAME), not(hasItem(hasFeature(DOB))));
diagnostics.add(error(cathy, NAME, "third"));
assertThat(fixture.getDiagnostic(cathy, NAME), hasInOrder(
error(cathy, NAME, "third"), warning(cathy, NAME, "second")));
}
//
// Test framework
//
@Before
public void createModel() {
league = BowlingFactory.eINSTANCE.createLeague();
league.setName("Bantam A");
alice = player("Alice");
brenda = player("Brenda");
cathy = player("Cathy");
}
private Player player(String name) {
final Player result = BowlingFactory.eINSTANCE.createPlayer();
result.setName(name);
result.setGender(Gender.FEMALE);
result.setDateOfBirth(
java.util.Date.from(LocalDate.now().minusYears(23).atStartOfDay(ZoneId.systemDefault()).toInstant()));
league.getPlayers().add(result);
return result;
}
Diagnostic error(EObject subject, EStructuralFeature feature, String message) {
return problem(Diagnostic.ERROR, subject, feature, message);
}
Diagnostic warning(EObject subject, EStructuralFeature feature, String message) {
return problem(Diagnostic.WARNING, subject, feature, message);
}
Diagnostic ok(EObject subject, EStructuralFeature feature, String message) {
return problem(Diagnostic.OK, subject, feature, message);
}
Diagnostic problem(int severity, EObject subject, EStructuralFeature feature, String message) {
return new BasicDiagnostic(severity, "source", 0, message, new Object[] { subject, feature }) {
// Implement equals() and hashcode() for convenience of testing uniqueness constraints
@Override
public int hashCode() {
return Objects.hash(getSeverity(), getSource(), getMessage(), getData());
}
@Override
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != getClass()) {
return false;
}
final Diagnostic other = (Diagnostic) obj;
return getSeverity() == other.getSeverity()
&& Objects.equals(getSource(), other.getSource())
&& Objects.equals(getMessage(), other.getMessage())
&& Objects.equals(getData(), other.getData());
}
};
}
static Matcher<Diagnostic> isAbout(Matcher<? super EObject> subjectMatcher) {
return new FeatureMatcher<Diagnostic, EObject>(subjectMatcher, "data[0] as EObject", "subject") {
@Override
protected EObject featureValueOf(Diagnostic actual) {
final List<?> data = actual.getData();
return data.isEmpty() || !(data.get(0) instanceof EObject)
? null
: (EObject) data.get(0);
}
};
}
static Matcher<Diagnostic> isAbout(EObject subject) {
return isAbout(is(subject));
}
static Matcher<Diagnostic> hasFeature(Matcher<? super EStructuralFeature> featureMatcher) {
return new FeatureMatcher<Diagnostic, EStructuralFeature>(featureMatcher, "data[1] as EStructuralFeature",
"feature") {
@Override
protected EStructuralFeature featureValueOf(Diagnostic actual) {
final List<?> data = actual.getData();
return data.size() < 2 || !(data.get(1) instanceof EStructuralFeature)
? null
: (EStructuralFeature) data.get(1);
}
};
}
static Matcher<Diagnostic> hasFeature(EStructuralFeature feature) {
return hasFeature(is(feature));
}
@SafeVarargs
static <T> Matcher<Iterable<T>> hasInOrder(Matcher<? super T>... matchers) {
return new InOrderMatcher<>(matchers);
}
private static final class InOrderMatcher<T> extends TypeSafeMatcher<Iterable<T>> {
private final List<Matcher<? super T>> matchers;
@SafeVarargs
InOrderMatcher(Matcher<? super T>... matchers) {
super();
this.matchers = Arrays.asList(matchers);
}
@Override
public void describeTo(Description description) {
description.appendText("matches in order ");
matchers.forEach(description::appendDescriptionOf);
}
@Override
protected boolean matchesSafely(Iterable<T> item) {
boolean result = true;
final Iterator<T> iter = item.iterator();
final Iterator<Matcher<? super T>> mIter = matchers.iterator();
Matcher<? super T> m = null;
while (iter.hasNext()) {
if (m == null) {
if (!mIter.hasNext()) {
break; // All matched
}
m = mIter.next();
}
final T next = iter.next();
if (m.matches(next)) {
// Match the next
m = null;
}
}
if (mIter.hasNext()) {
// Not all matched
result = false;
}
return result;
}
}
@SuppressWarnings("unchecked")
static <T> Matcher<Iterable<T>> hasInOrder(T... items) {
return hasInOrder(Stream.of(items).map(CoreMatchers::is).toArray(Matcher[]::new));
}
}