/*
* 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.io;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.zip.GZIPOutputStream;
import org.apache.commons.codec.binary.Base64OutputStream;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.powermock.reflect.Whitebox;
import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.io.Files;
import hudson.PluginManager;
import hudson.console.ConsoleNote;
import hudson.model.AbstractBuild;
import hudson.plugins.timestamper.Timestamp;
import hudson.plugins.timestamper.TimestampNote;
import hudson.plugins.timestamper.io.LogFileReader.Line;
import jenkins.model.Jenkins;
/**
* Unit test for {@link LogFileReader}.
*
* @author Steven G. Brown
*/
public class LogFileReaderTest {
/**
*/
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
private AbstractBuild<?, ?> build;
private LogFileReader logFileReader;
private Timestamp timestamp;
private String logFileContents;
private File uncompressedLogFile;
private File gzippedLogFile;
private File nonExistantFile;
/**
* @throws Exception
*/
@Before
public void setUp() throws Exception {
build = mock(AbstractBuild.class);
when(build.getLogInputStream()).thenCallRealMethod();
when(build.getLogReader()).thenCallRealMethod();
Whitebox.setInternalState(build, "charset", Charsets.UTF_8.name());
logFileReader = new LogFileReader(build);
timestamp = new Timestamp(42, 1000);
logFileContents = "line1\nline2"
+ new TimestampNote(timestamp.elapsedMillis, timestamp.millisSinceEpoch).encode() + "\n";
// Uncompressed log file
uncompressedLogFile = tempFolder.newFile();
Files.write(logFileContents, uncompressedLogFile, Charsets.UTF_8);
// Gzipped log file
gzippedLogFile = tempFolder.newFile("logFile.gz");
try (FileOutputStream fileOutputStream = new FileOutputStream(gzippedLogFile);
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(fileOutputStream);) {
gzipOutputStream.write(logFileContents.getBytes(Charset.defaultCharset()));
}
// Non-existant log file
nonExistantFile = new File(tempFolder.getRoot(), "logFile");
// Need to mock Jenkins to read the console notes.
Jenkins jenkins = mock(Jenkins.class);
Whitebox.setInternalState(jenkins, "pluginManager", mock(PluginManager.class));
Whitebox.setInternalState(Jenkins.class, "theInstance", jenkins);
}
/**
*/
@After
public void tearDown() {
Whitebox.setInternalState(Jenkins.class, "theInstance", (Jenkins) null);
logFileReader.close();
}
/**
* @throws Exception
*/
@Test
public void testNextLine_logFileExists() throws Exception {
when(build.getLogFile()).thenReturn(uncompressedLogFile);
testNextLine();
}
/**
* @throws Exception
*/
@Test
public void testNextLine_zippedLogFile() throws Exception {
when(build.getLogFile()).thenReturn(gzippedLogFile);
testNextLine();
}
private void testNextLine() throws Exception {
List<String> texts = new ArrayList<String>();
List<Optional<Timestamp>> timestamps = new ArrayList<Optional<Timestamp>>();
for (int i = 0; i < 3; i++) {
Optional<Line> line = logFileReader.nextLine();
if (!line.isPresent()) {
break;
}
texts.add(line.get().getText());
timestamps.add(line.get().readTimestamp());
}
List<String> expectedTexts = ImmutableList.of("line1", "line2");
assertThat("texts", texts, is(expectedTexts));
List<Optional<Timestamp>> expectedTimestamps = ImmutableList.of(Optional.<Timestamp>absent(),
Optional.of(timestamp));
assertThat("timestamps", timestamps, is(expectedTimestamps));
}
/**
* @throws Exception
*/
@Test
public void testNextLine_noLogFile() throws Exception {
when(build.getLogFile()).thenReturn(nonExistantFile);
assertThat(logFileReader.nextLine(), is(Optional.<Line>absent()));
}
/**
* @throws Exception
*/
@Test
public void testReadTimestamp_logContainsEscapeCharacters() throws Exception {
File logFile = tempFolder.newFile();
when(build.getLogFile()).thenReturn(logFile);
List<String> logFileContents = Arrays.asList(
"\u001B[35m\u001B[1mScanning dependencies of target\u001B[0m",
//
"abc" + ConsoleNote.PREAMBLE_STR, "abc" + ConsoleNote.PREAMBLE_STR + "def",
//
"abc" + ConsoleNote.PREAMBLE_STR + encodeConsoleNote(2, ""),
//
"abc" + ConsoleNote.PREAMBLE_STR + encodeConsoleNote(2, "de")
+ ConsoleNote.POSTAMBLE_STR.substring(0, 2),
//
"abc" + ConsoleNote.PREAMBLE_STR + encodeConsoleNote(2, "de") + ConsoleNote.POSTAMBLE_STR);
Files.write(Joiner.on('\n').join(logFileContents), logFile, Charsets.UTF_8);
List<Optional<Timestamp>> timestamps = new ArrayList<Optional<Timestamp>>();
for (int i = 0; i <= logFileContents.size(); i++) {
Optional<Line> line = logFileReader.nextLine();
if (!line.isPresent()) {
break;
}
timestamps.add(line.get().readTimestamp());
}
List<Optional<Timestamp>> expectedTimestamps = new ArrayList<Optional<Timestamp>>();
for (int i = 0; i < logFileContents.size(); i++) {
expectedTimestamps.add(Optional.<Timestamp>absent());
}
assertThat(timestamps, is(expectedTimestamps));
}
private String encodeConsoleNote(int size, String content) throws Exception {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try (DataOutputStream dataOutputStream = new DataOutputStream(
new Base64OutputStream(byteArrayOutputStream, true, -1, null))) {
dataOutputStream.writeInt(size);
dataOutputStream.writeBytes(content);
}
return byteArrayOutputStream.toString();
}
/**
* @throws Exception
*/
@Test
public void testLineCount_logFileExists() throws Exception {
when(build.getLogFile()).thenReturn(uncompressedLogFile);
assertThat(logFileReader.lineCount(), is(2));
}
/**
* @throws Exception
*/
@Test
public void testLineCount_zippedLogFile() throws Exception {
when(build.getLogFile()).thenReturn(gzippedLogFile);
assertThat(logFileReader.lineCount(), is(2));
}
/**
* @throws Exception
*/
@Test
public void testLineCount_noLogFile() throws Exception {
when(build.getLogFile()).thenReturn(nonExistantFile);
assertThat(logFileReader.lineCount(), is(0));
}
}