You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by sr...@apache.org on 2019/12/06 15:07:59 UTC

[plc4x] 02/07: added some tests for expression parser and WIP for MFL and MFP

This is an automated email from the ASF dual-hosted git repository.

sruehl pushed a commit to branch feature/ams_ads_mpsec
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit c76ff54090f8b8385a9c75f172398977b5241ee8
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Fri Nov 29 11:33:36 2019 +0100

    added some tests for expression parser and WIP for MFL and MFP
---
 build-utils/protocol-base-mspec/pom.xml            |  36 +++++
 .../mspec/parser/MessageFormatListener.java        |  16 +-
 .../language/mspec/parser/MessageFormatParser.java |  54 +++++--
 .../expression/ExpressionStringParserTest.java     | 178 +++++++++++++++++++++
 .../mspec/parser/MessageFormatListenerTest.java    | 162 +++++++++++++++++++
 .../mspec/parser/MessageFormatParserTest.java      |  88 ++++++++++
 6 files changed, 517 insertions(+), 17 deletions(-)

diff --git a/build-utils/protocol-base-mspec/pom.xml b/build-utils/protocol-base-mspec/pom.xml
index db361a8..a9096c2 100644
--- a/build-utils/protocol-base-mspec/pom.xml
+++ b/build-utils/protocol-base-mspec/pom.xml
@@ -64,6 +64,42 @@
       <groupId>org.antlr</groupId>
       <artifactId>antlr4-runtime</artifactId>
     </dependency>
+
+    <!-- Testing -->
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-api</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-params</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-engine</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.junit.vintage</groupId>
+      <artifactId>junit-vintage-engine</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-junit-jupiter</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.hamcrest</groupId>
+      <artifactId>hamcrest-library</artifactId>
+    </dependency>
   </dependencies>
 
 </project>
\ No newline at end of file
diff --git a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatListener.java b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatListener.java
index 74ff1cf..42c3ce3 100644
--- a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatListener.java
+++ b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatListener.java
@@ -55,6 +55,14 @@ public class MessageFormatListener extends MSpecBaseListener {
 
     private Map<String, TypeDefinition> types;
 
+    public Deque<List<Field>> getParserContexts() {
+        return parserContexts;
+    }
+
+    public Deque<List<EnumValue>> getEnumContexts() {
+        return enumContexts;
+    }
+
     public Map<String, TypeDefinition> getTypes() {
         return types;
     }
@@ -458,12 +466,4 @@ public class MessageFormatListener extends MSpecBaseListener {
         return quotedString;
     }
 
-    public static void main(String[] args) {
-        //Term term = new ExpressionStringParser().parse(IOUtils.toInputStream("CAST(CAST(parameter,S7ParameterUserData).items(hurz)[0],S7ParameterUserDataItemCPUFunctions).cpuFunctionType", Charset.defaultCharset()));
-        //Term term = new ExpressionStringParser().parse(IOUtils.toInputStream("CAST(parameter,S7ParameterUserData).items(hurz)[0]", Charset.defaultCharset()));
-        Term term = new ExpressionStringParser().parse(IOUtils.toInputStream("payload.lengthInBytes+4", Charset.defaultCharset()));
-
-        System.out.println(term);
-    }
-
 }
diff --git a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatParser.java b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatParser.java
index bdd99fb..a134666 100644
--- a/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatParser.java
+++ b/build-utils/protocol-base-mspec/src/main/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatParser.java
@@ -19,6 +19,7 @@
 
 package org.apache.plc4x.plugins.codegenerator.language.mspec.parser;
 
+import org.antlr.v4.runtime.CharStream;
 import org.antlr.v4.runtime.CharStreams;
 import org.antlr.v4.runtime.CommonTokenStream;
 import org.antlr.v4.runtime.tree.ParseTree;
@@ -27,24 +28,59 @@ import org.apache.plc4x.plugins.codegenerator.language.mspec.MSpecLexer;
 import org.apache.plc4x.plugins.codegenerator.language.mspec.MSpecParser;
 import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
 
