All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.maven.surefire.booter.spi.EventChannelEncoder Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.maven.surefire.booter.spi;

import javax.annotation.Nonnull;

import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.charset.CharsetEncoder;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.maven.plugin.surefire.log.api.ConsoleLoggerUtils;
import org.apache.maven.surefire.api.booter.DumpErrorSingleton;
import org.apache.maven.surefire.api.booter.ForkedProcessEventType;
import org.apache.maven.surefire.api.booter.MasterProcessChannelEncoder;
import org.apache.maven.surefire.api.report.ReportEntry;
import org.apache.maven.surefire.api.report.RunMode;
import org.apache.maven.surefire.api.report.SafeThrowable;
import org.apache.maven.surefire.api.report.StackTraceWriter;
import org.apache.maven.surefire.api.report.TestOutputReportEntry;
import org.apache.maven.surefire.api.report.TestSetReportEntry;
import org.apache.maven.surefire.api.util.internal.WritableBufferedByteChannel;
import org.apache.maven.surefire.booter.stream.EventEncoder;

import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_BYE;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_DEBUG;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_ERROR;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_INFO;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_CONSOLE_WARNING;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_JVM_EXIT_ERROR;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_NEXT_TEST;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDERR;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDERR_NEW_LINE;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDOUT;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STDOUT_NEW_LINE;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_STOP_ON_NEXT_TEST;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_SYSPROPS;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TESTSET_COMPLETED;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TESTSET_STARTING;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_ASSUMPTIONFAILURE;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_ERROR;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_FAILED;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_SKIPPED;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_STARTING;
import static org.apache.maven.surefire.api.booter.ForkedProcessEventType.BOOTERCODE_TEST_SUCCEEDED;

