/* * Grapht, an open source dependency injector. * Copyright 2014-2015 various contributors (see CONTRIBUTORS.txt) * Copyright 2010-2014 Regents of the University of Minnesota * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.grouplens.grapht.context; import org.grouplens.grapht.reflect.MockInjectionPoint; import org.grouplens.grapht.solver.DependencySolver; import org.grouplens.grapht.solver.InjectionContext; import org.grouplens.grapht.reflect.Desire; import org.grouplens.grapht.reflect.Desires; import org.grouplens.grapht.reflect.MockSatisfaction; import org.junit.Test; import java.util.ArrayList; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; public class ContextPatternTest { @Test public void testSingletonChainEmptyContextSuccess() throws Exception { // Test that a singleton pattern matches an empty context assertThat(ContextPattern.empty().append(ContextElements.matchAny()).matches(makeContext()), notNullValue()); } @Test public void testAnyChainEmptyContextSuccess() throws Exception { // Test that the 'any' pattern matches an empty context assertThat(ContextPattern.any().matches(makeContext()), notNullValue()); } @Test public void testEmptyChainNonEmptyContextFailure() throws Exception { assertThat(ContextPattern.empty().matches(makeContext(A.class)), nullValue()); } @Test public void testSingleton() { // test a single type pattern // should match itself ContextPattern initial = ContextPattern.empty().append(ContextElements.matchAny()); assertThat(initial.append(A.class).matches(makeContext(A.class)), notNullValue()); // should not match other type assertThat(initial.append(A.class).matches(makeContext(B.class)), nullValue()); // should not match empty assertThat(initial.append(A.class).matches(makeContext()), nullValue()); // should not match too long assertThat(initial.append(A.class).matches(makeContext(A.class, B.class)), nullValue()); // either way assertThat(initial.append(A.class).matches(makeContext(B.class, A.class)), nullValue()); // either way assertThat(initial.append(A.class).matches(makeContext(B.class, A.class)), nullValue()); // or duplicated assertThat(initial.append(A.class).matches(makeContext(A.class, A.class)), nullValue()); } @Test public void testAnyChainNonEmptyContextSuccess() throws Exception { assertThat(ContextPattern.any().matches(makeContext(A.class)), notNullValue()); assertThat(ContextPattern.any().matches(makeContext(A.class, B.class)), notNullValue()); assertThat(ContextPattern.any().matches(makeContext(A.class, Ap.class)), notNullValue()); assertThat(ContextPattern.any().matches(makeContext(A.class, B.class, C.class)), notNullValue()); } @Test public void testSubsequenceEqualChainContextSuccess() throws Exception { // Test that a subsequence pattern matches assertThat(ContextPattern.subsequence(A.class) .matches(makeContext(A.class)), notNullValue()); assertThat(ContextPattern.subsequence(A.class, B.class) .matches(makeContext(A.class, B.class)), notNullValue()); assertThat(ContextPattern.subsequence(A.class, B.class, C.class) .matches(makeContext(A.class, B.class, C.class)), notNullValue()); } @Test public void testSubstringChainSuccess() throws Exception { assertThat(ContextPattern.subsequence(A.class) .matches(makeContext(A.class, B.class, C.class)), notNullValue()); assertThat(ContextPattern.subsequence(B.class) .matches(makeContext(A.class, B.class, C.class)), notNullValue()); assertThat(ContextPattern.subsequence(C.class) .matches(makeContext(A.class, B.class, C.class)), notNullValue()); assertThat(ContextPattern.subsequence(A.class, B.class) .matches(makeContext(A.class, B.class, C.class)), notNullValue()); assertThat(ContextPattern.subsequence(B.class, C.class) .matches(makeContext(A.class, B.class, C.class)), notNullValue()); assertThat(ContextPattern.subsequence(B.class, C.class) .matches(makeContext(A.class, B.class, C.class, Ap.class)), notNullValue()); } @Test public void testSubsequenceChainSuccess() throws Exception { assertThat(ContextPattern.subsequence(A.class, C.class) .matches(makeContext(A.class, B.class, C.class, Ap.class, Bp.class, Cp.class)), notNullValue()); assertThat(ContextPattern.subsequence(A.class, Ap.class) .matches(makeContext(A.class, B.class, C.class, Ap.class, Bp.class, Cp.class)), notNullValue()); assertThat(ContextPattern.subsequence(A.class, Bp.class) .matches(makeContext(A.class, B.class, C.class, Ap.class, Bp.class, Cp.class)), notNullValue()); assertThat(ContextPattern.subsequence(A.class, C.class, Bp.class) .matches(makeContext(A.class, B.class, C.class, Ap.class, Bp.class, Cp.class)), notNullValue()); assertThat(ContextPattern.subsequence(A.class, B.class, Ap.class) .matches(makeContext(A.class, B.class, C.class, Ap.class, Bp.class, Cp.class)), notNullValue()); assertThat(ContextPattern.subsequence(B.class, Cp.class) .matches(makeContext(A.class, B.class, C.class, Ap.class, Bp.class, Cp.class)), notNullValue()); assertThat(ContextPattern.subsequence(A.class, Cp.class) .matches(makeContext(A.class, B.class, C.class, Ap.class, Bp.class, Cp.class)), notNullValue()); assertThat(ContextPattern.subsequence(C.class, Ap.class, Bp.class) .matches(makeContext(A.class, B.class, C.class, Ap.class, Bp.class, Cp.class)), notNullValue()); } @Test public void testMatcherInheritenceSuccess() throws Exception { assertThat(ContextPattern.subsequence(A.class).matches(makeContext(Ap.class)), notNullValue()); assertThat(ContextPattern.subsequence(A.class, C.class).matches(makeContext(Ap.class, Cp.class)), notNullValue()); assertThat(ContextPattern.subsequence(A.class, C.class).matches(makeContext(A.class, Cp.class)), notNullValue()); assertThat(ContextPattern.subsequence(A.class, C.class).matches(makeContext(Ap.class, C.class)), notNullValue()); } @Test public void testNonSubsequenceFail() throws Exception { assertThat(ContextPattern.subsequence(A.class).matches(makeContext(B.class)), nullValue()); assertThat(ContextPattern.subsequence(A.class, B.class).matches(makeContext(B.class, A.class)), nullValue()); assertThat(ContextPattern.subsequence(B.class, A.class, C.class).matches(makeContext(C.class, B.class, A.class)), nullValue()); assertThat(ContextPattern.subsequence(A.class, B.class, C.class).matches(makeContext(C.class, B.class, A.class)), nullValue()); } @Test public void testSuperstringFail() throws Exception { assertThat(ContextPattern.subsequence(A.class, B.class).matches(makeContext(A.class)), nullValue()); assertThat(ContextPattern.subsequence(A.class, B.class, C.class).matches(makeContext(A.class, C.class)), nullValue()); assertThat(ContextPattern.subsequence(A.class, B.class, C.class).matches(makeContext(A.class, B.class)), nullValue()); } @Test public void testTailAnchoredMatch() { ContextMatcher matcher = ContextPattern.any().append(A.class); assertThat(matcher.matches(makeContext()), nullValue()); assertThat(matcher.matches(makeContext(A.class)), notNullValue()); assertThat(matcher.matches(makeContext(B.class, A.class)), notNullValue()); assertThat(matcher.matches(makeContext(A.class, B.class)), nullValue()); } @Test public void testAnchoredAndUnanchored() { ContextMatcher matcher = ContextPattern.any() .append(A.class) .append(B.class) .appendDotStar(); assertThat(matcher.matches(makeContext()), nullValue()); assertThat(matcher.matches(makeContext(A.class, B.class)), notNullValue()); assertThat(matcher.matches(makeContext(A.class, B.class, C.class)), notNullValue()); assertThat(matcher.matches(makeContext(A.class, C.class, B.class)), nullValue()); } @Test public void testOrderByCloseness() { ContextPattern patA = ContextPattern.subsequence(A.class); ContextPattern patB = ContextPattern.subsequence(B.class); InjectionContext ctx1 = makeContext(A.class, B.class); // B matches more closely than A assertThat(patB.matches(ctx1), lessThan(patA.matches(ctx1))); // A matches the same as itself assertThat(patA.matches(ctx1), allOf(lessThanOrEqualTo(patA.matches(ctx1)), greaterThanOrEqualTo(patA.matches(ctx1)))); } @Test public void testOrderByLength() { ContextPattern patShort = ContextPattern.subsequence(B.class); ContextPattern patLong = ContextPattern.subsequence(A.class, B.class); InjectionContext ctx1 = makeContext(A.class, B.class); // Long matches more closely than short assertThat(patLong.matches(ctx1), lessThan(patShort.matches(ctx1))); // Long matches like itself. assertThat(patLong.matches(ctx1), allOf(lessThanOrEqualTo(patLong.matches(ctx1)), greaterThanOrEqualTo(patLong.matches(ctx1)))); } @Test public void testOrderByLengthAfterCloseness() { ContextPattern patClose = ContextPattern.subsequence(C.class); ContextPattern patFar = ContextPattern.subsequence(A.class, B.class); InjectionContext ctx1 = makeContext(A.class, B.class, C.class); // Close matches more closely than long assertThat(patClose.matches(ctx1), lessThan(patFar.matches(ctx1))); } @Test public void testOrderByType() { ContextPattern patStrict = ContextPattern.subsequence(Ap.class); ContextPattern patLoose = ContextPattern.subsequence(A.class); InjectionContext ctx1 = makeContext(Ap.class); // Tight matches more tightly assertThat(patStrict.matches(ctx1), lessThan(patLoose.matches(ctx1))); } @Test public void testOrderByTypeLast() { ContextPattern patStrict = ContextPattern.subsequence(Bp.class); ContextPattern patLong = ContextPattern.subsequence(A.class, B.class); InjectionContext ctx1 = makeContext(A.class, Bp.class); // Length trumps tightness assertThat(patLong.matches(ctx1), lessThan(patStrict.matches(ctx1))); } @Test public void testOrderSubsequenceBeforeOther() { ContextPattern patAny = ContextPattern.any(); ContextPattern patLimited = ContextPattern.subsequence(B.class); InjectionContext ctx1 = makeContext(A.class, B.class); // B matches more closely than A assertThat(patLimited.matches(ctx1), lessThan(patAny.matches(ctx1))); } private InjectionContext makeContext(Class<?>... types) { InjectionContext context = DependencySolver.initialContext(); for (Class<?> type: types) { MockSatisfaction sat = new MockSatisfaction(type, new ArrayList<Desire>()); context = context.extend(sat, new MockInjectionPoint(type, null, false)); } return context; } private static class A {} private static class B {} private static class C {} private static class Ap extends A {} private static class Bp extends B {} private static class Cp extends C {} }