+import java.io.IOException;
 import java.io.InputStream;
 import java.util.Map;
+import java.util.function.Function;
+import java.util.function.Supplier;
 
 public class MessageFormatParser {
 
+    // TODO: can be used a instance once thread safety is asserted
+    private Function<CharStream, MSpecLexer> lexerBuilder;
+
+    // TODO: can be used a instance once thread safety is asserted
+    private Function<MSpecLexer, CommonTokenStream> commonTokenStreamBuilder;
+
+    // TODO: can be used a instance once thread safety is asserted
+    private Function<CommonTokenStream, MSpecParser> commonTokenStreamMSpecParserBuilder;
+
+    // TODO: can be used a instance once thread safety is asserted
+    private Supplier<ParseTreeWalker> voidParseTreeWalkerBuilder;
+
+    // TODO: can be used a instance once thread safety is asserted
+    private Supplier<MessageFormatListener> messageFormatListenerBuilder;
+
+    public MessageFormatParser() {
+        lexerBuilder = MSpecLexer::new;
+        commonTokenStreamBuilder = CommonTokenStream::new;
+        commonTokenStreamMSpecParserBuilder = MSpecParser::new;
+        voidParseTreeWalkerBuilder = ParseTreeWalker::new;
+        messageFormatListenerBuilder = MessageFormatListener::new;
+    }
+
+    MessageFormatParser(Function<CharStream, MSpecLexer> lexerBuilder, Function<MSpecLexer, CommonTokenStream> commonTokenStreamBuilder, Function<CommonTokenStream, MSpecParser> commonTokenStreamMSpecParserBuilder, Supplier<ParseTreeWalker> voidParseTreeWalkerBuilder, Supplier<MessageFormatListener> messageFormatListenerBuilder) {
+        this.lexerBuilder = lexerBuilder;
+        this.commonTokenStreamBuilder = commonTokenStreamBuilder;
+        this.commonTokenStreamMSpecParserBuilder = commonTokenStreamMSpecParserBuilder;
+        this.voidParseTreeWalkerBuilder = voidParseTreeWalkerBuilder;
+        this.messageFormatListenerBuilder = messageFormatListenerBuilder;
+    }
+
     public Map<String, TypeDefinition> parse(InputStream source) {
+        MSpecLexer lexer;
         try {
-            MSpecLexer lexer = new MSpecLexer(CharStreams.fromStream(source));
-            CommonTokenStream tokens = new CommonTokenStream(lexer);
-            MSpecParser parser = new MSpecParser(tokens);
-            ParseTree tree = parser.file();
-            ParseTreeWalker walker = new ParseTreeWalker();
-            MessageFormatListener listener = new MessageFormatListener();
-            walker.walk(listener, tree);
-            return listener.getTypes();
-        } catch (Exception e) {
+            lexer = lexerBuilder.apply(CharStreams.fromStream(source));
+        } catch (IOException e) {
             throw new RuntimeException(e);
         }
+        CommonTokenStream tokens = commonTokenStreamBuilder.apply(lexer);
+        MSpecParser parser = commonTokenStreamMSpecParserBuilder.apply(tokens);
+        ParseTree tree = parser.file();
+        ParseTreeWalker walker = voidParseTreeWalkerBuilder.get();
+        MessageFormatListener listener = messageFormatListenerBuilder.get();
+        walker.walk(listener, tree);
+        return listener.getTypes();
     }
 
 }
