diff --git a/plexus-compiler-test/src/main/java/org/codehaus/plexus/compiler/AbstractCompilerTest.java b/plexus-compiler-test/src/main/java/org/codehaus/plexus/compiler/AbstractCompilerTest.java index 1c42dc70..d5cbe65a 100644 --- a/plexus-compiler-test/src/main/java/org/codehaus/plexus/compiler/AbstractCompilerTest.java +++ b/plexus-compiler-test/src/main/java/org/codehaus/plexus/compiler/AbstractCompilerTest.java @@ -128,6 +128,20 @@ protected List getClasspath() throws Exception { protected void configureCompilerConfig(CompilerConfiguration compilerConfig) {} + /** + * Called once per compile iteration to allow configuration customization for + * tests. + * + * @param compilerConfig + * configuration used for this compile iteration. + * @param filename + * file about to be compiled this iteration. + * @since 2.14.0 + */ + protected void configureCompilerConfig(CompilerConfiguration compilerConfig, String filename) { + configureCompilerConfig(compilerConfig); + } + @Test public void testCompilingSources() throws Exception { List messages = new ArrayList<>(); @@ -257,7 +271,7 @@ private List getCompilerConfigurations() throws Exception compilerConfig.setForceJavacCompilerUse(this.forceJavacCompilerUse); - configureCompilerConfig(compilerConfig); + configureCompilerConfig(compilerConfig, filename); String target = getTargetVersion(); if (StringUtils.isNotEmpty(target)) { @@ -319,6 +333,12 @@ protected int expectedWarnings() { return 0; } + /** + * Count of output generated at the {@link Kind#NOTE} level. + * + * @return count + * @since 2.14.0 + */ protected int expectedNotes() { return 0; } diff --git a/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EcjLogParser.java b/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EcjLogParser.java new file mode 100644 index 00000000..1c15c35b --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EcjLogParser.java @@ -0,0 +1,48 @@ +/* + * The MIT License + * + * Copyright (c) 2005, The Codehaus + * + * 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 org.codehaus.plexus.compiler.eclipse; + +import java.io.File; +import java.util.List; + +import org.codehaus.plexus.compiler.CompilerMessage; + +/** + * Log parser interface. + * + * @author Jason Faust + * @since 2.14.0 + */ +public interface EcjLogParser { + + /** + * Pares an Eclipse Compiler log file. + * + * @param logFile file to parse. + * @param errorsAsWarnings if errors should be down-graded to warnings. + * @return the messages. + * @throws Exception on parse errors. + */ + List parse(File logFile, boolean errorsAsWarnings) throws Exception; +} diff --git a/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EcjResponseParser.java b/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EcjResponseParser.java index b02d052c..3261678e 100644 --- a/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EcjResponseParser.java +++ b/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EcjResponseParser.java @@ -18,9 +18,10 @@ /** * @author Frits Jalvingh + * @author Jason Faust * Created on 31-3-18. */ -public class EcjResponseParser { +public class EcjResponseParser implements EcjLogParser { /*--------------------------------------------------------------*/ /* CODING: Decode ECJ -log format results. */ /*--------------------------------------------------------------*/ @@ -40,6 +41,7 @@ private static XMLInputFactory getStreamFactory() { * @param errorsAsWarnings should we treat errors as warnings * Scan the specified response file for compilation messages. */ + @Override public List parse(File xmltf, boolean errorsAsWarnings) throws Exception { // if(xmltf.length() < 80) // return; @@ -106,20 +108,33 @@ private void decodeProblem( throws Exception { String id = xsr.getAttributeValue(null, "optionKey"); // Key for the problem int startline = getInt(xsr, "line"); - int column = getInt(xsr, "charStart"); - int endCol = getInt(xsr, "charEnd"); + int column = 0; + int endCol = 0; String sev = xsr.getAttributeValue(null, "severity"); String message = "Unknown message?"; - // -- Go watch for "message" + // -- Go watch for "message" and "source_context" while (xsr.hasNext()) { int type = xsr.nextTag(); if (type == XMLStreamConstants.START_ELEMENT) { if ("message".equals(xsr.getLocalName())) { message = xsr.getAttributeValue(null, "value"); } + if ("source_context".equals(xsr.getLocalName())) { + column = getInt(xsr, "sourceStart"); + if (column != -1) { + column += 1; // Offset to 1-based index + } else { + column = 0; + } + endCol = getInt(xsr, "sourceEnd"); + if (endCol != -1) { + endCol += 1; // Offset to 1-based index + } else { + endCol = 0; + } + } ignoreTillEnd(xsr); - } else if (type == XMLStreamConstants.END_ELEMENT) { break; } diff --git a/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EcjTextLogParser.java b/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EcjTextLogParser.java new file mode 100644 index 00000000..c9804c76 --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EcjTextLogParser.java @@ -0,0 +1,127 @@ +/* + * The MIT License + * + * Copyright (c) 2005, The Codehaus + * + * 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 org.codehaus.plexus.compiler.eclipse; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.codehaus.plexus.compiler.CompilerMessage; +import org.codehaus.plexus.compiler.CompilerMessage.Kind; + +/** + * Parser for non-XML Eclipse Compiler output. + * + * @author Jason Faust + * @since 2.14.0 + */ +public class EcjTextLogParser implements EcjLogParser { + + private static final String TEN_DASH = "----------"; + private static final String PATTERN_LEVEL_FILE = "^\\d+\\.\\s+(\\w+)\\s+in\\s+(.*)\\s+\\(at line (\\d+)\\)$"; + + private Pattern levelFile = Pattern.compile(PATTERN_LEVEL_FILE); + + @Override + public List parse(File logFile, boolean errorsAsWarnings) throws Exception { + List ret = new ArrayList<>(); + try (BufferedReader in = + new BufferedReader(new InputStreamReader(new FileInputStream(logFile), StandardCharsets.UTF_8))) { + String line; + List buffer = new LinkedList<>(); + boolean header = true; + + while ((line = in.readLine()) != null) { + if (header) { + if (!line.startsWith(TEN_DASH)) { + continue; + } + header = false; + } + if (line.startsWith(TEN_DASH)) { + if (!buffer.isEmpty()) { + ret.add(parse(buffer, errorsAsWarnings)); + } + buffer.clear(); + } else { + buffer.add(line); + } + } + } + return ret; + } + + private CompilerMessage parse(List buffer, boolean errorsAsWarnings) { + + Kind kind = null; + String file = null; + int lineNum = 0; + int startCol = 0; + int endCol = 0; + String message = null; + + // First line, kind, file, lineNum + if (buffer.size() > 1) { + String str = buffer.get(0); + Matcher matcher = levelFile.matcher(str); + if (matcher.find()) { + file = matcher.group(2); + kind = decodeKind(matcher.group(1), errorsAsWarnings); + lineNum = Integer.parseInt(matcher.group(3)); + } + } + + // Last line, message + if (buffer.size() >= 2) { + String str = buffer.get(buffer.size() - 1); + message = str.trim(); + } + + // 2nd to last line, columns + if (buffer.size() >= 3) { + String str = buffer.get(buffer.size() - 2); + startCol = str.indexOf('^'); + endCol = str.lastIndexOf('^'); + } + + return new CompilerMessage(file, kind, lineNum, startCol, lineNum, endCol, message); + } + + private Kind decodeKind(String str, boolean errorsAsWarnings) { + if (str.equals("ERROR")) { + return errorsAsWarnings ? Kind.WARNING : Kind.ERROR; + } + if (str.equals("INFO")) { + return Kind.NOTE; + } + return Kind.WARNING; + } +} diff --git a/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EclipseJavaCompiler.java b/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EclipseJavaCompiler.java index a45f8078..c7a01778 100644 --- a/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EclipseJavaCompiler.java +++ b/plexus-compilers/plexus-compiler-eclipse/src/main/java/org/codehaus/plexus/compiler/eclipse/EclipseJavaCompiler.java @@ -1,5 +1,3 @@ -package org.codehaus.plexus.compiler.eclipse; - /** * The MIT License *

@@ -23,6 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +package org.codehaus.plexus.compiler.eclipse; + import javax.inject.Named; import javax.inject.Singleton; import javax.tools.Diagnostic; @@ -32,6 +32,7 @@ import javax.tools.StandardJavaFileManager; import java.io.File; +import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.nio.charset.Charset; @@ -42,6 +43,7 @@ import java.util.Iterator; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Map.Entry; import java.util.ServiceLoader; @@ -198,6 +200,28 @@ public CompilerResult performCompile(CompilerConfiguration config) throws Compil } } + // Check for custom -log + boolean tempLog; + File logFile; + EcjLogParser logParser; + Map extras = config.getCustomCompilerArgumentsAsMap(); + if (extras.containsKey("-log")) { + String key = extras.get("-log"); + logFile = new File(key); + tempLog = false; + logParser = key.endsWith(".xml") ? new EcjResponseParser() : new EcjTextLogParser(); + } else { + try { + logFile = File.createTempFile("ecjerr-", ".xml"); + } catch (IOException e) { + throw new CompilerException("Unable to create temporary file for compiler output", e); + } + args.add("-log"); + args.add(logFile.toString()); + tempLog = true; + logParser = new EcjResponseParser(); + } + // -- classpath List classpathEntries = new ArrayList<>(config.getClasspathEntries()); classpathEntries.add(config.getOutputLocation()); @@ -343,14 +367,14 @@ public void worked(int i, int i1) {} }); log.debug(sw.toString()); - if (errorF.length() < 80) { + if (logFile.length() < 80) { throw new EcjFailureException(sw.toString()); } - messageList = new EcjResponseParser().parse(errorF, errorsAsWarnings); + messageList = logParser.parse(errorF, errorsAsWarnings); } finally { - if (null != errorF) { + if (tempLog) { try { - errorF.delete(); + logFile.delete(); } catch (Exception x) { } } @@ -358,14 +382,16 @@ public void worked(int i, int i1) {} } boolean hasError = false; for (CompilerMessage compilerMessage : messageList) { - if (compilerMessage.isError()) { + if (compilerMessage.isError() + || (compilerMessage.getKind() == CompilerMessage.Kind.WARNING + || compilerMessage.getKind() == CompilerMessage.Kind.MANDATORY_WARNING) + && config.isFailOnWarning()) { hasError = true; break; } } if (!hasError && !success && !errorsAsWarnings) { - CompilerMessage.Kind kind = - errorsAsWarnings ? CompilerMessage.Kind.WARNING : CompilerMessage.Kind.ERROR; + CompilerMessage.Kind kind = CompilerMessage.Kind.ERROR; // -- Compiler reported failure but we do not seem to have one -> probable // exception @@ -437,7 +463,7 @@ static boolean processCustomArguments(CompilerConfiguration config, List if (null != optionValue) { File propFile = new File(optionValue); if (!propFile.exists() || !propFile.isFile()) { - throw new IllegalArgumentException( + throw new EcjFailureException( "Properties file specified by -properties " + propFile + " does not exist"); } } diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test-input/src/main/org/codehaus/foo/Info.java b/plexus-compilers/plexus-compiler-eclipse/src/test-input/src/main/org/codehaus/foo/Info.java new file mode 100644 index 00000000..ae2a4249 --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/test-input/src/main/org/codehaus/foo/Info.java @@ -0,0 +1,7 @@ +package org.codehaus.foo; + +public class Info { + { + "".equals(Integer.valueOf(1)); + } +} diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/AbstractEclipseCompilerTest.java b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/AbstractEclipseCompilerTest.java new file mode 100644 index 00000000..e6f86290 --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/AbstractEclipseCompilerTest.java @@ -0,0 +1,113 @@ +/* + * The MIT License + * + * Copyright (c) 2005, The Codehaus + * + * 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 org.codehaus.plexus.compiler.eclipse; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; + +import org.codehaus.plexus.compiler.AbstractCompilerTest; +import org.codehaus.plexus.compiler.CompilerConfiguration; +import org.junit.jupiter.api.BeforeEach; + +import static org.codehaus.plexus.testing.PlexusExtension.getBasedir; + +/** + * @author Jason van Zyl + * @author Jason Faust + */ +public abstract class AbstractEclipseCompilerTest extends AbstractCompilerTest { + + private int expectedErrors; + private int expectedWarnings; + private int expectedNotes; + private Collection expectedOutputFiles; + + protected AbstractEclipseCompilerTest() { + this(5, 1, 1); + } + + protected AbstractEclipseCompilerTest(int expectedErrors, int expectedWarnings, int expectedNotes) { + this( + expectedErrors, + expectedWarnings, + expectedNotes, + Arrays.asList( + "org/codehaus/foo/Deprecation.class", + "org/codehaus/foo/ExternalDeps.class", + "org/codehaus/foo/Info.class", + "org/codehaus/foo/Person.class")); + } + + protected AbstractEclipseCompilerTest( + int expectedErrors, int expectedWarnings, int expectedNotes, Collection expectedOutputFiles) { + this.expectedErrors = expectedErrors; + this.expectedWarnings = expectedWarnings; + this.expectedNotes = expectedNotes; + this.expectedOutputFiles = expectedOutputFiles; + } + + @BeforeEach + public void setUp() throws Exception { + File compileDir = new File(getBasedir() + "/target/" + getRoleHint()); + if (!compileDir.exists() && !compileDir.mkdir()) { + throw new IOException("Unable to create compile target directory " + compileDir); + } + + setCompilerDebug(true); + setCompilerDeprecationWarnings(true); + } + + @Override + protected String getRoleHint() { + return "eclipse"; + } + + @Override + protected int expectedErrors() { + return expectedErrors; + } + + @Override + protected int expectedWarnings() { + return expectedWarnings; + } + + @Override + protected int expectedNotes() { + return expectedNotes; + } + + protected Collection expectedOutputFiles() { + return expectedOutputFiles; + } + + @Override + protected void configureCompilerConfig(CompilerConfiguration compilerConfig) { + compilerConfig.setSourceVersion("1.8"); + compilerConfig.setTargetVersion("1.8"); + // compilerConfig.setReleaseVersion("1.8"); + } +} diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EcjResponseParserTest.java b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EcjResponseParserTest.java new file mode 100644 index 00000000..17d6cb5b --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EcjResponseParserTest.java @@ -0,0 +1,173 @@ +/* + * The MIT License + * + * Copyright (c) 2005, The Codehaus + * + * 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 org.codehaus.plexus.compiler.eclipse; + +import java.io.File; +import java.util.List; + +import org.codehaus.plexus.compiler.CompilerMessage; +import org.codehaus.plexus.compiler.CompilerMessage.Kind; +import org.codehaus.plexus.testing.PlexusTest; +import org.junit.jupiter.api.Test; + +import static org.codehaus.plexus.testing.PlexusExtension.getTestFile; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests for {@link EcjResponseParser}. Tests are written against ecj 3.15.0 + * output. + * + * @author Jason Faust + * @since 2.14.0 + */ +@PlexusTest +public class EcjResponseParserTest { + + private static final String LOG_FILE_1 = "src/test/resources/org/codehaus/plexus/compiler/eclipse/3.15.0.log.xml"; + private static final String LOG_FILE_2 = "src/test/resources/org/codehaus/plexus/compiler/eclipse/log-xml-0.xml"; + private static final String LOG_FILE_3 = + "src/test/resources/org/codehaus/plexus/compiler/eclipse/eclipse-withinfo.xml"; + private static final String LOG_FILE_4 = + "src/test/resources/org/codehaus/plexus/compiler/eclipse/eclipse-columns.xml"; + + /** + * Test for example compiler output in XML format. + * + * @throws Exception on test failure. + */ + @Test + public void testParse() throws Exception { + File logFile = getTestFile(LOG_FILE_1); + assertTrue(logFile.exists(), "Input log file missing"); + + EcjResponseParser parser = new EcjResponseParser(); + List cm = parser.parse(logFile, false); + + assertEquals(9, cm.size(), "Wrong message count"); + + checkCompilerMessage(cm.get(0), "Bad.java", Kind.ERROR, 6, 1, 5); + checkCompilerMessage(cm.get(1), "Bad.java", Kind.ERROR, 6, 1, 5); + checkCompilerMessage(cm.get(2), "Deprecation.java", Kind.WARNING, 7, 5, 30); + checkCompilerMessage(cm.get(3), "ExternalDeps.java", Kind.ERROR, 4, 8, 17); + checkCompilerMessage(cm.get(4), "ExternalDeps.java", Kind.ERROR, 12, 21, 31); + checkCompilerMessage(cm.get(5), "Info.java", Kind.NOTE, 5, 11, 11); + checkCompilerMessage(cm.get(6), "ReservedWord.java", Kind.ERROR, 5, 8, 13); + checkCompilerMessage(cm.get(7), "UnknownSymbol.java", Kind.ERROR, 7, 1, 3); + checkCompilerMessage(cm.get(8), "WrongClassname.java", Kind.ERROR, 3, 14, 27); + } + + /** + * Test for example compiler output in XML format with errors reported as + * warnings. + * + * @throws Exception on test failure. + */ + @Test + public void testParseErrorsAsWarnings() throws Exception { + File logFile = getTestFile(LOG_FILE_1); + assertTrue(logFile.exists(), "Input log file missing"); + + EcjResponseParser parser = new EcjResponseParser(); + List cm = parser.parse(logFile, true); + + assertEquals(9, cm.size(), "Wrong message count"); + + checkCompilerMessage(cm.get(0), "Bad.java", Kind.WARNING, 6, 1, 5); + checkCompilerMessage(cm.get(1), "Bad.java", Kind.WARNING, 6, 1, 5); + checkCompilerMessage(cm.get(2), "Deprecation.java", Kind.WARNING, 7, 5, 30); + checkCompilerMessage(cm.get(3), "ExternalDeps.java", Kind.WARNING, 4, 8, 17); + checkCompilerMessage(cm.get(4), "ExternalDeps.java", Kind.WARNING, 12, 21, 31); + checkCompilerMessage(cm.get(5), "Info.java", Kind.NOTE, 5, 11, 11); + checkCompilerMessage(cm.get(6), "ReservedWord.java", Kind.WARNING, 5, 8, 13); + checkCompilerMessage(cm.get(7), "UnknownSymbol.java", Kind.WARNING, 7, 1, 3); + checkCompilerMessage(cm.get(8), "WrongClassname.java", Kind.WARNING, 3, 14, 27); + } + + /** + * Test for ignoring top level errors. + * + * @throws Exception on test failure. + */ + @Test + public void testHeaderErrors() throws Exception { + File logFile = getTestFile(LOG_FILE_2); + assertTrue(logFile.exists(), "Input log file missing"); + + EcjResponseParser parser = new EcjResponseParser(); + List cm = parser.parse(logFile, false); + + assertEquals(2, cm.size(), "Wrong message count"); + + checkCompilerMessage(cm.get(0), "Bad.java", Kind.ERROR, 6, 1, 5); + checkCompilerMessage(cm.get(1), "Bad.java", Kind.ERROR, 6, 1, 5); + } + + /** + * Tests for parsing the contents of {@value #LOG_FILE_3}. + * + * @throws Exception on test failure. + */ + public void testParse2() throws Exception { + File logFile = getTestFile(LOG_FILE_3); + assertTrue(logFile.exists(), "Input log file missing"); + + EcjResponseParser parser = new EcjResponseParser(); + List cm = parser.parse(logFile, false); + + assertEquals(6, cm.size(), "Wrong message count"); + + checkCompilerMessage(cm.get(0), "ECE.java", Kind.ERROR, 8, 13, 16); + checkCompilerMessage(cm.get(1), "ECE.java", Kind.ERROR, 16, 8, 40); + checkCompilerMessage(cm.get(2), "ECE.java", Kind.WARNING, 22, 9, 9); + checkCompilerMessage(cm.get(3), "ECE.java", Kind.WARNING, 27, 8, 40); + checkCompilerMessage(cm.get(4), "ECE.java", Kind.NOTE, 33, 13, 18); + checkCompilerMessage(cm.get(5), "ECE.java", Kind.NOTE, 35, 1, 95); + } + + /** + * Tests for parsing the contents of {@value #LOG_FILE_4}. + * + * @throws Exception on test failure. + */ + public void testParse4() throws Exception { + File logFile = getTestFile(LOG_FILE_4); + assertTrue(logFile.exists(), "Input log file missing"); + + EcjResponseParser parser = new EcjResponseParser(); + List cm = parser.parse(logFile, false); + + assertEquals(1, cm.size(), "Wrong message count"); + + checkCompilerMessage(cm.get(0), "Column.java", Kind.ERROR, 2, 1, 5); + } + + private void checkCompilerMessage(CompilerMessage cm, String file, Kind kind, int line, int startCol, int endCol) { + assertTrue(cm.getFile().endsWith(file), "Failure checking output for " + file); + assertEquals(kind, cm.getKind(), "Failure checking output for " + file); + assertEquals(line, cm.getStartLine(), "Failure checking output for " + file); + assertEquals(startCol, cm.getStartColumn(), "Failure checking output for " + file); + assertEquals(endCol, cm.getEndColumn(), "Failure checking output for " + file); + } +} diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EcjTextLogParserTest.java b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EcjTextLogParserTest.java new file mode 100644 index 00000000..850ca51c --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EcjTextLogParserTest.java @@ -0,0 +1,175 @@ +/* + * The MIT License + * + * Copyright (c) 2005, The Codehaus + * + * 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 org.codehaus.plexus.compiler.eclipse; + +import java.io.File; +import java.util.List; + +import org.codehaus.plexus.compiler.CompilerMessage; +import org.codehaus.plexus.compiler.CompilerMessage.Kind; +import org.codehaus.plexus.testing.PlexusTest; +import org.junit.jupiter.api.Test; + +import static org.codehaus.plexus.testing.PlexusExtension.getTestFile; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests for {@link EcjTextLogParser}. Tests are written against ecj 3.15.0 + * output. + * + * @author Jason Faust + * @since 2.14.0 + */ +@PlexusTest +public class EcjTextLogParserTest { + + private static final String LOG_FILE_1 = "src/test/resources/org/codehaus/plexus/compiler/eclipse/3.15.0.log.txt"; + private static final String LOG_FILE_2 = "src/test/resources/org/codehaus/plexus/compiler/eclipse/log-text-0.log"; + private static final String LOG_FILE_3 = + "src/test/resources/org/codehaus/plexus/compiler/eclipse/eclipse-withinfo.txt"; + private static final String LOG_FILE_4 = + "src/test/resources/org/codehaus/plexus/compiler/eclipse/eclipse-columns.txt"; + + /** + * Test for example compiler output in non-XML format. + * + * @throws Exception on test failure. + */ + @Test + public void testParse() throws Exception { + File logFile = getTestFile(LOG_FILE_1); + assertTrue(logFile.exists(), "Input log file missing"); + + EcjTextLogParser parser = new EcjTextLogParser(); + List cm = parser.parse(logFile, false); + + assertEquals(9, cm.size(), "Wrong message count"); + + checkCompilerMessage(cm.get(0), "Bad.java", Kind.ERROR, 6, 1, 5); + checkCompilerMessage(cm.get(1), "Bad.java", Kind.ERROR, 6, 1, 5); + checkCompilerMessage(cm.get(2), "Deprecation.java", Kind.WARNING, 7, 5, 30); + checkCompilerMessage(cm.get(3), "ExternalDeps.java", Kind.ERROR, 4, 8, 17); + checkCompilerMessage(cm.get(4), "ExternalDeps.java", Kind.ERROR, 12, 21, 31); + checkCompilerMessage(cm.get(5), "Info.java", Kind.NOTE, 5, 11, 11); + checkCompilerMessage(cm.get(6), "ReservedWord.java", Kind.ERROR, 5, 8, 13); + checkCompilerMessage(cm.get(7), "UnknownSymbol.java", Kind.ERROR, 7, 1, 3); + checkCompilerMessage(cm.get(8), "WrongClassname.java", Kind.ERROR, 3, 14, 27); + } + + /** + * Test for example compiler output in non-XML format with errors reported as + * warnings. + * + * @throws Exception on test failure. + */ + @Test + public void testParseErrorsAsWarnings() throws Exception { + File logFile = getTestFile(LOG_FILE_1); + assertTrue(logFile.exists(), "Input log file missing"); + + EcjTextLogParser parser = new EcjTextLogParser(); + List cm = parser.parse(logFile, true); + + assertEquals(9, cm.size(), "Wrong message count"); + + checkCompilerMessage(cm.get(0), "Bad.java", Kind.WARNING, 6, 1, 5); + checkCompilerMessage(cm.get(1), "Bad.java", Kind.WARNING, 6, 1, 5); + checkCompilerMessage(cm.get(2), "Deprecation.java", Kind.WARNING, 7, 5, 30); + checkCompilerMessage(cm.get(3), "ExternalDeps.java", Kind.WARNING, 4, 8, 17); + checkCompilerMessage(cm.get(4), "ExternalDeps.java", Kind.WARNING, 12, 21, 31); + checkCompilerMessage(cm.get(5), "Info.java", Kind.NOTE, 5, 11, 11); + checkCompilerMessage(cm.get(6), "ReservedWord.java", Kind.WARNING, 5, 8, 13); + checkCompilerMessage(cm.get(7), "UnknownSymbol.java", Kind.WARNING, 7, 1, 3); + checkCompilerMessage(cm.get(8), "WrongClassname.java", Kind.WARNING, 3, 14, 27); + } + + /** + * Test for ignoring output before first output delimiter. + * + * @throws Exception on test failure. + */ + @Test + public void testHeaderErrors() throws Exception { + File logFile = getTestFile(LOG_FILE_2); + assertTrue(logFile.exists(), "Input log file missing"); + + EcjTextLogParser parser = new EcjTextLogParser(); + List cm = parser.parse(logFile, false); + + assertEquals(2, cm.size(), "Wrong message count"); + + checkCompilerMessage(cm.get(0), "Bad.java", Kind.ERROR, 6, 1, 5); + checkCompilerMessage(cm.get(1), "Bad.java", Kind.ERROR, 6, 1, 5); + } + + /** + * Tests for parsing the contents of {@value #LOG_FILE_3}. + * + * @throws Exception on test failure. + */ + @Test + public void testParse3() throws Exception { + File logFile = getTestFile(LOG_FILE_3); + assertTrue(logFile.exists(), "Input log file missing"); + + EcjTextLogParser parser = new EcjTextLogParser(); + List cm = parser.parse(logFile, false); + + assertEquals(6, cm.size(), "Wrong message count"); + + checkCompilerMessage(cm.get(0), "ECE.java", Kind.ERROR, 8, 13, 16); + checkCompilerMessage(cm.get(1), "ECE.java", Kind.ERROR, 16, 8, 40); + checkCompilerMessage(cm.get(2), "ECE.java", Kind.WARNING, 22, 9, 9); + checkCompilerMessage(cm.get(3), "ECE.java", Kind.WARNING, 27, 8, 40); + checkCompilerMessage(cm.get(4), "ECE.java", Kind.NOTE, 33, 13, 18); + checkCompilerMessage(cm.get(5), "ECE.java", Kind.NOTE, 35, 1, 95); + } + + /** + * Tests for parsing the contents of {@value #LOG_FILE_4}. + * + * @throws Exception on test failure. + */ + @Test + public void testParse4() throws Exception { + File logFile = getTestFile(LOG_FILE_4); + assertTrue(logFile.exists(), "Input log file missing"); + + EcjTextLogParser parser = new EcjTextLogParser(); + List cm = parser.parse(logFile, false); + + assertEquals(1, cm.size(), "Wrong message count"); + + checkCompilerMessage(cm.get(0), "Column.java", Kind.ERROR, 2, 1, 5); + } + + private void checkCompilerMessage(CompilerMessage cm, String file, Kind kind, int line, int startCol, int endCol) { + assertTrue(cm.getFile().endsWith(file), "Failure checking output for " + file); + assertEquals(kind, cm.getKind(), "Failure checking output for " + file); + assertEquals(line, cm.getStartLine(), "Failure checking output for " + file); + assertEquals(startCol, cm.getStartColumn(), "Failure checking output for " + file); + assertEquals(endCol, cm.getEndColumn(), "Failure checking output for " + file); + } +} diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerBasicTest.java b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerBasicTest.java new file mode 100644 index 00000000..847b1d30 --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerBasicTest.java @@ -0,0 +1,33 @@ +/* + * The MIT License + * + * Copyright (c) 2005, The Codehaus + * + * 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 org.codehaus.plexus.compiler.eclipse; + +/** + * @author Jason van Zyl + * @author Jason Faust + * @since 2.14.0 + */ +public class EclipseCompilerBasicTest extends AbstractEclipseCompilerTest { + // Empty +} diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerConfigurationTest.java b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerConfigurationTest.java index 607c7141..0e29a81d 100644 --- a/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerConfigurationTest.java +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerConfigurationTest.java @@ -1,5 +1,3 @@ -package org.codehaus.plexus.compiler.eclipse; - /** * The MIT License * @@ -23,6 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +package org.codehaus.plexus.compiler.eclipse; + import java.io.File; import java.util.ArrayList; import java.util.Collections; @@ -35,6 +35,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; public class EclipseCompilerConfigurationTest { @@ -98,10 +99,12 @@ public void testProcessCustomArgumentsWithErrorsAsWarningsAndMinus() { @Test public void testProcessCustomArgumentsWithPropertiesAndNonExistingFile() { configuration.addCompilerCustomArgument("-properties", "fooBar.txt"); - IllegalArgumentException expected = Assertions.assertThrows( - IllegalArgumentException.class, + EcjFailureException expected = Assertions.assertThrows( + EcjFailureException.class, () -> EclipseJavaCompiler.processCustomArguments(configuration, Collections.emptyList())); - assertThat(expected.getMessage(), is("Properties file specified by -properties fooBar.txt does not exist")); + assertThat( + expected.getMessage(), + containsString("Properties file specified by -properties fooBar.txt does not exist")); } @Test diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerCustomArgumentTest.java b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerCustomArgumentTest.java new file mode 100644 index 00000000..2fb47283 --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerCustomArgumentTest.java @@ -0,0 +1,60 @@ +/* + * The MIT License + * + * Copyright (c) 2005, The Codehaus + * + * 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 org.codehaus.plexus.compiler.eclipse; + +import java.util.Collections; + +import org.codehaus.plexus.compiler.CompilerConfiguration; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author Jason van Zyl + * @author Jason Faust + */ +public class EclipseCompilerCustomArgumentTest extends AbstractEclipseCompilerTest { + + @SuppressWarnings("unchecked") + public EclipseCompilerCustomArgumentTest() { + super(0, 0, 0, Collections.EMPTY_LIST); + } + + @Override + protected void configureCompilerConfig(CompilerConfiguration compilerConfig) { + super.configureCompilerConfig(compilerConfig); + compilerConfig.addCompilerCustomArgument("-key", "value"); + } + + @Test + @Override + public void testCompilingSources() throws Exception { + EcjFailureException exception = assertThrows( + EcjFailureException.class, super::testCompilingSources, "Compile should of thrown an exception"); + assertTrue( + exception.getMessage().startsWith("Failed to run the ecj compiler: Unrecognized option : -key"), + "Unexpected compiler error"); + } +} diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerDashedArgumentsTest.java b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerDashedArgumentsTest.java index 3fc3dd6f..c7a8b003 100644 --- a/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerDashedArgumentsTest.java +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerDashedArgumentsTest.java @@ -45,7 +45,7 @@ /** * @author Frits Jalvingh - * Created on 22-4-18. + * @author Jason Faust */ @PlexusTest public class EclipseCompilerDashedArgumentsTest { diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerErrorsAsWarningsTest.java b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerErrorsAsWarningsTest.java index 91580922..aeba9dcb 100644 --- a/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerErrorsAsWarningsTest.java +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerErrorsAsWarningsTest.java @@ -1,16 +1,18 @@ package org.codehaus.plexus.compiler.eclipse; -import java.util.Arrays; -import java.util.Collection; - -import org.codehaus.plexus.compiler.AbstractCompilerTest; import org.codehaus.plexus.compiler.CompilerConfiguration; import org.junit.jupiter.api.BeforeEach; -public class EclipseCompilerErrorsAsWarningsTest extends AbstractCompilerTest { +/** + * Test for errors being reported as warnings. + * + * @author Jason Faust + * @since 2.14.0 + */ +public class EclipseCompilerErrorsAsWarningsTest extends AbstractEclipseCompilerTest { - protected void configureCompilerConfig(CompilerConfiguration compilerConfig) { - compilerConfig.addCompilerCustomArgument("-errorsAsWarnings", "true"); + public EclipseCompilerErrorsAsWarningsTest() { + super(0, 6, 1); } @BeforeEach @@ -24,27 +26,8 @@ protected String getRoleHint() { return "eclipse"; } - @Override - protected int expectedErrors() { - return 0; - } - - @Override - protected int expectedWarnings() { - return 6; - } - - @Override - protected Collection expectedOutputFiles() { - return Arrays.asList( - "org/codehaus/foo/Deprecation.class", - "org/codehaus/foo/ExternalDeps.class", - "org/codehaus/foo/Person.class", - "org/codehaus/foo/ReservedWord.class" - // "org/codehaus/foo/Bad.class", // This one has no class file generated as it's one big - // issue - // "org/codehaus/foo/UnknownSymbol.class", - // "org/codehaus/foo/RightClassname.class" - ); + protected void configureCompilerConfig(CompilerConfiguration compilerConfig) { + super.configureCompilerConfig(compilerConfig); + compilerConfig.addCompilerCustomArgument("-errorsAsWarnings", "true"); } } diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerFailOnWarningsTest.java b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerFailOnWarningsTest.java index c5a94b3d..aee91957 100644 --- a/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerFailOnWarningsTest.java +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerFailOnWarningsTest.java @@ -1,13 +1,25 @@ package org.codehaus.plexus.compiler.eclipse; import java.util.Arrays; -import java.util.Collection; -import org.codehaus.plexus.compiler.AbstractCompilerTest; import org.codehaus.plexus.compiler.CompilerConfiguration; -public class EclipseCompilerFailOnWarningsTest extends AbstractCompilerTest { +public class EclipseCompilerFailOnWarningsTest extends AbstractEclipseCompilerTest { + + public EclipseCompilerFailOnWarningsTest() { + super( + 4, + 2, + 1, + Arrays.asList( + "org/codehaus/foo/Deprecation.class", + "org/codehaus/foo/ExternalDeps.class", + "org/codehaus/foo/Info.class", + "org/codehaus/foo/Person.class", + "org/codehaus/foo/ReservedWord.class")); + } + @Override protected void configureCompilerConfig(CompilerConfiguration compilerConfig) { compilerConfig.setFailOnWarning(true); } @@ -16,23 +28,4 @@ protected void configureCompilerConfig(CompilerConfiguration compilerConfig) { protected String getRoleHint() { return "eclipse"; } - - @Override - protected int expectedErrors() { - return 6; - } - - @Override - protected int expectedWarnings() { - return 1; - } - - @Override - protected Collection expectedOutputFiles() { - return Arrays.asList( - "org/codehaus/foo/Deprecation.class", - "org/codehaus/foo/ExternalDeps.class", - "org/codehaus/foo/Person.class", - "org/codehaus/foo/ReservedWord.class"); - } } diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerInitializeWarningsForPropertiesArgumentTest.java b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerInitializeWarningsForPropertiesArgumentTest.java new file mode 100644 index 00000000..0c51f2c5 --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerInitializeWarningsForPropertiesArgumentTest.java @@ -0,0 +1,62 @@ +/* + * The MIT License + * + * Copyright (c) 2005, The Codehaus + * + * 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 org.codehaus.plexus.compiler.eclipse; + +import java.util.Collections; + +import org.codehaus.plexus.compiler.CompilerConfiguration; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author Jason van Zyl + * @author Jason Faust + */ +public class EclipseCompilerInitializeWarningsForPropertiesArgumentTest extends AbstractEclipseCompilerTest { + + @SuppressWarnings("unchecked") + public EclipseCompilerInitializeWarningsForPropertiesArgumentTest() { + super(0, 0, 0, Collections.EMPTY_LIST); + } + + @Override + protected void configureCompilerConfig(CompilerConfiguration compilerConfig) { + super.configureCompilerConfig(compilerConfig); + compilerConfig.addCompilerCustomArgument("-properties", "file_does_not_exist"); + } + + @Test + @Override + public void testCompilingSources() throws Exception { + EcjFailureException exception = assertThrows( + EcjFailureException.class, + super::testCompilingSources, + "looking up the properties file should have thrown an exception"); + assertTrue( + exception.getMessage().startsWith("Failed to run the ecj compiler: Properties file"), + "Unexpected compiler error"); + } +} diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerTest.java b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerTest.java index de72e6b4..e75d6c46 100644 --- a/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerTest.java +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerTest.java @@ -23,22 +23,21 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -import java.util.Arrays; -import java.util.Collection; - -import org.codehaus.plexus.compiler.AbstractCompilerTest; import org.codehaus.plexus.compiler.CompilerConfiguration; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.startsWith; +import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.assertThrows; /** * @author Jason van Zyl */ -public class EclipseCompilerTest extends AbstractCompilerTest { +public class EclipseCompilerTest extends AbstractEclipseCompilerTest { + public EclipseCompilerTest() { + super(5, 1, 1); + } @BeforeEach public void setUp() { @@ -51,25 +50,6 @@ protected String getRoleHint() { return "eclipse"; } - @Override - protected int expectedErrors() { - return 4; - } - - @Override - protected int expectedWarnings() { - return 2; - } - - @Override - protected Collection expectedOutputFiles() { - return Arrays.asList( - "org/codehaus/foo/Deprecation.class", - "org/codehaus/foo/ExternalDeps.class", - "org/codehaus/foo/Person.class", - "org/codehaus/foo/ReservedWord.class"); - } - // The test is fairly meaningless as we can not validate anything @Test public void testCustomArgument() throws Exception { @@ -86,9 +66,9 @@ public void testInitializeWarningsForPropertiesArgument() { compilerConfig.addCompilerCustomArgument("-properties", "file_does_not_exist"); - IllegalArgumentException e = - assertThrows(IllegalArgumentException.class, () -> getCompiler().performCompile(compilerConfig)); - assertThat("Message must start with 'Properties file'", e.getMessage(), startsWith("Properties file")); + EcjFailureException e = + assertThrows(EcjFailureException.class, () -> getCompiler().performCompile(compilerConfig)); + assertThat("Message must start with 'Properties file'", e.getMessage(), containsString("Properties file")); } private CompilerConfiguration createMinimalCompilerConfig() { diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerTextLogTest.java b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerTextLogTest.java new file mode 100644 index 00000000..447703c0 --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerTextLogTest.java @@ -0,0 +1,63 @@ +/* + * The MIT License + * + * Copyright (c) 2005, The Codehaus + * + * 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 org.codehaus.plexus.compiler.eclipse; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.codehaus.plexus.compiler.CompilerConfiguration; +import org.junit.jupiter.api.Test; + +import static org.codehaus.plexus.testing.PlexusExtension.getTestPath; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test of a XML user log specified. + * + * @author Jason Faust + * @since 2.14.0 + */ +public class EclipseCompilerTextLogTest extends AbstractEclipseCompilerTest { + + private List logFiles = new ArrayList<>(); + private int idx = 0; + + @Override + protected void configureCompilerConfig(CompilerConfiguration compilerConfig, String filename) { + super.configureCompilerConfig(compilerConfig, filename); + String logFile = getTestPath("/target/" + getRoleHint() + "/log-text-" + idx + ".log"); + compilerConfig.addCompilerCustomArgument("-log", logFile); + idx++; + } + + @Test + @Override + public void testCompilingSources() throws Exception { + super.testCompilingSources(); + for (String file : logFiles) { + assertTrue(new File(file).exists()); + } + } +} diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerXMLLogTest.java b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerXMLLogTest.java new file mode 100644 index 00000000..f40d2a87 --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/java/org/codehaus/plexus/compiler/eclipse/EclipseCompilerXMLLogTest.java @@ -0,0 +1,63 @@ +/* + * The MIT License + * + * Copyright (c) 2005, The Codehaus + * + * 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 org.codehaus.plexus.compiler.eclipse; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.codehaus.plexus.compiler.CompilerConfiguration; +import org.junit.jupiter.api.Test; + +import static org.codehaus.plexus.testing.PlexusExtension.getTestPath; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Test of a XML user log specified. + * + * @author Jason Faust + * @since 2.14.0 + */ +public class EclipseCompilerXMLLogTest extends AbstractEclipseCompilerTest { + + private List logFiles = new ArrayList<>(); + private int idx = 0; + + @Override + protected void configureCompilerConfig(CompilerConfiguration compilerConfig, String filename) { + super.configureCompilerConfig(compilerConfig, filename); + String logFile = getTestPath("/target/" + getRoleHint() + "/log-xml-" + idx + ".xml"); + compilerConfig.addCompilerCustomArgument("-log", logFile); + idx++; + } + + @Test + @Override + public void testCompilingSources() throws Exception { + super.testCompilingSources(); + for (String file : logFiles) { + assertTrue(new File(file).exists()); + } + } +} diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/3.15.0.log.txt b/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/3.15.0.log.txt new file mode 100644 index 00000000..cb9ce953 --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/3.15.0.log.txt @@ -0,0 +1,55 @@ +# 9/26/18 1:10:25 PM EDT +# Eclipse Compiler for Java(TM) v20180905-0317, 3.15.0, Copyright IBM Corp 2000, 2015. All rights reserved. +---------- +1. ERROR in C:\devenv\gitrepo\plexus-compiler\plexus-compilers\plexus-compiler-eclipse\src\test-input\src\main\org\codehaus\foo\Bad.java (at line 6) + pubic String name; + ^^^^^ +Syntax error on token "pubic", public expected +---------- +2. ERROR in C:\devenv\gitrepo\plexus-compiler\plexus-compilers\plexus-compiler-eclipse\src\test-input\src\main\org\codehaus\foo\Bad.java (at line 6) + pubic String name; + ^^^^^ +pubic cannot be resolved to a type +---------- +---------- +3. WARNING in C:\devenv\gitrepo\plexus-compiler\plexus-compilers\plexus-compiler-eclipse\src\test-input\src\main\org\codehaus\foo\Deprecation.java (at line 7) + new java.util.Date("testDate"); + ^^^^^^^^^^^^^^^^^^^^^^^^^^ +The constructor Date(String) is deprecated +---------- +---------- +4. ERROR in C:\devenv\gitrepo\plexus-compiler\plexus-compilers\plexus-compiler-eclipse\src\test-input\src\main\org\codehaus\foo\ExternalDeps.java (at line 4) + import org.apache.commons.lang.StringUtils; + ^^^^^^^^^^ +The import org.apache cannot be resolved +---------- +5. ERROR in C:\devenv\gitrepo\plexus-compiler\plexus-compilers\plexus-compiler-eclipse\src\test-input\src\main\org\codehaus\foo\ExternalDeps.java (at line 12) + System.out.println( StringUtils.upperCase( str) ); + ^^^^^^^^^^^ +StringUtils cannot be resolved +---------- +---------- +6. INFO in C:\devenv\gitrepo\plexus-compiler\plexus-compilers\plexus-compiler-eclipse\src\test-input\src\main\org\codehaus\foo\Info.java (at line 5) + "".equals(1); + ^ +Unlikely argument type for equals(): int seems to be unrelated to String +---------- +---------- +7. ERROR in C:\devenv\gitrepo\plexus-compiler\plexus-compilers\plexus-compiler-eclipse\src\test-input\src\main\org\codehaus\foo\ReservedWord.java (at line 5) + String assert; + ^^^^^^ +Syntax error on token "assert", invalid VariableDeclarator +---------- +---------- +8. ERROR in C:\devenv\gitrepo\plexus-compiler\plexus-compilers\plexus-compiler-eclipse\src\test-input\src\main\org\codehaus\foo\UnknownSymbol.java (at line 7) + foo(); + ^^^ +The method foo() is undefined for the type UnknownSymbol +---------- +---------- +9. ERROR in C:\devenv\gitrepo\plexus-compiler\plexus-compilers\plexus-compiler-eclipse\src\test-input\src\main\org\codehaus\foo\WrongClassname.java (at line 3) + public class RightClassname + ^^^^^^^^^^^^^^ +The public type RightClassname must be defined in its own file +---------- +9 problems (7 errors, 1 warning, 1 info) diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/3.15.0.log.xml b/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/3.15.0.log.xml new file mode 100644 index 00000000..c1b34d7c --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/3.15.0.log.xml @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/eclipse-columns.txt b/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/eclipse-columns.txt new file mode 100644 index 00000000..d3118a14 --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/eclipse-columns.txt @@ -0,0 +1,9 @@ +# 10/29/18, 3:53:36 PM EDT +# Eclipse Compiler for Java(TM) v20180905-0317, 3.15.0, Copyright IBM Corp 2000, 2015. All rights reserved. +---------- +1. ERROR in C:\TEMP\Column.java (at line 2) + 12345 + ^^^^^ +Syntax error on token "12345", delete this token +---------- +1 problem (1 error) diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/eclipse-columns.xml b/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/eclipse-columns.xml new file mode 100644 index 00000000..219f63f7 --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/eclipse-columns.xml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/eclipse-withinfo.txt b/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/eclipse-withinfo.txt new file mode 100644 index 00000000..bc7102ad --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/eclipse-withinfo.txt @@ -0,0 +1,42 @@ +# 10/15/18, 2:50:59 PM EDT +# Eclipse Compiler for Java(TM) v20180905-0317, 3.15.0, Copyright IBM Corp 2000, 2015. All rights reserved. +---------- +1. ERROR in C:\devenv\workspace\x\y\src\main\java\y\ECE.java (at line 8) + Integer x = 0.0f; + ^^^^ +Type mismatch: cannot convert from float to Integer +---------- +2. ERROR in C:\devenv\workspace\x\y\src\main\java\y\ECE.java (at line 16) + } else { + x = 20; + } + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Dead code +---------- +3. WARNING in C:\devenv\workspace\x\y\src\main\java\y\ECE.java (at line 22) + Integer x; + ^ +The value of the local variable x is not used +---------- +4. WARNING in C:\devenv\workspace\x\y\src\main\java\y\ECE.java (at line 27) + } else { + x = 30; + } + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Statement unnecessarily nested within else clause. The corresponding then clause does not complete normally +---------- +5. INFO in C:\devenv\workspace\x\y\src\main\java\y\ECE.java (at line 33) + Boolean x = 7 == 7; + ^^^^^^ +Comparing identical expressions +---------- +6. INFO in C:\devenv\workspace\x\y\src\main\java\y\ECE.java (at line 35) + new Object() { + { + System.out.println(x); + } + }; + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The allocated object is never used +---------- +6 problems (2 errors, 2 warnings, 2 info) diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/eclipse-withinfo.xml b/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/eclipse-withinfo.xml new file mode 100644 index 00000000..0204c553 --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/eclipse-withinfo.xml @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/log-text-0.log b/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/log-text-0.log new file mode 100644 index 00000000..e492cce0 --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/log-text-0.log @@ -0,0 +1,15 @@ +# 9/26/18 4:37:25 PM EDT +# Eclipse Compiler for Java(TM) v20180905-0317, 3.15.0, Copyright IBM Corp 2000, 2015. All rights reserved. +incorrect classpath: C:\devenv\gitrepo\plexus-compiler\plexus-compilers\plexus-compiler-eclipse/target/eclipse/classes-0 +---------- +1. ERROR in C:\devenv\gitrepo\plexus-compiler\plexus-compilers\plexus-compiler-eclipse\src\test-input\src\main\org\codehaus\foo\Bad.java (at line 6) + pubic String name; + ^^^^^ +Syntax error on token "pubic", public expected +---------- +2. ERROR in C:\devenv\gitrepo\plexus-compiler\plexus-compilers\plexus-compiler-eclipse\src\test-input\src\main\org\codehaus\foo\Bad.java (at line 6) + pubic String name; + ^^^^^ +pubic cannot be resolved to a type +---------- +2 problems (2 errors) diff --git a/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/log-xml-0.xml b/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/log-xml-0.xml new file mode 100644 index 00000000..9d1ed77a --- /dev/null +++ b/plexus-compilers/plexus-compiler-eclipse/src/test/resources/org/codehaus/plexus/compiler/eclipse/log-xml-0.xml @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +