/*
* Copyright 2015-2016 OpenCB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.opencb.opencga.storage.core.variant.adaptors;
import org.hamcrest.*;
import org.hamcrest.core.Every;
import org.opencb.biodata.models.core.Region;
import org.opencb.biodata.models.variant.StudyEntry;
import org.opencb.biodata.models.variant.Variant;
import org.opencb.biodata.models.variant.avro.*;
import org.opencb.biodata.models.variant.stats.VariantStats;
import org.opencb.commons.datastore.core.QueryResult;
import java.util.*;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static org.hamcrest.CoreMatchers.*;
/**
* Created on 05/07/16
*
* @author Jacobo Coll <jacobo167@gmail.com>
*/
public class VariantMatchers {
public static <T> FeatureMatcher<QueryResult<T>, List<T>> hasResult(Matcher<? super List<T>> subMatcher) {
return new FeatureMatcher<QueryResult<T>, List<T>>(subMatcher, "a query result where", "QueryResult") {
@Override
protected List<T> featureValueOf(QueryResult<T> actual) {
return actual.getResult();
}
};
}
public static <T> Matcher<QueryResult<T>> everyResult(QueryResult<T> allValues, Matcher<T> subMatcher) {
return everyResult(allValues.getResult(), subMatcher);
}
public static <T> Matcher<QueryResult<T>> everyResult(List<T> allValues, Matcher<T> subMatcher) {
long count = count(allValues, subMatcher);
Set<T> expectValues = filter(allValues, subMatcher);
return allOf(numResults(expectValues), everyResult(subMatcher));
}
public static <T> Matcher<QueryResult<T>> everyResult(Matcher<T> subMatcher) {
return hasResult(Every.everyItem(subMatcher));
}
public static Matcher<QueryResult<?>> numTotalResults(Matcher<Long> subMatcher) {
return new FeatureMatcher<QueryResult<?>, Long>(subMatcher, "a queryResult with numTotalResults", "NumTotalResults") {
@Override
protected Long featureValueOf(QueryResult<?> actual) {
return actual.getNumTotalResults();
}
};
}
public static Matcher<QueryResult<?>> numResults(Matcher<Integer> subMatcher) {
return new FeatureMatcher<QueryResult<?>, Integer>(subMatcher, "a queryResult with numResults", "NumResults") {
@Override
protected Integer featureValueOf(QueryResult<?> actual) {
return actual.getNumResults();
}
};
}
public static <T> Matcher<QueryResult<T>> numResults(Set<T> expectedValues) {
return new TypeSafeDiagnosingMatcher<QueryResult<T>>() {
@Override
public void describeTo(Description description) {
description.appendText(" a query result with " + expectedValues.size() + " values");
}
@Override
protected boolean matchesSafely(QueryResult<T> item, Description mismatchDescription) {
List<T> missingValues = new ArrayList<T>();
List<T> extraValues = new ArrayList<T>();
for (T t : item.getResult()) {
if (!expectedValues.contains(t)) {
extraValues.add(t);
}
}
if (extraValues.isEmpty() && item.getNumResults() == expectedValues.size()) {
// Same size and all no extra values? matches!
return true;
}
for (T expectedValue : expectedValues) {
if (!item.getResult().contains(expectedValue)) {
missingValues.add(expectedValue);
}
}
mismatchDescription.appendText(" has " + item.getNumResults() + " values "
+ '(' + missingValues.size() + " missing, " + extraValues.size() + " extra)");
if (!missingValues.isEmpty()) {
mismatchDescription.appendValueList(" , missing values [", ", ", "] ", missingValues);
}
if (!extraValues.isEmpty()) {
mismatchDescription.appendValueList(" , extra values [", ", ", "] ", extraValues);
}
return false;
}
};
}
public static Matcher<Variant> overlaps(Region region) {
return overlaps(region, true);
}
public static Matcher<Variant> overlaps(final Region region, final boolean inclusive) {
return new TypeSafeDiagnosingMatcher<Variant>() {
@Override
protected boolean matchesSafely(Variant item, Description mismatchDescription) {
return item.overlapWith(region.getChromosome(), region.getStart(), region.getEnd(), inclusive);
}
@Override
public void describeTo(Description description) {
description.appendText("overlaps with region " + region + (inclusive? " inclusively" : " non inclusively"));
}
};
}
public static Matcher<Variant> hasAnnotation(Matcher<? super VariantAnnotation> subMatcher) {
return new FeatureMatcher<Variant, VariantAnnotation>(subMatcher, "with variant annotation", "VariantAnnotation") {
@Override
protected VariantAnnotation featureValueOf(Variant actual) {
return actual.getAnnotation();
}
};
}
public static Matcher<VariantAnnotation> at(final String variant) {
Variant v = new Variant(variant);
return allOf(with("chromosome", VariantAnnotation::getChromosome, is(v.getChromosome())),
with("position", VariantAnnotation::getStart, is(v.getStart())),
with("reference", VariantAnnotation::getReference, is(v.getReference())),
with("alternate", VariantAnnotation::getAlternate, is(v.getAlternate()))
);
}
public static Matcher<VariantAnnotation> hasGenes(Matcher<? super Collection<String>> subMatcher) {
return new FeatureMatcher<VariantAnnotation, Collection<String>>(subMatcher, "with genes", "annotation") {
@Override
protected Collection<String> featureValueOf(VariantAnnotation actual) {
return actual.getConsequenceTypes().stream().map(ConsequenceType::getGeneName).collect(Collectors.toList());
}
};
}
public static Matcher<VariantAnnotation> hasGenes(Collection<String> genes) {
return hasGenes(hasItems(genes.toArray(new String[genes.size()])));
}
public static Matcher<VariantAnnotation> hasAnyGeneOf(Collection<String> genes) {
LinkedList<Matcher<? super Collection<String>>> any = new LinkedList<>();
for (String gene : genes) {
any.add(hasItem(gene));
}
return hasGenes(anyOf(any));
}
public static Matcher<VariantAnnotation> hasSO(Matcher<? super Collection<String>> subMatcher) {
return new FeatureMatcher<VariantAnnotation, Collection<String>>(subMatcher, "with Sequence Ontology Terms", "SOTerms") {
@Override
protected Collection<String> featureValueOf(VariantAnnotation actual) {
return actual.getConsequenceTypes()
.stream()
.map(ConsequenceType::getSequenceOntologyTerms)
.flatMap(Collection::stream)
.map(SequenceOntologyTerm::getAccession)
.collect(Collectors.toSet());
}
};
}
public static Matcher<VariantAnnotation> hasPopAltFreq(String study, String population, Matcher<? super Float> subMatcher) {
return new FeatureMatcher<VariantAnnotation, Float>(subMatcher, "with Population alternate allele Frequency (" + study + ", " + population + ")", "PopulationAltFreq") {
@Override
protected Float featureValueOf(VariantAnnotation actual) {
if (actual.getPopulationFrequencies() != null) {
for (PopulationFrequency populationFrequency : actual.getPopulationFrequencies()) {
if (populationFrequency.getStudy().equalsIgnoreCase(study)
&& populationFrequency.getPopulation().equalsIgnoreCase(population)) {
return populationFrequency.getAltAlleleFreq();
}
}
}
return 0F;
}
};
}
public static Matcher<VariantAnnotation> hasPopRefFreq(String study, String population, Matcher<? super Float> subMatcher) {
return new FeatureMatcher<VariantAnnotation, Float>(subMatcher, "with Population reference allele Frequency (" + study + ", " + population + ")", "PopulationRefFreq") {
@Override
protected Float featureValueOf(VariantAnnotation actual) {
if (actual.getPopulationFrequencies() != null) {
for (PopulationFrequency populationFrequency : actual.getPopulationFrequencies()) {
if (populationFrequency.getStudy().equalsIgnoreCase(study)
&& populationFrequency.getPopulation().equalsIgnoreCase(population)) {
return populationFrequency.getRefAlleleFreq();
}
}
}
return 0F;
}
};
}
public static Matcher<VariantAnnotation> hasSift(Matcher<? super Iterable<Double>> subMatcher) {
return hasProteinSubstitutionScore("sift", subMatcher);
}
public static Matcher<VariantAnnotation> hasAnySift(Matcher<? super Double> subMatcher) {
return hasSift(CoreMatchers.<Double>hasItem(subMatcher));
}
public static Matcher<VariantAnnotation> hasAnySiftDesc(Matcher<? super String> subMatcher) {
return hasProteinSubstitutionScoreDesc("sift", CoreMatchers.<String>hasItem(subMatcher));
}
public static Matcher<VariantAnnotation> hasPolyphen(Matcher<? super Iterable<Double>> subMatcher) {
return hasProteinSubstitutionScore("polyphen", subMatcher);
}
public static Matcher<VariantAnnotation> hasAnyPolyphen(Matcher<? super Double> subMatcher) {
return hasPolyphen(CoreMatchers.<Double>hasItem(subMatcher));
}
public static Matcher<VariantAnnotation> hasAnyPolyphenDesc(Matcher<? super String> subMatcher) {
return hasProteinSubstitutionScoreDesc("polyphen", CoreMatchers.<String>hasItem(subMatcher));
}
public static Matcher<VariantAnnotation> hasProteinSubstitutionScore(String source, Matcher<? super Iterable<Double>> subMatcher) {
return hasProteinSubstitutionScore(source, subMatcher, Score::getScore);
}
public static Matcher<VariantAnnotation> hasProteinSubstitutionScoreDesc(String source, Matcher<? super Iterable<String>> subMatcher) {
return hasProteinSubstitutionScore(source, subMatcher, Score::getDescription);
}
private static <T> Matcher<VariantAnnotation> hasProteinSubstitutionScore(String source, Matcher<? super Iterable<T>> subMatcher, Function<Score, T> mapper) {
return new FeatureMatcher<VariantAnnotation, Iterable<T>>(subMatcher, "with all protein substitution " + source, source) {
@Override
protected Iterable<T> featureValueOf(VariantAnnotation actual) {
if (actual.getConsequenceTypes() != null) {
Set<T> set = new HashSet<>();
for (ConsequenceType ct : actual.getConsequenceTypes()) {
if (ct != null && ct.getProteinVariantAnnotation() != null
&& ct.getProteinVariantAnnotation().getSubstitutionScores() != null) {
for (Score score : ct.getProteinVariantAnnotation().getSubstitutionScores()) {
if (score != null && source.equals(score.getSource())) {
set.add(mapper.apply(score));
}
}
}
}
return set;
}
return Collections.emptyList();
}
};
}
public static Matcher<Variant> firstStudy(Matcher<? super StudyEntry> subMatcher) {
return new FeatureMatcher<Variant, StudyEntry>(subMatcher, "with first study", "Study") {
@Override
protected StudyEntry featureValueOf(Variant actual) {
return actual.getStudies().get(0);
}
};
}
public static Matcher<Variant> withStudy(final String study) {
return withStudy(study, notNullValue());
}
public static Matcher<Variant> withStudy(final String study, Matcher<? super StudyEntry> subMatcher) {
return new FeatureMatcher<Variant, StudyEntry>(subMatcher, "with study " + study, "Study") {
@Override
protected StudyEntry featureValueOf(Variant actual) {
return actual.getStudy(study);
}
};
}
public static Matcher<? super StudyEntry> withSamples(Set<String> samples) {
return new FeatureMatcher<StudyEntry, Set<String>>(is(equalTo(samples)), "with samples " + samples, "Samples") {
@Override
protected Set<String> featureValueOf(StudyEntry actual) {
return actual.getSamplesName();
}
};
}
public static Matcher<? super StudyEntry> withSamples(List<String> samples) {
return new FeatureMatcher<StudyEntry, List<String>>(is(equalTo(samples)), "with samples " + samples, "Samples") {
@Override
protected List<String> featureValueOf(StudyEntry actual) {
return actual.getOrderedSamplesName();
}
};
}
public static Matcher<? super StudyEntry> withSampleData(String sampleName, String formatField, Matcher<String> subMatcher) {
return new FeatureMatcher<StudyEntry, String>(subMatcher, "with sample " + sampleName + " with " + formatField, "SampleData") {
@Override
protected String featureValueOf(StudyEntry actual) {
return actual.getSampleData(sampleName, formatField);
}
};
}
public static Matcher<StudyEntry> withFileId(Matcher<? super Iterable<? super String>> subMatcher) {
return new FeatureMatcher<StudyEntry, List<String>>(subMatcher, "with fileIds ", "FileIds") {
@Override
protected List<String> featureValueOf(StudyEntry actual) {
return actual.getFiles()
.stream()
.map(FileEntry::getFileId).collect(Collectors.toList());
}
};
}
public static Matcher<StudyEntry> withStats(final String cohortName, Matcher<? super VariantStats> subMatcher) {
return new FeatureMatcher<StudyEntry, VariantStats>(subMatcher, "with stats " + cohortName, "Stats") {
@Override
protected VariantStats featureValueOf(StudyEntry actual) {
return actual.getStats(cohortName);
}
};
}
public static Matcher<VariantStats> withMaf(Matcher<? super Float> subMatcher) {
return new FeatureMatcher<VariantStats, Float>(subMatcher, "with maf", "MAF") {
@Override
protected Float featureValueOf(VariantStats actual) {
return actual.getMaf();
}
};
}
public static Matcher<VariantStats> withMgf(Matcher<? super Float> subMatcher) {
return new FeatureMatcher<VariantStats, Float>(subMatcher, "with mgf", "MGF") {
@Override
protected Float featureValueOf(VariantStats actual) {
return actual.getMgf();
}
};
}
public static <T, R> Matcher<T> with(String name, Function<T, R> f, Matcher<? super R> subMatcher) {
return new FeatureMatcher<T, R>(subMatcher, "with " + name, name) {
@Override
protected R featureValueOf(T actual) {
return f.apply(actual);
}
};
}
public static <T, R> Matcher<T> withAny(String name, Function<T, Iterable<? super R>> f, Matcher<? super R> subMatcher) {
Matcher<Iterable<? super R>> iterableMatcher = hasItem(subMatcher);
return new FeatureMatcher<T, Iterable<? super R>>(iterableMatcher, "with " + name, name) {
@Override
protected Iterable<? super R> featureValueOf(T actual) {
return f.apply(actual);
}
};
}
public static <T> Matcher<T> matcher(Predicate<T> predicate, final String describe) {
return matcher((t, description) -> {
if (predicate.test(t)) {
return true;
} else {
description.appendText("is " + t);
return false;
}
}, describe);
}
public static <T> Matcher<T> matcher(BiPredicate<T, Description> predicate, final String describe) {
Objects.requireNonNull(predicate);
return new TypeSafeDiagnosingMatcher<T>() {
@Override
protected boolean matchesSafely(T item, Description mismatchDescription) {
return predicate.test(item, mismatchDescription);
}
@Override
public void describeTo(Description description) {
description.appendText(describe);
}
};
}
public static <T extends Number> Matcher<T> gt(T n) {
return new TypeSafeDiagnosingMatcher<T>() {
@Override
protected boolean matchesSafely(T item, Description mismatchDescription) {
if (item.doubleValue() > n.doubleValue()) {
return true;
} else {
mismatchDescription.appendText("is " + item);
return false;
}
}
@Override
public void describeTo(Description description) {
description.appendText("> " + n);
}
};
}
public static <T extends Number> Matcher<T> gte(T n) {
return new TypeSafeDiagnosingMatcher<T>() {
@Override
protected boolean matchesSafely(T item, Description mismatchDescription) {
if (item.doubleValue() >= n.doubleValue()) {
return true;
} else {
mismatchDescription.appendText("is " + item);
return false;
}
}
@Override
public void describeTo(Description description) {
description.appendText(">= " + n);
}
};
}
public static <T extends Number> Matcher<T> lt(T n) {
return new TypeSafeDiagnosingMatcher<T>() {
@Override
protected boolean matchesSafely(T item, Description mismatchDescription) {
if (item.doubleValue() < n.doubleValue()) {
return true;
} else {
mismatchDescription.appendText(" is " + item);
return false;
}
}
@Override
public void describeTo(Description description) {
description.appendText("< " + n);
}
};
}
public static <T extends Number> Matcher<T> lte(T n) {
return new TypeSafeDiagnosingMatcher<T>() {
@Override
protected boolean matchesSafely(T item, Description mismatchDescription) {
if (item.doubleValue() <= n.doubleValue()) {
return true;
} else {
mismatchDescription.appendText(" is " + item);
return false;
}
}
@Override
public void describeTo(Description description) {
description.appendText("<= " + n);
}
};
}
public static <T> long count(List<T> objects, Matcher<T> matcher) {
long c = 0;
for (T t: objects) {
if (matcher.matches(t)) {
c++;
}
}
return c;
}
public static <T> Set<T> filter(List<T> objects, Matcher<T> matcher) {
Set<T> l = new HashSet<>();
for (T t: objects) {
if (matcher.matches(t)) {
l.add(t);
}
}
return l;
}
// private static class VariantVariantAnnotationFeatureMatcher extends FeatureMatcher<Variant, VariantAnnotation> {
// /**
// * Constructor
// *
// * @param subMatcher The matcher to apply to the feature
// */
// public VariantVariantAnnotationFeatureMatcher(Matcher<? super VariantAnnotation> subMatcher) {
// super(subMatcher, "variant annotation", "annotation");
// }
//
// @Override
// protected VariantAnnotation featureValueOf(Variant actual) {
// return actual.getAnnotation();
// }
//
// public static VariantVariantAnnotationFeatureMatcher hasAnnotation(Matcher<? super VariantAnnotation> subMatcher) {
// return new VariantVariantAnnotationFeatureMatcher(subMatcher);
// }
// }
}