diff --git a/build-utils/protocol-base-mspec/src/test/java/org/apache/plc4x/plugins/codegenerator/language/mspec/expression/ExpressionStringParserTest.java b/build-utils/protocol-base-mspec/src/test/java/org/apache/plc4x/plugins/codegenerator/language/mspec/expression/ExpressionStringParserTest.java
new file mode 100644
index 0000000..71c0222
--- /dev/null
+++ b/build-utils/protocol-base-mspec/src/test/java/org/apache/plc4x/plugins/codegenerator/language/mspec/expression/ExpressionStringParserTest.java
@@ -0,0 +1,178 @@
+/*
+ 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.plc4x.plugins.codegenerator.language.mspec.expression;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.plc4x.plugins.codegenerator.types.terms.BinaryTerm;
+import org.apache.plc4x.plugins.codegenerator.types.terms.NumericLiteral;
+import org.apache.plc4x.plugins.codegenerator.types.terms.Term;
+import org.apache.plc4x.plugins.codegenerator.types.terms.VariableLiteral;
+import org.junit.jupiter.api.Test;
+
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.function.Consumer;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.*;
+
+class ExpressionStringParserTest {
+
+    ExpressionStringParser SUT = new ExpressionStringParser();
+
+    @Test
+    void parseSimple() {
+        Term term = SUT.parse(IOUtils.toInputStream("payload.lengthInBytes+4", Charset.defaultCharset()));
+        assertThat(term, not(nullValue()));
+        assertThat(term, instanceOf(BinaryTerm.class));
+        BinaryTerm binaryTerm = (BinaryTerm) term;
+        assertVariableLiteral(
+            binaryTerm.getA(),
+            "payload",
+            null,
+            lengthInBytesVariableLiteral -> assertVariableLiteral(
+                lengthInBytesVariableLiteral,
+                "lengthInBytes"
+            )
+        );
+        assertNumericLiteral(binaryTerm.getB(), 4L);
+        assertThat(binaryTerm.getOperation(), is("+"));
+    }
+
+
+    @Test
+    void parseDoubleCast() {
+        Term term = SUT.parse(IOUtils.toInputStream("CAST(CAST(parameter,S7ParameterUserData).items(hurz)[0],S7ParameterUserDataItemCPUFunctions).cpuFunctionType", Charset.defaultCharset()));
+        assertVariableLiteral(
+            term,
+            "CAST",
+            outerCast -> {
+                assertThat(outerCast, hasSize(2));
+                assertVariableLiteral(
+                    outerCast.get(0),
+                    "CAST",
+                    innerCast -> {
+                        assertThat(innerCast, hasSize(2));
+                        assertVariableLiteral(
+                            innerCast.get(0),
+                            "parameter"
+                        );
+                        assertVariableLiteral(
+                            innerCast.get(1),
+                            "S7ParameterUserData"
+                        );
+                    },
+                    items -> {
+                        assertVariableLiteral(
+                            items,
+                            "items",
+                            hurzes -> {
+                                assertThat(hurzes, hasSize(1));
+                                assertVariableLiteral(
+                                    hurzes.get(0),
+                                    "hurz"
+                                );
+                            },
+                            null,
+                            0
+                        );
+                    });
+                assertVariableLiteral(
+                    outerCast.get(1),
+                    "S7ParameterUserDataItemCPUFunctions"
+                );
+            }, variableLiteral -> {
+                assertVariableLiteral(
+                    variableLiteral,
+                    "cpuFunctionType"
+                );
+            }
+        );
+    }
+
+    @Test
+    void parseCast() {
+        Term term = SUT.parse(IOUtils.toInputStream("CAST(parameter,S7ParameterUserData).items(hurz)[0]", Charset.defaultCharset()));
+        assertVariableLiteral(
+            term,
+            "CAST",
+            terms -> {
+                assertThat(terms, hasSize(2));
+                assertVariableLiteral(
+                    terms.get(0),
+                    "parameter"
+                );
+                assertVariableLiteral(
+                    terms.get(1),
+                    "S7ParameterUserData"
+                );
+            }, variableLiteral -> {
+                assertVariableLiteral(
+                    variableLiteral,
+                    "items",
+                    terms -> {
+                        assertThat(terms, hasSize(1));
+                        assertVariableLiteral(
+                            terms.get(0),
+                            "hurz"
+                        );
+                    },
+                    null,
+                    0
+                );
+            }
+        );
+    }
+
+    void assertNumericLiteral(Term term, Number number) {
+        assertThat(term, not(nullValue()));
+        assertThat(term, instanceOf(NumericLiteral.class));
+        NumericLiteral numericLiteral = (NumericLiteral) term;
+        assertThat(numericLiteral.getNumber(), is(number));
+    }
+
+    void assertVariableLiteral(Term term, String name) {
+        assertVariableLiteral(term, name, null, null, -1);
+    }
+
+    void assertVariableLiteral(Term term, String name, Consumer<List<Term>> argsAsserter, Consumer<VariableLiteral> childAsserter) {
+        assertVariableLiteral(term, name, argsAsserter, childAsserter, -1);
+    }
+
+    void assertVariableLiteral(Term term, String name, Consumer<List<Term>> argsAsserter, Consumer<VariableLiteral> childAsserter, int index) {
+        assertThat(term, not(nullValue()));
+        assertThat(term, instanceOf(VariableLiteral.class));
+        VariableLiteral variableLiteral = (VariableLiteral) term;
+        assertThat(variableLiteral.getName(), is(name));
+        assertThat(variableLiteral.getIndex(), is(index));
+        if (argsAsserter != null) {
+            argsAsserter.accept(variableLiteral.getArgs());
+        } else {
+            assertThat(variableLiteral.getArgs(), nullValue());
+        }
+        if (childAsserter != null) {
+            childAsserter.accept(variableLiteral.getChild());
+        } else {
+            assertThat(variableLiteral.getChild(), nullValue());
+        }
+    }
+
+
+}
\ No newline at end of file
diff --git a/build-utils/protocol-base-mspec/src/test/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatListenerTest.java b/build-utils/protocol-base-mspec/src/test/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatListenerTest.java
new file mode 100644
index 0000000..3d9f4c4
--- /dev/null
+++ b/build-utils/protocol-base-mspec/src/test/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatListenerTest.java
@@ -0,0 +1,162 @@
+/*
+ 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.plc4x.plugins.codegenerator.language.mspec.parser;
+
+import org.antlr.v4.runtime.CommonToken;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.MSpecParser;
+import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Map;
+
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+
+// TODO: implement me
+@ExtendWith(MockitoExtension.class)
+class MessageFormatListenerTest {
+
+    MessageFormatListener SUT;
+
+    @BeforeEach
+    void setUp() {
+        SUT = new MessageFormatListener();
+    }
+
+    @Test
+    void getTypes() {
+        Map<String, TypeDefinition> types = SUT.getTypes();
+        assertThat(types, nullValue());
+    }
+
+    @Test
+    void enterFile() {
+        assertThat(SUT.getParserContexts(), nullValue());
+        assertThat(SUT.getEnumContexts(), nullValue());
+        assertThat(SUT.getTypes(), nullValue());
+        SUT.enterFile(null);
+        assertThat(SUT.getParserContexts(), not(nullValue()));
+        assertThat(SUT.getEnumContexts(), not(nullValue()));
+        assertThat(SUT.getTypes(), not(nullValue()));
+    }
+
+    @Test
+    void enterComplexType() {
+        assertThat(SUT.getParserContexts(), nullValue());
+        assertThat(SUT.getEnumContexts(), nullValue());
+        assertThat(SUT.getTypes(), nullValue());
+        SUT.enterFile(null);
+        MSpecParser.ComplexTypeContext complexTypeContext = new MSpecParser.ComplexTypeContext(null, 0);
+        SUT.enterComplexType(complexTypeContext);
+        assertThat(SUT.getParserContexts(), hasSize(1));
+        assertThat(SUT.getEnumContexts(), hasSize(0));
+        assertThat(SUT.getTypes().values(), hasSize(0));
+        complexTypeContext.enumValues = new MSpecParser.EnumValueDefinitionContext(null, 0);
+        SUT.enterComplexType(complexTypeContext);
+        assertThat(SUT.getParserContexts(), hasSize(1));
+        assertThat(SUT.getEnumContexts(), hasSize(1));
+        assertThat(SUT.getTypes().values(), hasSize(0));
+    }
+
+    @Test
+    void exitComplexType() {
+        MSpecParser.ComplexTypeContext complexTypeContext = new MSpecParser.ComplexTypeContext(null, 0);
+        complexTypeContext.name = new MSpecParser.IdExpressionContext(null, 0);
+        complexTypeContext.name.id = new CommonToken(0);
+        SUT.exitComplexType(complexTypeContext);
+    }
+
+    @Test
+    void enterArrayField() {
+    }
+
+    @Test
+    void enterChecksumField() {
+    }
+
+    @Test
+    void enterConstField() {
+    }
+
+    @Test
+    void enterDiscriminatorField() {
+    }
+
+    @Test
+    void enterEnumField() {
+    }
+
+    @Test
+    void enterImplicitField() {
+    }
+
+    @Test
+    void enterManualArrayField() {
+    }
+
+    @Test
+    void enterManualField() {
+    }
+
+    @Test
+    void enterOptionalField() {
+    }
+
+    @Test
+    void enterPaddingField() {
+    }
+
+    @Test
+    void enterReservedField() {
+    }
+
+    @Test
+    void enterSimpleField() {
+    }
+
+    @Test
+    void enterTypeSwitchField() {
+    }
+
+    @Test
+    void enterVirtualField() {
+    }
+
+    @Test
+    void enterCaseStatement() {
+    }
+
+    @Test
+    void exitCaseStatement() {
+    }
+
+    @Test
+    void enterEnumValueDefinition() {
+    }
+
+    @Test
+    void main() {
+    }
+}
\ No newline at end of file
diff --git a/build-utils/protocol-base-mspec/src/test/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatParserTest.java b/build-utils/protocol-base-mspec/src/test/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatParserTest.java
new file mode 100644
index 0000000..7aa47d2
--- /dev/null
+++ b/build-utils/protocol-base-mspec/src/test/java/org/apache/plc4x/plugins/codegenerator/language/mspec/parser/MessageFormatParserTest.java
@@ -0,0 +1,88 @@
+/*
+ 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.plc4x.plugins.codegenerator.language.mspec.parser;
+
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
+import org.apache.commons.io.IOUtils;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.MSpecLexer;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.MSpecParser;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.mock;
+
+// TODO: implement me
+@ExtendWith(MockitoExtension.class)
+class MessageFormatParserTest {
+
+    //@InjectMocks
+    MessageFormatParser SUT;
+
+    @Mock
+    MSpecLexer mSpecLexer;
+
+    @Mock
+    CommonTokenStream commonTokenStream;
+
+    @Mock
+    MSpecParser mSpecParser;
+
+    @Mock
+    ParseTreeWalker parseTreeWalker;
+
+    @Mock
+    MessageFormatListener messageFormatListener;
+
+    @BeforeEach
+    void setUp() {
+        SUT = new MessageFormatParser(
+            (_ignore) -> mSpecLexer,
+            (_ignore) -> commonTokenStream,
+            (_ignore) -> mSpecParser,
+            () -> parseTreeWalker,
+            () -> messageFormatListener
+        );
+    }
+
+    @Test
+    void parseNull() {
+        assertThrows(NullPointerException.class, () -> SUT.parse(null));
+    }
+
+    @Disabled("mockito broken because of NPE in REAL code")
+    @Test
+    void parseSomething() {
+        // TODO: seems like mockito is broken somehow...
+        MSpecParser mSpecParser = mock(MSpecParser.class);
+        // TODO: ... because this throws a NPE... (somehow call ends up in real code)
+        mSpecParser.file();
+        InputStream is = IOUtils.toInputStream("test", StandardCharsets.UTF_8);
+        SUT.parse(is);
+    }
+}
\ No newline at end of file