/**
 * magic number : opcode : run mode [: opcode specific data]*
 * 
* * @author Tibor Digana (tibor17) * @since 3.0.0-M4 */ @SuppressWarnings("checkstyle:linelength") public class EventChannelEncoder extends EventEncoder implements MasterProcessChannelEncoder { private final AtomicBoolean trouble = new AtomicBoolean(); private volatile boolean onExit; /** * The encoder for events. * * @param out the channel available for writing the events */ public EventChannelEncoder(@Nonnull WritableBufferedByteChannel out) { super(out); } @Override public boolean checkError() { return trouble.get(); } @Override public void onJvmExit() { onExit = true; write(ByteBuffer.wrap(new byte[] {'\n'}), true); } void encodeSystemProperties(Map sysProps, RunMode runMode, Long testRunId) { CharsetEncoder encoder = newCharsetEncoder(); ByteBuffer result = null; for (Iterator> it = sysProps.entrySet().iterator(); it.hasNext(); ) { Entry entry = it.next(); String key = entry.getKey(); String value = entry.getValue(); int bufferLength = estimateBufferLength(BOOTERCODE_SYSPROPS.getOpcode().length(), runMode, encoder, 0, 1, key, value); result = result != null && result.capacity() >= bufferLength ? result : ByteBuffer.allocate(bufferLength); ((Buffer) result).clear(); // :maven-surefire-event:sys-prop:::UTF-8::::: encode(encoder, result, BOOTERCODE_SYSPROPS, runMode, testRunId, key, value); boolean sync = !it.hasNext(); write(result, sync); } } @Override public void testSetStarting(TestSetReportEntry reportEntry, boolean trimStackTraces) { encode(BOOTERCODE_TESTSET_STARTING, reportEntry, trimStackTraces, true); } @Override public void testSetCompleted(TestSetReportEntry reportEntry, boolean trimStackTraces) { encodeSystemProperties(reportEntry.getSystemProperties(), reportEntry.getRunMode(), reportEntry.getTestRunId()); encode(BOOTERCODE_TESTSET_COMPLETED, reportEntry, trimStackTraces, true); } @Override public void testStarting(ReportEntry reportEntry, boolean trimStackTraces) { encode(BOOTERCODE_TEST_STARTING, reportEntry, trimStackTraces, true); } @Override public void testSucceeded(ReportEntry reportEntry, boolean trimStackTraces) { encode(BOOTERCODE_TEST_SUCCEEDED, reportEntry, trimStackTraces, true); } @Override public void testFailed(ReportEntry reportEntry, boolean trimStackTraces) { encode(BOOTERCODE_TEST_FAILED, reportEntry, trimStackTraces, true); } @Override public void testSkipped(ReportEntry reportEntry, boolean trimStackTraces) { encode(BOOTERCODE_TEST_SKIPPED, reportEntry, trimStackTraces, true); } @Override public void testError(ReportEntry reportEntry, boolean trimStackTraces) { encode(BOOTERCODE_TEST_ERROR, reportEntry, trimStackTraces, true); } @Override public void testAssumptionFailure(ReportEntry reportEntry, boolean trimStackTraces) { encode(BOOTERCODE_TEST_ASSUMPTIONFAILURE, reportEntry, trimStackTraces, true); } @Override public void testOutput(TestOutputReportEntry reportEntry) { boolean stdout = reportEntry.isStdOut(); boolean newLine = reportEntry.isNewLine(); String msg = reportEntry.getLog(); ForkedProcessEventType event = stdout ? (newLine ? BOOTERCODE_STDOUT_NEW_LINE : BOOTERCODE_STDOUT) : (newLine ? BOOTERCODE_STDERR_NEW_LINE : BOOTERCODE_STDERR); setOutErr(event, reportEntry.getRunMode(), reportEntry.getTestRunId(), msg); } private void setOutErr(ForkedProcessEventType eventType, RunMode runMode, Long testRunId, String message) { ByteBuffer result = encodeMessage(eventType, runMode, testRunId, message); write(result, false); } @Override public void consoleInfoLog(String message) { ByteBuffer result = encodeMessage(BOOTERCODE_CONSOLE_INFO, message); write(result, true); } @Override public void consoleErrorLog(String message) { consoleErrorLog(message, null); } @Override public void consoleErrorLog(Throwable t) { consoleErrorLog(t.getLocalizedMessage(), t); } @Override public void consoleErrorLog(String message, Throwable t) { CharsetEncoder encoder = newCharsetEncoder(); String stackTrace = t == null ? null : ConsoleLoggerUtils.toString(t); int bufferMaxLength = estimateBufferLength( BOOTERCODE_CONSOLE_ERROR.getOpcode().length(), null, encoder, 0, 0, message, null, stackTrace); ByteBuffer result = ByteBuffer.allocate(bufferMaxLength); encodeHeader(result, BOOTERCODE_CONSOLE_ERROR); encodeCharset(result); encode(encoder, result, message, null, stackTrace); write(result, true); } @Override public void consoleErrorLog(StackTraceWriter stackTraceWriter, boolean trimStackTraces) { error(stackTraceWriter, trimStackTraces, BOOTERCODE_CONSOLE_ERROR, true); } @Override public void consoleDebugLog(String message) { ByteBuffer result = encodeMessage(BOOTERCODE_CONSOLE_DEBUG, message); write(result, true); } @Override public void consoleWarningLog(String message) { ByteBuffer result = encodeMessage(BOOTERCODE_CONSOLE_WARNING, message); write(result, true); } @Override public void bye() { encodeOpcode(BOOTERCODE_BYE, true); } @Override public void stopOnNextTest() { encodeOpcode(BOOTERCODE_STOP_ON_NEXT_TEST, true); } @Override public void acquireNextTest() { encodeOpcode(BOOTERCODE_NEXT_TEST, true); } @Override public void sendExitError(StackTraceWriter stackTraceWriter, boolean trimStackTraces) { error(stackTraceWriter, trimStackTraces, BOOTERCODE_JVM_EXIT_ERROR, true); } private void error( StackTraceWriter stackTraceWriter, boolean trimStackTraces, ForkedProcessEventType eventType, @SuppressWarnings("SameParameterValue") boolean sync) { CharsetEncoder encoder = newCharsetEncoder(); StackTrace stackTraceWrapper = new StackTrace(stackTraceWriter, trimStackTraces); int bufferMaxLength = estimateBufferLength( eventType.getOpcode().length(), null, encoder, 0, 0, stackTraceWrapper.message, stackTraceWrapper.smartTrimmedStackTrace, stackTraceWrapper.stackTrace); ByteBuffer result = ByteBuffer.allocate(bufferMaxLength); encodeHeader(result, eventType); encodeCharset(result); encode(encoder, result, stackTraceWrapper); write(result, sync); } // example // :maven-surefire-event:testset-starting:rerun-test-after-failure:1:5:UTF-8::SourceName::SourceText::Name::NameText::Group::Message::ElapsedTime::LocalizedMessage::SmartTrimmedStackTrace::toStackTrace( stw, trimStackTraces ):: private void encode( ForkedProcessEventType operation, ReportEntry reportEntry, boolean trimStackTraces, @SuppressWarnings("SameParameterValue") boolean sync) { ByteBuffer result = encode(operation, reportEntry, trimStackTraces); write(result, sync); } private void encodeOpcode(ForkedProcessEventType eventType, boolean sync) { int bufferMaxLength = estimateBufferLength(eventType.getOpcode().length(), null, null, 0, 0); ByteBuffer result = ByteBuffer.allocate(bufferMaxLength); encodeHeader(result, eventType); write(result, sync); } @Override protected void write(ByteBuffer frame, boolean sync) { final boolean wasInterrupted = Thread.interrupted(); try { super.write(frame, sync); } catch (ClosedChannelException e) { if (!onExit) { String event = new String( frame.array(), frame.arrayOffset() + ((Buffer) frame).position(), frame.remaining(), getCharset()); DumpErrorSingleton.getSingleton() .dumpException(e, "Channel closed while writing the event '" + event + "'."); } } catch (IOException e) { if (trouble.compareAndSet(false, true)) { DumpErrorSingleton.getSingleton().dumpException(e); } } finally { if (wasInterrupted) { Thread.currentThread().interrupt(); } } } private void encode(CharsetEncoder encoder, ByteBuffer result, StackTrace stw) { encode(encoder, result, stw.message, stw.smartTrimmedStackTrace, stw.stackTrace); } private void encode( CharsetEncoder encoder, ByteBuffer result, String message, String smartStackTrace, String stackTrace) { encodeString(encoder, result, message); encodeString(encoder, result, smartStackTrace); encodeString(encoder, result, stackTrace); } /** * Used operations:
*
    *
  • {@link ForkedProcessEventType#BOOTERCODE_TESTSET_STARTING},
  • *
  • {@link ForkedProcessEventType#BOOTERCODE_TESTSET_COMPLETED},
  • *
  • {@link ForkedProcessEventType#BOOTERCODE_TEST_STARTING},
  • *
  • {@link ForkedProcessEventType#BOOTERCODE_TEST_SUCCEEDED},
  • *
  • {@link ForkedProcessEventType#BOOTERCODE_TEST_FAILED},
  • *
  • {@link ForkedProcessEventType#BOOTERCODE_TEST_ERROR},
  • *
  • {@link ForkedProcessEventType#BOOTERCODE_TEST_SKIPPED},
  • *
  • {@link ForkedProcessEventType#BOOTERCODE_TEST_ASSUMPTIONFAILURE}.
  • *
*/ ByteBuffer encode(ForkedProcessEventType operation, ReportEntry reportEntry, boolean trimStackTraces) { StackTrace stackTraceWrapper = new StackTrace(reportEntry.getStackTraceWriter(), trimStackTraces); CharsetEncoder encoder = newCharsetEncoder(); int bufferMaxLength = estimateBufferLength( operation.getOpcode().length(), reportEntry.getRunMode(), encoder, 1, 1, reportEntry.getSourceName(), reportEntry.getSourceText(), reportEntry.getName(), reportEntry.getNameText(), reportEntry.getGroup(), reportEntry.getMessage(), stackTraceWrapper.message, stackTraceWrapper.smartTrimmedStackTrace, stackTraceWrapper.stackTrace); ByteBuffer result = ByteBuffer.allocate(bufferMaxLength); encodeHeader(result, operation, reportEntry.getRunMode(), reportEntry.getTestRunId()); encodeCharset(result); encodeString(encoder, result, reportEntry.getSourceName()); encodeString(encoder, result, reportEntry.getSourceText()); encodeString(encoder, result, reportEntry.getName()); encodeString(encoder, result, reportEntry.getNameText()); encodeString(encoder, result, reportEntry.getGroup()); encodeString(encoder, result, reportEntry.getMessage()); encodeInteger(result, reportEntry.getElapsed()); encode(encoder, result, stackTraceWrapper); return result; } ByteBuffer encodeMessage(ForkedProcessEventType eventType, RunMode runMode, Long testRunId, String message) { CharsetEncoder encoder = newCharsetEncoder(); int bufferMaxLength = estimateBufferLength(eventType.getOpcode().length(), runMode, encoder, 0, 1, message); ByteBuffer result = ByteBuffer.allocate(bufferMaxLength); encode(encoder, result, eventType, runMode, testRunId, message); return result; } ByteBuffer encodeMessage(ForkedProcessEventType eventType, String message) { CharsetEncoder encoder = newCharsetEncoder(); int bufferMaxLength = estimateBufferLength(eventType.getOpcode().length(), null, encoder, 0, 0, message); ByteBuffer result = ByteBuffer.allocate(bufferMaxLength); encode(encoder, result, eventType, message); return result; } private static String toStackTrace(StackTraceWriter stw, boolean trimStackTraces) { if (stw == null) { return null; } return trimStackTraces ? stw.writeTrimmedTraceToString() : stw.writeTraceToString(); } private static final class StackTrace { final String message; final String smartTrimmedStackTrace; final String stackTrace; StackTrace(StackTraceWriter stackTraceWriter, boolean trimStackTraces) { SafeThrowable throwable = stackTraceWriter == null ? null : stackTraceWriter.getThrowable(); message = throwable == null ? null : throwable.getLocalizedMessage(); smartTrimmedStackTrace = stackTraceWriter == null ? null : stackTraceWriter.smartTrimmedStackTrace(); stackTrace = stackTraceWriter == null ? null : toStackTrace(stackTraceWriter, trimStackTraces); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy