package net.sf.expectit;
/*
* #%L
* ExpectIt
* %%
* Copyright (C) 2014 Alexey Gavrilov and contributors
* %%
* 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.
* #L%
*/
import static net.sf.expectit.ExpectBuilder.DEFAULT_BUFFER_SIZE;
import static net.sf.expectit.TestUtils.LONG_TIMEOUT;
import static net.sf.expectit.TestUtils.SMALL_TIMEOUT;
import static net.sf.expectit.matcher.Matchers.allOf;
import static net.sf.expectit.matcher.Matchers.anyOf;
import static net.sf.expectit.matcher.Matchers.anyString;
import static net.sf.expectit.matcher.Matchers.contains;
import static net.sf.expectit.matcher.Matchers.eof;
import static net.sf.expectit.matcher.Matchers.exact;
import static net.sf.expectit.matcher.Matchers.matches;
import static net.sf.expectit.matcher.Matchers.regexp;
import static net.sf.expectit.matcher.Matchers.sequence;
import static net.sf.expectit.matcher.Matchers.startsWith;
import static net.sf.expectit.matcher.Matchers.times;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.when;
import java.io.EOFException;
import java.io.IOException;
import java.nio.channels.Pipe;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import net.sf.expectit.matcher.Matcher;
import net.sf.expectit.matcher.Matchers;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* Unit tests for basic single operation matchers.
*
* @author Alexey Gavrilov
*/
public class MatcherTest {
private SingleInputExpect input;
private ExecutorService executor;
private MockInputStream mock;
private final String text = "a1b2c3_";
/**
* Creates a mock input stream which send some data every SMALL_TIMEOUT ms.
*/
@Before
public void setup() throws Exception {
mock = TestUtils.mockInputStream(text);
final Pipe pipe = Pipe.open();
input = new SingleInputExpect(
pipe.source(),
pipe.sink(),
mock.getStream(),
Charset.defaultCharset(),
null,
null,
DEFAULT_BUFFER_SIZE,
false);
executor = Executors.newSingleThreadExecutor();
input.start(executor);
mock.waitUntilReady();
}
@After
public void cleanup() throws IOException {
input.stop();
executor.shutdown();
}
/**
* Basic expect string tests
*/
@Test
public void testExpectString() throws IOException, InterruptedException {
Result result = input.expect(LONG_TIMEOUT, contains("b2"));
assertTrue(result.isSuccessful());
assertEquals(result.groupCount(), 0);
assertEquals(result.end(), 4);
assertEquals(result.start(), 2);
assertEquals(result.group(), "b2");
assertEquals(result.end(0), 4);
assertEquals(result.start(0), 2);
assertEquals(result.group(0), "b2");
assertEquals(result.getBefore(), "a1");
checkIndexOutOfBound(result, 1);
StringBuilder buffer = input.getBuffer();
result = input.expect(SMALL_TIMEOUT, contains("cx"));
// no changes in the buffer if matching fails
assertEquals(buffer.length(), input.getBuffer().length());
assertFalse(result.isSuccessful());
checkIndexOutOfBound(result, 1);
checkIllegalState(result);
checkIllegalState(result, 0);
assertEquals(result.groupCount(), 0);
mock.push(text);
// expecting that mocking input stream goes circles
assertTrue(input.expect(LONG_TIMEOUT, contains("c3_a")).isSuccessful());
buffer = input.getBuffer();
// check that buffer gets updated
assertTrue(buffer.toString().startsWith("1b2c3"));
}
/**
* Check for null, empty and invalid inputs.
*/
@Test
public void testEmptyStringAndNull() throws IOException {
Result result = input.expect(LONG_TIMEOUT, contains(""));
assertTrue(result.isSuccessful());
try {
input.expect(LONG_TIMEOUT, contains(null));
fail();
} catch (NullPointerException ok) {
}
result = input.expect(LONG_TIMEOUT, regexp(""));
assertTrue(result.isSuccessful());
result = input.expect(LONG_TIMEOUT, matches(""));
assertTrue(result.isSuccessful());
try {
input.expect(LONG_TIMEOUT, regexp((String) null));
fail();
} catch (NullPointerException ok) {
}
try {
input.expect(LONG_TIMEOUT, regexp((Pattern) null));
fail();
} catch (NullPointerException ok) {
}
try {
input.expect(LONG_TIMEOUT, regexp("^(^^^"));
fail();
} catch (PatternSyntaxException ok) {
}
}
/**
* Tests for regexp matcher
*/
@Test
public void testExpectRegexp() throws IOException, InterruptedException {
Result result = input.expect(LONG_TIMEOUT, regexp(".b.c"));
assertTrue(result.isSuccessful());
assertEquals(result.getBefore(), "a");
assertEquals(result.groupCount(), 0);
assertEquals(result.end(), 5);
assertEquals(result.start(), 1);
assertEquals(result.group(), "1b2c");
assertEquals(result.end(0), 5);
assertEquals(result.start(0), 1);
assertEquals(result.group(0), "1b2c");
checkIndexOutOfBound(result, 1);
assertTrue(input.getBuffer().toString().startsWith("3_"));
assertEquals(result.getInput(), result.getBefore()
+ result.group(0)
+ input.getBuffer().toString());
result = input.expect(SMALL_TIMEOUT, regexp("a.z.c"));
assertFalse(result.isSuccessful());
checkIllegalState(result);
assertEquals(result.getInput(), "3_");
mock.push(text);
result = input.expect(LONG_TIMEOUT, regexp("3_([^_]*)c3"));
assertEquals(result.group(0), "3_a1b2c3");
assertEquals(result.group(1), "a1b2");
mock.push(text);
result = input.expect(LONG_TIMEOUT, regexp("a(.)b(.)c(.)"));
assertEquals(result.group(1), "1");
assertEquals(result.group(3), "3");
assertEquals(result.groupCount(), 3);
assertEquals(result.end(), 7);
mock.push(text);
// sanity check for Pattern instance
result = input.expect(LONG_TIMEOUT, regexp(Pattern.compile("a(.*)")));
assertTrue(result.isSuccessful());
assertFalse(result.canStopMatching());
}
/**
* Tests for exact match.
*/
@Test
public void testExpectMatch() throws IOException, InterruptedException {
mock.push(text);
Result result = input.expect(LONG_TIMEOUT, matches(".*a1b2.*"));
assertTrue(result.isSuccessful());
assertEquals(result.getBefore(), "");
checkIndexOutOfBound(result, 1);
assertEquals(result.groupCount(), 0);
assertEquals(input.getBuffer().length(), 0);
mock.push(text);
//await for some data
input.expect(LONG_TIMEOUT, contains("_a"));
mock.push(text);
assertTrue(input.expect(LONG_TIMEOUT, contains("_a")).isSuccessful());
result = input.expect(SMALL_TIMEOUT, matches("a(.)b(.)c(.)"));
assertFalse(result.isSuccessful());
checkIllegalState(result);
checkIndexOutOfBound(result, 1);
String buffer = input.getBuffer().toString();
assertTrue(buffer, buffer.startsWith("1b2c3_"));
result = input.expect(LONG_TIMEOUT, matches("(.*)c(.).*"));
assertEquals(result.start(), 0);
assertEquals(result.end(), buffer.length());
assertEquals(result.group(), buffer);
assertEquals(result.getBefore(), "");
assertEquals(result.groupCount(), 2);
assertEquals(result.group(2), "3");
checkIndexOutOfBound(result, 3);
mock.push(text);
// sanity check for Pattern instance
result = input.expect(LONG_TIMEOUT, matches(Pattern.compile("^a(.*)")));
assertTrue(result.isSuccessful());
assertEquals(result.group(1), "1b2c3_");
}
/**
* Tests for the 'allOf' matcher
*/
@Test
public void testAllOf() throws IOException, InterruptedException {
MultiResult result = input.expect(LONG_TIMEOUT, allOf(contains("b")));
assertTrue(result.isSuccessful());
assertEquals(result.start(), 2);
assertEquals(result.end(), 3);
mock.push(text);
result = input.expect(LONG_TIMEOUT, allOf(contains("a"), contains("b"), regexp("a1")));
assertTrue(result.isSuccessful());
assertEquals(result.start(), 6);
assertEquals(result.end(), 7);
for (Result r : result.getResults()) {
assertTrue(r.isSuccessful());
}
assertEquals(result.getResults().size(), 3);
assertEquals(result.getResults().get(1).start(), 6);
assertEquals(result.getResults().get(1).end(), 7);
assertEquals(result.getResults().get(2).start(), 4);
assertEquals(result.getResults().get(2).end(), 6);
mock.push(text);
// one negative
result = input.expect(SMALL_TIMEOUT, allOf(contains("a"), contains("b"), regexp("XXX")));
assertFalse(result.isSuccessful());
assertEquals(result.getResults().size(), 3);
assertEquals(result.getResults().get(0).start(), 4);
assertEquals(result.getResults().get(0).end(), 5);
assertTrue(result.getResults().get(1).isSuccessful());
assertFalse(result.getResults().get(2).isSuccessful());
// all negative
result = input.expect(SMALL_TIMEOUT, allOf(matches("vv"), regexp("XXX")));
for (Result r : result.getResults()) {
assertFalse(r.isSuccessful());
}
// same end position: the first wins
result = input.expect(LONG_TIMEOUT, allOf(contains("a"), contains("b"), regexp("a(.*)")));
assertTrue(result.isSuccessful());
assertEquals(result.groupCount(), 1);
try {
input.expect(LONG_TIMEOUT, allOf());
fail();
} catch (IllegalArgumentException ok) {
}
try {
input.expect(LONG_TIMEOUT, allOf((Matcher<?>[]) null));
fail();
} catch (NullPointerException ok) {
}
}
@Test
public void testAnyOf() throws IOException, InterruptedException {
try {
input.expect(LONG_TIMEOUT, anyOf());
fail();
} catch (IllegalArgumentException ok) {
}
try {
input.expect(LONG_TIMEOUT, anyOf((Matcher<?>[]) null));
fail();
} catch (NullPointerException ok) {
}
MultiResult result = input.expect(LONG_TIMEOUT, anyOf(contains("b")));
assertTrue(result.isSuccessful());
assertEquals(result.start(), 2);
assertEquals(result.end(), 3);
assertTrue(result.getInput().equals(result.getResults().get(0).getInput()));
assertEquals(result.getInput(), "a1b2c3_");
mock.push(text);
result = input.expect(LONG_TIMEOUT, anyOf(contains("x"), contains("b"), contains("zzz")));
assertTrue(result.isSuccessful());
assertEquals(result.start(), 6);
assertEquals(result.end(), 7);
assertEquals(result.group(), "b");
result = input.expect(SMALL_TIMEOUT, anyOf(contains("ttt"), contains("zzz")));
assertFalse(result.isSuccessful());
mock.push(text);
// test for combination of matchers
result = input.expect(
LONG_TIMEOUT,
anyOf(allOf(contains("1"), contains("2")), contains("FFFF")));
assertTrue(result.isSuccessful());
assertTrue(result.getResults().get(0) instanceof MultiResult);
assertEquals(result.getResults().size(), 2);
// test canStopMatching
mock.push(text);
result = input.expect(SMALL_TIMEOUT, anyOf(startsWith("a")));
assertTrue(result.canStopMatching());
result = input.expect(SMALL_TIMEOUT, anyOf(startsWith("a"), startsWith(text + text)));
assertFalse(result.canStopMatching());
}
@Test
public void testToString() {
Matcher<Result> c = contains("xyz");
assertEquals(c.toString(), "contains('xyz')");
Matcher<Result> r = regexp("xyz");
assertEquals(r.toString(), "regexp('xyz')");
Matcher<Result> m = matches("xyz");
assertEquals(m.toString(), "matches('xyz')");
assertEquals(
anyOf(c, r, m).toString(),
"anyOf(contains('xyz'),regexp('xyz'),matches('xyz'))");
assertEquals(allOf(c).toString(), "allOf(contains('xyz'))");
Matcher<?> e = Matchers.eof();
assertEquals(allOf(c, e).toString(), "allOf(contains('xyz'),eof)");
assertEquals(startsWith("XXX").toString(), "startsWith('XXX')");
assertEquals(
sequence(startsWith("TTT"), eof()).toString(), "sequence(startsWith('TTT'),eof)");
}
@Test
public void testEofMatcher() throws IOException, InterruptedException {
assertTrue(input.expect(SMALL_TIMEOUT, contains("a1")).isSuccessful());
assertTrue(input.expect(SMALL_TIMEOUT, contains("_")).isSuccessful());
// make sure that all the data received
input.expect(SMALL_TIMEOUT, times(3, contains("_")));
// make sure the buffer is cleaned
assertTrue(input.expect(SMALL_TIMEOUT, matches(".*")).isSuccessful());
mock.push(TestUtils.EOF);
try {
// now the buffer is empty
input.expect(SMALL_TIMEOUT, contains("xxx"));
fail();
} catch (EOFException ok) {
}
// should pass on successful match
assertTrue(input.expect(SMALL_TIMEOUT, contains("")).isSuccessful());
assertTrue(input.expect(SMALL_TIMEOUT, eof()).isSuccessful());
}
@Test
public void testEofMultiMatcher1() throws IOException, InterruptedException {
assertTrue(input.expect(SMALL_TIMEOUT, contains("a1")).isSuccessful());
mock.push(TestUtils.EOF);
MultiResult result = input.expect(SMALL_TIMEOUT, allOf(contains("b"), eof()));
assertTrue(result.isSuccessful());
assertFalse(result.getBefore().isEmpty());
assertTrue(input.expect(SMALL_TIMEOUT, eof()).isSuccessful());
}
@Test
public void testEofMultiMatcher2() throws IOException, InterruptedException {
assertTrue(input.expect(SMALL_TIMEOUT, contains("a1")).isSuccessful());
mock.push(TestUtils.EOF);
MultiResult result = input.expect(SMALL_TIMEOUT, anyOf(contains("wrong"), eof()));
assertTrue(result.isSuccessful());
assertFalse(result.getBefore().isEmpty());
assertTrue(input.expect(SMALL_TIMEOUT, eof()).isSuccessful());
}
@Test
public void testTimes() throws IOException, InterruptedException {
MultiResult result = input.expect(SMALL_TIMEOUT, times(1, contains("b")));
assertEquals("a1", result.getBefore());
assertEquals(result.group(), "b");
result = times(2, contains("abc")).matches("ZabcXabcY", false);
assertTrue(result.isSuccessful());
assertEquals(result.getBefore(), "ZabcX");
assertEquals(result.getResults().get(0).getBefore(), "Z");
assertEquals(result.getResults().get(1).getBefore(), "X");
result = times(3, contains("abc")).matches("ZabcXabcY", false);
assertFalse(result.isSuccessful());
mock.push(text + text);
result = input.expect(2 * SMALL_TIMEOUT, times(3, contains("_")));
assertTrue(result.isSuccessful());
assertEquals(result.getBefore(), "2c3_a1b2c3_a1b2c3");
assertEquals(result.group(), "_");
for (Result r : result.getResults()) {
assertTrue(r.isSuccessful());
assertTrue(r.getBefore().endsWith("2c3"));
}
mock.push(text);
result = input.expect(SMALL_TIMEOUT, times(10, contains("c")));
assertTrue(result.getResults().get(0).isSuccessful());
assertFalse(result.getResults().get(8).isSuccessful());
assertFalse(result.getResults().get(9).isSuccessful());
assertFalse(result.isSuccessful());
}
@Test
public void testSequence() throws IOException, InterruptedException {
try {
sequence();
fail();
} catch (IllegalArgumentException ignore) {
}
MultiResult result = input.expect(SMALL_TIMEOUT, sequence(contains("b")));
assertEquals("a1", result.getBefore());
mock.push(text);
result = input.expect(SMALL_TIMEOUT, sequence(contains("c"), contains("b")));
assertEquals("2c3_a1", result.getBefore());
assertEquals(result.group(), "b");
assertEquals(result.getResults().get(0).group(), "c");
mock.push(text);
final Matcher<MultiResult> sequence = sequence(contains("a"), contains("Z"), contains("c"));
assertEquals(sequence.toString(), "sequence(contains('a'),contains('Z'),contains('c'))");
result = input.expect(SMALL_TIMEOUT, sequence);
assertFalse(result.isSuccessful());
assertTrue(result.getResults().get(0).isSuccessful());
assertEquals(result.getResults().get(0).getBefore(), "2c3_");
result = input.expect(
SMALL_TIMEOUT,
sequence(contains("3"), anyOf(contains("Z"), contains("c"))));
assertTrue(result.isSuccessful());
assertEquals(result.getBefore(), "2c3_a1b2");
assertEquals(result.getResults().get(0).getBefore(), "2c");
assertEquals(result.getResults().get(1).getBefore(), "_a1b2");
assertEquals(result.getInput(), "2c3_a1b2c3_");
assertEquals(result.getResults().get(1).getInput(), "_a1b2c3_");
}
@Test
public void testAnyStringInfiniteTimeout() throws IOException {
assertTrue(input.expect(ExpectImpl.INFINITE_TIMEOUT, anyString()).isSuccessful());
}
@Test
public void testAnyString() throws IOException, InterruptedException {
Result result = input.expect(SMALL_TIMEOUT, anyString());
assertTrue(result.group().startsWith("a1"));
assertTrue(result.group().endsWith("_"));
assertEquals(result.getBefore(), "");
mock.push(text);
assertTrue(input.expect(SMALL_TIMEOUT, contains("a1b2")).getBefore().isEmpty());
// stop producing new input
reset(mock.getStream());
when(mock.getStream().read(any(byte[].class))).thenReturn(0);
// just to make sure that the buffer is clean
input.expect(SMALL_TIMEOUT, anyString());
input.expect(SMALL_TIMEOUT, anyString());
assertTrue(input.getBuffer().length() == 0);
result = input.expect(SMALL_TIMEOUT, anyString());
assertFalse(result.isSuccessful());
}
/**
* Verifies that all the result methods throw IllegalStateException.
*/
private static void checkIllegalState(Result result) {
try {
result.end();
fail();
} catch (IllegalStateException ignore) {
}
try {
result.getBefore();
fail();
} catch (IllegalStateException ignore) {
}
try {
result.group();
fail();
} catch (IllegalStateException ignore) {
}
try {
result.start();
fail();
} catch (IllegalStateException ignore) {
}
assertNotNull(result.getInput());
}
/**
* Verifies that all the result methods that take the index throws IndexOutOfBoundException
* for the given index.
*/
private static void checkIndexOutOfBound(Result result, int index) {
try {
result.end(index);
fail();
} catch (IndexOutOfBoundsException ignore) {
}
try {
result.group(index);
fail();
} catch (IndexOutOfBoundsException ignore) {
}
try {
result.start(index);
fail();
} catch (IndexOutOfBoundsException ignore) {
}
}
/**
* Verifies that all the result methods throw IllegalStateException.
*/
private static void checkIllegalState(Result result, int index) {
try {
result.end(index);
fail();
} catch (IllegalStateException ignore) {
}
try {
result.group(index);
fail();
} catch (IllegalStateException ignore) {
}
try {
result.start(index);
fail();
} catch (IllegalStateException ignore) {
}
}
@Test (timeout = 5000)
public void testExact() throws IOException, InterruptedException {
Result result = input.expect(SMALL_TIMEOUT, exact("a1"));
assertFalse(result.isSuccessful());
result = input.expect(SMALL_TIMEOUT, exact(text));
assertTrue(result.isSuccessful());
assertTrue(result.canStopMatching());
assertEquals(result.getBefore(), "");
assertEquals(result.groupCount(), 0);
assertEquals(result.group(), text);
assertFalse(input.expect(SMALL_TIMEOUT, exact(text)).isSuccessful());
mock.push("XZY");
result = input.expect(LONG_TIMEOUT * 100, exact("x"));
assertFalse(result.isSuccessful());
assertTrue(result.canStopMatching());
}
@Test
public void testStartsWith() throws IOException, InterruptedException {
Result result = input.expect(SMALL_TIMEOUT, startsWith("YZ"));
assertFalse(result.isSuccessful());
final String substring = text.substring(0, 2);
result = input.expect(SMALL_TIMEOUT, startsWith(substring));
assertTrue(result.isSuccessful());
assertEquals(result.getBefore(), "");
assertEquals(result.groupCount(), 0);
assertTrue(result.canStopMatching());
assertEquals(result.group(), substring);
final Result result2 = input.expect(SMALL_TIMEOUT, startsWith(text + text));
assertFalse(result2.isSuccessful());
assertFalse(result2.canStopMatching());
}
}