/* * The MIT License * * Copyright (c) 2016 Steven G. Brown * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.plugins.timestamper.action; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import static org.junit.Assume.assumeThat; import static org.mockito.Mockito.atMost; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.BufferedReader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import javax.annotation.Nonnull; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; import org.mockito.stubbing.OngoingStubbing; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import hudson.plugins.timestamper.Timestamp; import hudson.plugins.timestamper.io.LogFileReader; import hudson.plugins.timestamper.io.LogFileReader.Line; import hudson.plugins.timestamper.io.TimestampsReader; /** * Unit test for the {@link TimestampsActionOutput} class. * * @author Steven G. Brown */ @RunWith(Parameterized.class) public class TimestampsActionOutputTest { private static final Optional<Integer> NO_ENDLINE = Optional.absent(); private static final Function<Timestamp, String> FORMAT = new Function<Timestamp, String>() { @Override public String apply(@Nonnull Timestamp timestamp) { return String.valueOf(timestamp.millisSinceEpoch); } }; private static final Function<Timestamp, String> ELAPSED_FORMAT = new Function<Timestamp, String>() { @Override public String apply(@Nonnull Timestamp timestamp) { return String.valueOf(timestamp.elapsedMillis); } }; private static final List<Timestamp> TIMESTAMPS = ImmutableList.of(new Timestamp(0, 1), // new Timestamp(1, 2), // new Timestamp(10, 3), // new Timestamp(100, 4), // new Timestamp(1000, 5), // new Timestamp(10000, 6)); /** * @return the test data */ @Parameters(name = "{0}") public static Collection<Object[]> data() { List<Object[]> testCases = new ArrayList<Object[]>(); testCases.add(new Object[] { "format", new TimestampsActionQuery(0, NO_ENDLINE, Collections.singletonList(FORMAT), false, false), Arrays.asList("1", "2", "3", "4", "5", "6") }); testCases.add(new Object[] { "format + elapsed_format", new TimestampsActionQuery(0, NO_ENDLINE, ImmutableList.of(FORMAT, ELAPSED_FORMAT), false, false), Arrays.asList("1 0", "2 1", "3 10", "4 100", "5 1000", "6 10000") }); testCases.add(new Object[] { "appendLogLine", new TimestampsActionQuery(0, NO_ENDLINE, Collections.singletonList(FORMAT), true, false), Arrays.asList("1 line1", "2 line2", "3 line3", "4 line4", "5 line5", "6 line6") }); testCases.add(new Object[] { "currentTime", new TimestampsActionQuery(0, NO_ENDLINE, ImmutableList.of(FORMAT, ELAPSED_FORMAT), false, true), Arrays.asList("1 42") }); // start line testCases.add(new Object[] { "start 2", new TimestampsActionQuery(2, NO_ENDLINE, Collections.singletonList(FORMAT), true, false), Arrays.asList("2 line2", "3 line3", "4 line4", "5 line5", "6 line6") }); testCases.add(new Object[] { "start 1", new TimestampsActionQuery(1, NO_ENDLINE, Collections.singletonList(FORMAT), true, false), Arrays.asList("1 line1", "2 line2", "3 line3", "4 line4", "5 line5", "6 line6") }); testCases.add(new Object[] { "start -1", new TimestampsActionQuery(-1, NO_ENDLINE, Collections.singletonList(FORMAT), true, false), Arrays.asList("6 line6") }); testCases.add(new Object[] { "start -2", new TimestampsActionQuery(-2, NO_ENDLINE, Collections.singletonList(FORMAT), true, false), Arrays.asList("5 line5", "6 line6") }); // end line testCases .add( new Object[] { "end 2", new TimestampsActionQuery(0, Optional.of(2), Collections.singletonList(FORMAT), true, false), Arrays.asList("1 line1", "2 line2") }); testCases.add(new Object[] { "end 1", new TimestampsActionQuery(0, Optional.of(1), Collections.singletonList(FORMAT), true, false), Arrays.asList("1 line1") }); testCases.add(new Object[] { "end 0", new TimestampsActionQuery(0, Optional.of(0), Collections.singletonList(FORMAT), true, false), Collections.emptyList() }); testCases.add(new Object[] { "end -1", new TimestampsActionQuery(0, Optional.of(-1), Collections.singletonList(FORMAT), true, false), Arrays.asList("1 line1", "2 line2", "3 line3", "4 line4", "5 line5", "6 line6") }); testCases.add(new Object[] { "end -2", new TimestampsActionQuery(0, Optional.of(-2), Collections.singletonList(FORMAT), true, false), Arrays.asList("1 line1", "2 line2", "3 line3", "4 line4", "5 line5") }); // start line and end line testCases.add(new Object[] { "start 2, end -2", new TimestampsActionQuery(2, Optional.of(-2), Collections.singletonList(FORMAT), true, false), Arrays.asList("2 line2", "3 line3", "4 line4", "5 line5") }); testCases.add(new Object[] { "start 2, end 5", new TimestampsActionQuery(2, Optional.of(5), Collections.singletonList(FORMAT), true, false), Arrays.asList("2 line2", "3 line3", "4 line4", "5 line5") }); testCases.add(new Object[] { "start -4, end -2", new TimestampsActionQuery(-4, Optional.of(-2), Collections.singletonList(FORMAT), true, false), Arrays.asList("3 line3", "4 line4", "5 line5") }); testCases.add(new Object[] { "start 4, end -4", new TimestampsActionQuery(4, Optional.of(-4), Collections.singletonList(FORMAT), true, false), Collections.emptyList() }); return testCases; } /** */ @Parameter(0) public String testCaseDescription; /** */ @Parameter(1) public TimestampsActionQuery query; /** */ @Parameter(2) public List<String> expectedLines; private TimestampsReader timestampsReader; private LogFileReader logFileReader; private BufferedReader reader; /** * @throws Exception */ @Before public void setUp() throws Exception { timestampsReader = mock(TimestampsReader.class); OngoingStubbing<Optional<Timestamp>> readStubbing = when(timestampsReader.read()); for (Timestamp timestamp : TIMESTAMPS) { readStubbing = readStubbing.thenReturn(Optional.of(timestamp)); } readStubbing.thenReturn(Optional.<Timestamp>absent()); List<Line> lines = new ArrayList<Line>(); for (int i = 1; i <= TIMESTAMPS.size(); i++) { Line line = mock(Line.class); when(line.getText()).thenReturn("line" + i); when(line.readTimestamp()).thenReturn(Optional.<Timestamp>absent()); lines.add(line); } logFileReader = mock(LogFileReader.class); OngoingStubbing<Optional<Line>> nextLineStubbing = when(logFileReader.nextLine()); for (Line line : lines) { nextLineStubbing = nextLineStubbing.thenReturn(Optional.of(line)); } nextLineStubbing.thenReturn(Optional.<Line>absent()); when(logFileReader.lineCount()).thenReturn(6); reader = TimestampsActionOutput.open(timestampsReader, logFileReader, query, new Timestamp(42, 1)); } /** * @throws Exception */ @After public void tearDown() throws Exception { // for efficiency, avoid counting the number of lines more than once verify(logFileReader, atMost(1)).lineCount(); reader.close(); } /** */ @Test public void testRead_eachCharacter() throws Exception { StringBuilder result = new StringBuilder(); int character; while ((character = reader.read()) != -1) { result.append((char) character); } assertThat(result.toString(), is(joinLines(expectedLines))); } /** */ @Test public void testRead_allAtOnce() throws Exception { String expectedResult = joinLines(expectedLines); char[] result = new char[expectedResult.length()]; reader.read(result, 0, result.length); assertThat(String.valueOf(result), is(expectedResult)); } /** * @throws Exception */ @Test public void testRead_noTimestamps() throws Exception { assumeThat(query.currentTime, is(false)); // Remove formatted timestamps from expected result if (query.appendLogLine) { expectedLines = Lists.transform(expectedLines, new Function<String, String>() { @Override public String apply(@Nonnull String input) { return input.replaceFirst("^.*( \\w*)$", "$1"); } }); } else { expectedLines = Collections.emptyList(); } when(timestampsReader.read()).thenReturn(Optional.<Timestamp>absent()); assertThat(readLines(), is(expectedLines)); } /** * @throws Exception */ @Test public void testRead_timestampsInLogFileOnly() throws Exception { when(timestampsReader.read()).thenReturn(Optional.<Timestamp>absent()); List<Line> lines = new ArrayList<Line>(); int i = 1; for (Timestamp timestamp : TIMESTAMPS) { Line line = mock(Line.class); when(line.getText()).thenReturn("line" + i); when(line.readTimestamp()).thenReturn(Optional.of(timestamp)); lines.add(line); i++; } OngoingStubbing<Optional<Line>> nextLineStubbing = when(logFileReader.nextLine()); for (Line line : lines) { nextLineStubbing = nextLineStubbing.thenReturn(Optional.of(line)); } nextLineStubbing.thenReturn(Optional.<Line>absent()); assertThat(readLines(), is(expectedLines)); } /** * @throws Exception */ @Test public void testRead_noLogFile() throws Exception { if (query.appendLogLine) { // Remove log line from expected result expectedLines = Lists.transform(expectedLines, new Function<String, String>() { @Override public String apply(@Nonnull String input) { return input.replaceFirst("\\w*$", ""); } }); } when(logFileReader.nextLine()).thenReturn(Optional.<Line>absent()); assertThat(readLines(), is(expectedLines)); } /** * @throws Exception */ @Test public void testRead_noTimestampsAndNoLogFile() throws Exception { assumeThat(query.currentTime, is(false)); when(timestampsReader.read()).thenReturn(Optional.<Timestamp>absent()); when(logFileReader.nextLine()).thenReturn(Optional.<Line>absent()); assertThat(readLines(), is(Collections.<String>emptyList())); } private String joinLines(List<String> lines) { StringBuilder result = new StringBuilder(); for (String line : lines) { result.append(line).append("\n"); } return result.toString(); } private List<String> readLines() throws Exception { List<String> lines = new ArrayList<String>(); String line; while ((line = reader.readLine()) != null) { lines.add(line); } return lines; } }