diff options
author | Sven Gothel <[email protected]> | 2015-03-24 03:22:05 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2015-03-24 03:22:05 +0100 |
commit | a2bf42eafb8d6bc270f832c6bd49793465a593d4 (patch) | |
tree | b7d091648e7da78cfc8675a1206560469d822fd3 /src/test/java/com | |
parent | 2ed80887326a12a2b47826c5ea0256f1b0c180d5 (diff) |
Complete JogAmp GlueGen merge: Relocate and patch unit test, strip unrelated files, add note in README.md
Diffstat (limited to 'src/test/java/com')
9 files changed, 929 insertions, 0 deletions
diff --git a/src/test/java/com/jogamp/gluegen/jcpp/CppReaderTest.java b/src/test/java/com/jogamp/gluegen/jcpp/CppReaderTest.java new file mode 100644 index 0000000..e4ef1c5 --- /dev/null +++ b/src/test/java/com/jogamp/gluegen/jcpp/CppReaderTest.java @@ -0,0 +1,73 @@ +package com.jogamp.gluegen.jcpp; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.Collections; + +import javax.annotation.Nonnull; + +import org.junit.Test; + +import com.jogamp.gluegen.test.junit.generation.BuildEnvironment; + +import static org.junit.Assert.assertEquals; + +public class CppReaderTest { + + public static String testCppReader(@Nonnull final String in, final Feature... f) throws Exception { + final String inclpath = BuildEnvironment.gluegenRoot + "/jcpp/src/test/resources" ; + + System.out.println("Testing " + in); + final StringReader r = new StringReader(in); + final CppReader p = new CppReader(r); + p.getPreprocessor().setSystemIncludePath( + Collections.singletonList(inclpath) + ); + p.getPreprocessor().addFeatures(f); + final BufferedReader b = new BufferedReader(p); + + final StringBuilder out = new StringBuilder(); + String line; + while ((line = b.readLine()) != null) { + System.out.println(" >> " + line); + out.append(line).append("\n"); + } + + return out.toString(); + } + + @Test + public void testCppReader() + throws Exception { + testCppReader("#include <test0.h>\n", Feature.LINEMARKERS); + } + + @Test + public void testVarargs() + throws Exception { + // The newlines are irrelevant, We want exactly one "foo" + testCppReader("#include <varargs.c>\n"); + } + + @Test + public void testPragmaOnce() + throws Exception { + // The newlines are irrelevant, We want exactly one "foo" + final String out = testCppReader("#include <once.c>\n", Feature.PRAGMA_ONCE); + assertEquals("foo", out.trim()); + } + + @Test + public void testPragmaOnceWithMarkers() + throws Exception { + // The newlines are irrelevant, We want exactly one "foo" + testCppReader("#include <once.c>\n", Feature.PRAGMA_ONCE, Feature.LINEMARKERS); + } + + public static void main(final String args[]) throws IOException { + final String tstname = CppReaderTest.class.getName(); + org.junit.runner.JUnitCore.main(tstname); + } + +} diff --git a/src/test/java/com/jogamp/gluegen/jcpp/ErrorTest.java b/src/test/java/com/jogamp/gluegen/jcpp/ErrorTest.java new file mode 100644 index 0000000..12a5160 --- /dev/null +++ b/src/test/java/com/jogamp/gluegen/jcpp/ErrorTest.java @@ -0,0 +1,65 @@ +package com.jogamp.gluegen.jcpp; + +import java.io.IOException; +import org.junit.Test; +import static com.jogamp.gluegen.jcpp.Token.*; +import static org.junit.Assert.*; + +public class ErrorTest { + + private boolean testError(Preprocessor p) + throws LexerException, + IOException { + for (;;) { + Token tok = p.token(); + if (tok.getType() == EOF) + break; + if (tok.getType() == INVALID) + return true; + } + return false; + } + + private void testError(String input) throws Exception { + StringLexerSource sl; + DefaultPreprocessorListener pl; + Preprocessor p; + + /* Without a PreprocessorListener, throws an exception. */ + sl = new StringLexerSource(input, true); + p = new Preprocessor(); + p.addFeature(Feature.CSYNTAX); + p.addInput(sl); + try { + assertTrue(testError(p)); + fail("Lexing unexpectedly succeeded without listener."); + } catch (LexerException e) { + /* required */ + } + + /* With a PreprocessorListener, records the error. */ + sl = new StringLexerSource(input, true); + p = new Preprocessor(); + p.addFeature(Feature.CSYNTAX); + p.addInput(sl); + pl = new DefaultPreprocessorListener(); + p.setListener(pl); + assertNotNull("CPP has listener", p.getListener()); + assertTrue(testError(p)); + assertTrue("Listener has errors", pl.getErrors() > 0); + + /* Without CSYNTAX, works happily. */ + sl = new StringLexerSource(input, true); + p = new Preprocessor(); + p.addInput(sl); + assertTrue(testError(p)); + } + + @Test + public void testErrors() throws Exception { + testError("\""); + testError("'"); + // testError("''"); + } + +} diff --git a/src/test/java/com/jogamp/gluegen/jcpp/IncludeAbsoluteTest.java b/src/test/java/com/jogamp/gluegen/jcpp/IncludeAbsoluteTest.java new file mode 100644 index 0000000..b44b900 --- /dev/null +++ b/src/test/java/com/jogamp/gluegen/jcpp/IncludeAbsoluteTest.java @@ -0,0 +1,46 @@ +package com.jogamp.gluegen.jcpp; + +import java.io.File; +import java.io.IOException; +import java.io.Reader; + +import org.junit.Test; + +import com.jogamp.common.util.IOUtil; +import com.jogamp.gluegen.Logging; +import com.jogamp.gluegen.Logging.LoggerIf; +import com.jogamp.gluegen.test.junit.generation.BuildEnvironment; + +import static org.junit.Assert.*; + +/** + * + * @author shevek + */ +public class IncludeAbsoluteTest { + + private static final LoggerIf LOG = Logging.getLogger(IncludeAbsoluteTest.class); + + @Test + public void testAbsoluteInclude() throws Exception { + final String filepath = BuildEnvironment.gluegenRoot + "/jcpp/src/test/resources/absolute.h" ; + LOG.info("filepath: " + filepath); + + final File file = new File(filepath); + assertTrue(file.exists()); + + final String input = "#include <" + file.getAbsolutePath() + ">\n"; + LOG.info("Input: " + input); + final Preprocessor pp = new Preprocessor(); + pp.addInput(new StringLexerSource(input, true)); + final Reader r = new CppReader(pp); + final String output = IOUtil.appendCharStream(new StringBuilder(), r).toString(); + r.close(); + LOG.info("Output: " + output); + assertTrue(output.contains("absolute-result")); + } + public static void main(final String args[]) throws IOException { + final String tstname = IncludeAbsoluteTest.class.getName(); + org.junit.runner.JUnitCore.main(tstname); + } +} diff --git a/src/test/java/com/jogamp/gluegen/jcpp/JavaFileSystemTest.java b/src/test/java/com/jogamp/gluegen/jcpp/JavaFileSystemTest.java new file mode 100644 index 0000000..d867fb8 --- /dev/null +++ b/src/test/java/com/jogamp/gluegen/jcpp/JavaFileSystemTest.java @@ -0,0 +1,39 @@ +package com.jogamp.gluegen.jcpp; + +import java.io.FileNotFoundException; +import org.junit.Test; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class JavaFileSystemTest { + + @Test + public void testJavaFileSystem() throws Exception { + JavaFileSystem fs = new JavaFileSystem(); + VirtualFile f; + + /* Anyone who has this file on their Unix box is messed up. */ + f = fs.getFile("/foo/bar baz"); + try { + f.getSource(); /* drop on floor */ + + assertTrue("Got a source for a non-file", f.isFile()); + } catch (FileNotFoundException e) { + assertFalse("Got no source for a file", f.isFile()); + } + + /* We hope we have this. */ + f = fs.getFile("/usr/include/stdio.h"); + try { + f.getSource(); /* drop on floor */ + + System.out.println("Opened stdio.h"); + assertTrue("Got a source for a non-file", f.isFile()); + } catch (FileNotFoundException e) { + System.out.println("Failed to open stdio.h"); + assertFalse("Got no source for a file", f.isFile()); + } + + } + +} diff --git a/src/test/java/com/jogamp/gluegen/jcpp/JoinReaderTest.java b/src/test/java/com/jogamp/gluegen/jcpp/JoinReaderTest.java new file mode 100644 index 0000000..628a7ec --- /dev/null +++ b/src/test/java/com/jogamp/gluegen/jcpp/JoinReaderTest.java @@ -0,0 +1,41 @@ +package com.jogamp.gluegen.jcpp; + +import java.io.StringReader; +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +public class JoinReaderTest { + + private void testJoinReader(String in, String out, boolean tg) + throws Exception { + System.out.println("Testing " + in + " => " + out); + StringReader r = new StringReader(in); + JoinReader j = new JoinReader(r, tg); + + for (int i = 0; i < out.length(); i++) { + int c = j.read(); + System.out.println("At offset " + i + ": " + (char) c); + assertEquals(out.charAt(i), c); + } + assertEquals(-1, j.read()); + assertEquals(-1, j.read()); + } + + private void testJoinReader(String in, String out) + throws Exception { + testJoinReader(in, out, true); + testJoinReader(in, out, false); + } + + @Test + public void testJoinReader() + throws Exception { + testJoinReader("ab", "ab"); + testJoinReader("a\\b", "a\\b"); + testJoinReader("a\nb", "a\nb"); + testJoinReader("a\\\nb", "ab\n"); + testJoinReader("foo??(bar", "foo[bar", true); + testJoinReader("foo??/\nbar", "foobar\n", true); + } + +} diff --git a/src/test/java/com/jogamp/gluegen/jcpp/LexerSourceTest.java b/src/test/java/com/jogamp/gluegen/jcpp/LexerSourceTest.java new file mode 100644 index 0000000..43ccbe6 --- /dev/null +++ b/src/test/java/com/jogamp/gluegen/jcpp/LexerSourceTest.java @@ -0,0 +1,143 @@ +package com.jogamp.gluegen.jcpp; + +import java.util.Arrays; + +import org.junit.Test; + +import com.jogamp.gluegen.Logging; +import com.jogamp.gluegen.Logging.LoggerIf; + +import static com.jogamp.gluegen.jcpp.PreprocessorTest.assertType; +import static com.jogamp.gluegen.jcpp.Token.*; +import static org.junit.Assert.*; + +public class LexerSourceTest { + + private static final LoggerIf LOG = Logging.getLogger(LexerSourceTest.class); + + public static void testLexerSource(final String in, final boolean textmatch, final int... out) + throws Exception { + LOG.info("Testing '" + in + "' => " + + Arrays.toString(out)); + final StringLexerSource s = new StringLexerSource(in); + + final StringBuilder buf = new StringBuilder(); + for (int i = 0; i < out.length; i++) { + final Token tok = s.token(); + LOG.info("Token is " + tok); + assertType(out[i], tok); + // assertEquals(col, tok.getColumn()); + buf.append(tok.getText()); + } + + final Token tok = s.token(); + LOG.info("Token is " + tok); + assertType(EOF, tok); + + if (textmatch) + assertEquals(in, buf.toString()); + } + + @Test + public void testLexerSource() + throws Exception { + + testLexerSource("int a = 5;", true, + IDENTIFIER, WHITESPACE, IDENTIFIER, WHITESPACE, + '=', WHITESPACE, NUMBER, ';' + ); + + // \n is WHITESPACE because ppvalid = false + testLexerSource("# # \r\n\n\r \rfoo", true, + HASH, WHITESPACE, '#', WHITESPACE, IDENTIFIER + ); + + // No match - trigraphs + testLexerSource("%:%:", false, PASTE); + testLexerSource("%:?", false, '#', '?'); + testLexerSource("%:%=", false, '#', MOD_EQ); + + testLexerSource("0x1234ffdUL 0765I", true, + NUMBER, WHITESPACE, NUMBER); + + testLexerSource("+= -= *= /= %= <= >= >>= <<= &= |= ^= x", true, + PLUS_EQ, WHITESPACE, + SUB_EQ, WHITESPACE, + MULT_EQ, WHITESPACE, + DIV_EQ, WHITESPACE, + MOD_EQ, WHITESPACE, + LE, WHITESPACE, + GE, WHITESPACE, + RSH_EQ, WHITESPACE, + LSH_EQ, WHITESPACE, + AND_EQ, WHITESPACE, + OR_EQ, WHITESPACE, + XOR_EQ, WHITESPACE, + IDENTIFIER); + + testLexerSource("/**/", true, CCOMMENT); + testLexerSource("/* /**/ */", true, CCOMMENT, WHITESPACE, '*', '/'); + testLexerSource("/** ** **/", true, CCOMMENT); + testLexerSource("//* ** **/", true, CPPCOMMENT); + testLexerSource("'\\r' '\\xf' '\\xff' 'x' 'aa' ''", true, + CHARACTER, WHITESPACE, + CHARACTER, WHITESPACE, + CHARACTER, WHITESPACE, + CHARACTER, WHITESPACE, + SQSTRING, WHITESPACE, + SQSTRING); + + if (false) // Actually, I think this is illegal. + testLexerSource("1i1I1l1L1ui1ul", true, + NUMBER, NUMBER, + NUMBER, NUMBER, + NUMBER, NUMBER); + + testLexerSource("'' 'x' 'xx'", true, + SQSTRING, WHITESPACE, CHARACTER, WHITESPACE, SQSTRING); + } + + @Test + public void testNumbers() throws Exception { + testLexerSource("0", true, NUMBER); + testLexerSource("045", true, NUMBER); + testLexerSource("45", true, NUMBER); + testLexerSource("0.45", true, NUMBER); + testLexerSource("1.45", true, NUMBER); + testLexerSource("1e6", true, NUMBER); + testLexerSource("1.45e6", true, NUMBER); + testLexerSource(".45e6", true, NUMBER); + testLexerSource("-6", true, '-', NUMBER); + } + + @Test + public void testNumbersSuffix() throws Exception { + testLexerSource("6f", true, NUMBER); + testLexerSource("6d", true, NUMBER); + testLexerSource("6l", true, NUMBER); + testLexerSource("6ll", true, NUMBER); + testLexerSource("6ul", true, NUMBER); + testLexerSource("6ull", true, NUMBER); + testLexerSource("6e3f", true, NUMBER); + testLexerSource("6e3d", true, NUMBER); + testLexerSource("6e3l", true, NUMBER); + testLexerSource("6e3ll", true, NUMBER); + testLexerSource("6e3ul", true, NUMBER); + testLexerSource("6e3ull", true, NUMBER); + } + + @Test + public void testNumbersInvalid() throws Exception { + // testLexerSource("0x foo", true, INVALID, WHITESPACE, IDENTIFIER); // FAIL + testLexerSource("6x foo", true, INVALID, WHITESPACE, IDENTIFIER); + testLexerSource("6g foo", true, INVALID, WHITESPACE, IDENTIFIER); + testLexerSource("6xsd foo", true, INVALID, WHITESPACE, IDENTIFIER); + testLexerSource("6gsd foo", true, INVALID, WHITESPACE, IDENTIFIER); + } + + @Test + public void testUnterminatedComment() throws Exception { + testLexerSource("5 /*", false, NUMBER, WHITESPACE, INVALID); // Bug #15 + testLexerSource("5 //", false, NUMBER, WHITESPACE, CPPCOMMENT); + } +} diff --git a/src/test/java/com/jogamp/gluegen/jcpp/NumericValueTest.java b/src/test/java/com/jogamp/gluegen/jcpp/NumericValueTest.java new file mode 100644 index 0000000..2d612ce --- /dev/null +++ b/src/test/java/com/jogamp/gluegen/jcpp/NumericValueTest.java @@ -0,0 +1,95 @@ +package com.jogamp.gluegen.jcpp; + +import java.io.IOException; +import org.junit.Test; +import static com.jogamp.gluegen.jcpp.Token.*; +import static org.junit.Assert.*; + +/** + * + * @author shevek + */ +public class NumericValueTest { + + private Token testNumericValue(String in) throws IOException, LexerException { + StringLexerSource s = new StringLexerSource(in); + + Token tok = s.token(); + System.out.println("Token is " + tok); + assertEquals(NUMBER, tok.getType()); + + Token eof = s.token(); + assertEquals("Didn't get EOF, but " + tok, EOF, eof.getType()); + + return tok; + } + + private void testNumericValue(String in, double out) throws IOException, LexerException { + System.out.println("Testing '" + in + "' -> " + out); + Token tok = testNumericValue(in); + assertEquals(in, tok.getText()); + NumericValue value = (NumericValue) tok.getValue(); + assertEquals("Double mismatch", out, value.doubleValue(), 0.01d); + assertEquals("Float mismatch", (float) out, value.floatValue(), 0.01f); + assertEquals("Long mismatch", (long) out, value.longValue()); + assertEquals("Integer mismatch", (int) out, value.intValue()); + } + + @Test + public void testNumericValue() throws Exception { + + // Zero + testNumericValue("0", 0); + + // Decimal + testNumericValue("1", 1); + testNumericValue("1L", 1); + testNumericValue("12", 12); + testNumericValue("12L", 12); + + // Hex + testNumericValue("0xf", 0xf); + testNumericValue("0xfL", 0xf); + testNumericValue("0x12", 0x12); + testNumericValue("0x12L", 0x12); + + // Negative + // testNumericValue("-0", 0); + // testNumericValue("-1", -1); + // Negative hex + // testNumericValue("-0x56", -0x56); + // testNumericValue("-0x102", -0x102); + // Octal and negative octal + testNumericValue("0673", Integer.parseInt("673", 8)); + // testNumericValue("-0673", Integer.parseInt("-673", 8)); + + // Floating point + testNumericValue(".0", 0); + testNumericValue(".00", 0); + testNumericValue("0.", 0); + testNumericValue("0.0", 0); + testNumericValue("00.0", 0); + testNumericValue("00.", 0); + + // Sign on exponents + testNumericValue("1e1", 1e1); + // testNumericValue("-1e1", -1e1); + testNumericValue("1e-1", 1e-1); + + // Hex numbers with decimal exponents + testNumericValue("0x12e3", 0x12e3); + testNumericValue("0x12p3", 0x12p3); + + // Octal numbers with decimal exponents + testNumericValue("012e3", 012e3); // Fails + testNumericValue("067e4", 067e4); // Fails + + // Issues a warning. + try { + testNumericValue("097", 97); + fail("No warning."); + } catch (LexerException e) { + } + + } +} diff --git a/src/test/java/com/jogamp/gluegen/jcpp/PreprocessorTest.java b/src/test/java/com/jogamp/gluegen/jcpp/PreprocessorTest.java new file mode 100644 index 0000000..651af98 --- /dev/null +++ b/src/test/java/com/jogamp/gluegen/jcpp/PreprocessorTest.java @@ -0,0 +1,372 @@ +package com.jogamp.gluegen.jcpp; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.util.List; +import java.util.logging.Level; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.jogamp.gluegen.Logging; +import com.jogamp.gluegen.Logging.LoggerIf; + +import static com.jogamp.gluegen.jcpp.Token.*; +import static org.junit.Assert.*; + +public class PreprocessorTest { + + private static final LoggerIf LOG = Logging.getLogger(PreprocessorTest.class); + + private OutputStreamWriter writer; + private Preprocessor p; + + @Before + public void setUp() throws Exception { + LOG.setLevel(Level.INFO); + final PipedOutputStream po = new PipedOutputStream(); + writer = new OutputStreamWriter(po); + + p = new Preprocessor(); + // p.addFeature(Feature.DEBUG); + p.addInput( + new LexerSource( + new InputStreamReader( + new PipedInputStream(po) + ), + true + ) + ); + } + + private static class I { + private final String t; + + public I(final String t) { + this.t = t; + } + + public String getText() { + return t; + } + + @Override + public String toString() { + return getText(); + } + } + private static I I(final String t) { + return new I(t); + } + private static class N { + private final String t; + + public N(final String t) { + this.t = t; + } + + public String getText() { + return t; + } + + @Override + public String toString() { + return getText(); + } + } + private static N N(final String t) { + return new N(t); + } + + /* + * When writing tests in this file, remember the preprocessor + * stashes NLs, so you won't see an immediate NL at the end of any + * input line. You will see it right before the next nonblank on + * the following input line. + */ + @Test + public void test01Preprocessor() throws Exception { + /* Magic macros */ + testInput("line = __LINE__\n", + I("line"), WHITESPACE, '=', WHITESPACE, NUMBER + /*, NL - all nls deferred so as not to block the reader */ + ); + testInput("file = __FILE__\n", NL, /* from before, etc */ + I("file"), WHITESPACE, '=', WHITESPACE, STRING + ); + + /* Simple definitions */ + testInput("#define A a /* a defined */\n", NL); + testInput("A /* a */\n", NL, I("a"), WHITESPACE, CCOMMENT); + testConstMacro("A", true, I("a")); + testInput("#define B b /* b defined */\n", NL); + testInput("B /* b */\n", NL, I("b"), WHITESPACE, CCOMMENT); + testConstMacro("B", false, I("b")); + testInput("#define C c /* c defined */\n", NL); + + /* Expansion of arguments */ + testInput("#define EXPAND(x) x\n", NL); + testInput("EXPAND(a)\n", NL, I("a")); + testInput("EXPAND(A)\n", NL, I("a")); + + /* Stringification */ + testInput("#define _STRINGIFY(x) #x\n", NL); + testInput("_STRINGIFY(A)\n", NL, "A"); + testInput("#define STRINGIFY(x) _STRINGIFY(x)\n", NL); + testInput("STRINGIFY(b)\n", NL, "b"); + testInput("STRINGIFY(A)\n", NL, "a"); + + /* Concatenation */ + testInput("#define _CONCAT(x, y) x ## y\n", NL); + testInput("_CONCAT(A, B)\n", NL, I("AB")); + testInput("#define A_CONCAT done_a_concat\n", NL); + testInput("_CONCAT(A, _CONCAT(B, C))\n", NL, + I("done_a_concat"), '(', I("b"), ',', WHITESPACE, I("c"), ')' + ); + testInput("#define CONCAT(x, y) _CONCAT(x, y)\n", NL); + testInput("CONCAT(A, CONCAT(B, C))\n", NL, I("abc")); + testInput("#define _CONCAT3(x, y, z) x ## y ## z\n", NL); + testInput("_CONCAT3(a, b, c)\n", NL, I("abc")); + testInput("_CONCAT3(A, B, C)\n", NL, I("ABC")); + testInput("_CONCAT(test_, inline)\n", NL, I("test_inline")); + testInput("_CONCAT(test_, \nnewline)\n", NL, I("test_newline")); + + /* Redefinitions, undefinitions. */ + testInput("#define two three\n", NL); + testInput("two /* three */\n", NL, I("three"), WHITESPACE, CCOMMENT); + testInput("one /* one */\n", NL, I("one"), WHITESPACE, CCOMMENT); + testConstMacro("two", false, I("three")); + testConstMacro("two", true, I("three")); + + testInput("#define one two\n", NL); + testInput("one /* three */\n", NL, I("three"), WHITESPACE, CCOMMENT); + testConstMacro("one", false, I("two")); + testConstMacro("one", true, I("three")); + + testInput("#undef two\n", NL); + testInput("one /* two */\n", NL, I("two"), WHITESPACE, CCOMMENT); + testConstMacro("one", false, I("two")); + testConstMacro("one", true, I("two")); + + testInput("#define two five\n", NL); + testInput("one /* five */\n", NL, I("five"), WHITESPACE, CCOMMENT); + testConstMacro("one", false, I("two")); + testConstMacro("one", true, I("five")); + + testInput("#undef two\n", NL); + testInput("one /* two */\n", NL, I("two"), WHITESPACE, CCOMMENT); + testConstMacro("one", false, I("two")); + testConstMacro("one", true, I("two")); + + testInput("#undef one\n", NL); + testInput("#define one four\n", NL); + testInput("one /* four */\n", NL, I("four"), WHITESPACE, CCOMMENT); + testConstMacro("one", false, I("four")); + testConstMacro("one", true, I("four")); + + testInput("#undef one\n", NL); + testInput("#define one one\n", NL); + testInput("one /* one */\n", NL, I("one"), WHITESPACE, CCOMMENT); + testConstMacro("one", false, I("one")); + testConstMacro("one", true, I("one")); + + testInput("#define NUM1 1\n", NL); + testInput("#define NUM4 ( 1 << ( NUM1 + NUM1 ) )\n", NL); + testInput("NUM4 /* ( 1 << ( 1 + 1 ) ) */\n", NL, + '(', WHITESPACE, N("1"), WHITESPACE, LSH, WHITESPACE, + '(', WHITESPACE, N("1"), WHITESPACE, '+', WHITESPACE, N("1"), WHITESPACE, ')', WHITESPACE, ')', + WHITESPACE, CCOMMENT); + testConstMacro("NUM4", false, '(', WHITESPACE, N("1"), WHITESPACE, LSH, WHITESPACE, + '(', WHITESPACE, I("NUM1"), WHITESPACE, '+', WHITESPACE, I("NUM1"), WHITESPACE, ')', + WHITESPACE, ')'); + testConstMacro("NUM4", true, '(', WHITESPACE, N("1"), WHITESPACE, LSH, WHITESPACE, + '(', WHITESPACE, N("1"), WHITESPACE, '+', WHITESPACE, N("1"), WHITESPACE, ')', WHITESPACE, ')'); + + /* Variadic macros. */ + testInput("#define var(x...) a x __VA_ARGS__ b\n", NL); + testInput("var(e, f, g)\n", NL, + I("a"), WHITESPACE, + I("e"), ',', WHITESPACE, + I("f"), ',', WHITESPACE, + I("g"), WHITESPACE, + I("__VA_ARGS__"), WHITESPACE, // __VA_ARGS__ is not expanded in this case. + I("b") + ); + /* Missing arguments are fine. */ + testInput("var()\n", NL, + I("a"), WHITESPACE, + /* No expansion for 'x'. */ WHITESPACE, + I("__VA_ARGS__"), WHITESPACE, + I("b") + ); + + /* Variadic macros with anonymous args. */ + testInput("#define var2(x, ...) a x __VA_ARGS__ e\n", NL); + testInput("var2(b, c, d)\n", NL, + I("a"), WHITESPACE, + I("b"), WHITESPACE, + I("c"), ',', WHITESPACE, + I("d"), WHITESPACE, + I("e") + ); + /* Missing arguments are fine. */ + testInput("var2(b)\n", NL, + I("a"), WHITESPACE, + I("b"), WHITESPACE, + /* No expansion for '__VA_ARGS__'. */ WHITESPACE, + I("e") + ); + + testInput("#define var3(...) a __VA_ARGS__ d\n", NL); + testInput("var3(b, c)\n", NL, + I("a"), WHITESPACE, + I("b"), ',', WHITESPACE, + I("c"), WHITESPACE, + I("d") + ); + testInput("var3()\n", NL, + I("a"), WHITESPACE, + /* No expansion for '__VA_ARGS__'. */ WHITESPACE, + I("d") + ); + + testInput("#define _Widen(x) L ## x\n", NL); + testInput("#define Widen(x) _Widen(x)\n", NL); + testInput("#define LStr(x) _Widen(#x)\n", NL); + testInput("LStr(x);\n", NL, I("L"), "x", ';'); + + testInput("'foo'\n", NL, SQSTRING); + testInput("#if 1 ? 2 : 0\nTEXT\n#endif\n", NL, NL, I("TEXT"), NL); + testInput("#if 1 ? 0 : 2\nTEXT\n#endif\n", NL, NL, NL); + testInput("#if 0 ? 0 : 2\nTEXT\n#endif\n", NL, NL, I("TEXT"), NL); + testInput("#if 0 ? 2 : 0\nTEXT\n#endif\n", NL, NL, NL); + + writer.close(); + + Token t; + do { + t = p.token(); + LOG.warning("Remaining token " + t); + } while (t.getType() != EOF); + } + + @Test + public void test02PreprocessorUnterminated() throws Exception { + testInput("#ifndef X\na\n#else\nb\n"); // Bug #16 + + writer.close(); + + Token t; + do { + t = p.token(); + LOG.warning("Remaining token " + t); + } while (t.getType() != EOF); + } + + public static void assertType(final int type, final Token t) { + final String typeExpect = TokenType.getTokenName(type); + final String typeActual = TokenType.getTokenName(t.getType()); + assertEquals("Expected " + typeExpect + " but got " + typeActual, type, t.getType()); + } + + private void testInput(final String in, final Object... out) + throws Exception { + LOG.info("Input: " + in); + writer.write(in); + writer.flush(); + for (final Object v : out) { + final Token t = p.token(); + LOG.info("READ: "+String.valueOf(t)); + if (v instanceof String) { + if (t.getType() != STRING) + fail("Expected STRING, but got " + t); + assertEquals(v, t.getValue()); + } else if (v instanceof I) { + if (t.getType() != IDENTIFIER) { + fail("Expected IDENTIFIER " + v + ", but got " + t); + } + assertEquals(((I) v).getText(), t.getText()); + } else if (v instanceof N) { + if (t.getType() != NUMBER) { + fail("Expected NUMBER " + v + ", but got " + t); + } + assertEquals(((N) v).getText(), t.getText()); + } else if (v instanceof Character) { + assertType(((Character) v).charValue(), t); + } else if (v instanceof Integer) { + assertType(((Number) v).intValue(), t); + } else { + fail("Bad object " + v.getClass()); + } + } + } + // slow .. + private Macro findMacro(final List<Macro> macros, final String macroName) { + final int count = macros.size(); + for(int i=0; i<count; i++) { + final Macro m = macros.get(i); + if( m.getName().equals(macroName) ) { + return m; + } + } + return null; + } + private void dumpMacros(final List<Macro> macros) { + final int count = macros.size(); + System.err.println("Macro count: "+count); + for(int i=0; i<count; i++) { + final Macro m = macros.get(i); + System.err.println(" ["+i+"]: "+m); + } + } + private void testConstMacro(final String macroName, final boolean expandMacro, final Object... out) + throws Exception { + final List<Macro> macros = p.getMacros(expandMacro); + final Macro m = findMacro(macros, macroName); + if( null == m ) { + dumpMacros(macros); + } + Assert.assertNotNull("Macro <"+macroName+"> is missing!", m); + Assert.assertFalse(m.isFunctionLike()); + + final Source s = new MacroTokenSource(m, null); + try { + for (final Object v : out) { + final Token t = s.token(); + LOG.info("READ: "+String.valueOf(t)); + if (v instanceof String) { + if (t.getType() != STRING) { + fail("Expected STRING, but got " + t); + } + assertEquals(v, t.getValue()); + } else if (v instanceof I) { + if (t.getType() != IDENTIFIER) { + fail("Expected IDENTIFIER " + v + ", but got " + t); + } + assertEquals(((I) v).getText(), t.getText()); + } else if (v instanceof N) { + if (t.getType() != NUMBER) { + fail("Expected NUMBER " + v + ", but got " + t); + } + assertEquals(((N) v).getText(), t.getText()); + } else if (v instanceof Character) { + assertType(((Character) v).charValue(), t); + } else if (v instanceof Integer) { + assertType(((Number) v).intValue(), t); + } else { + fail("Bad object " + v.getClass()); + } + } + } finally { + s.close(); + } + } + public static void main(final String args[]) throws IOException { + final String tstname = PreprocessorTest.class.getName(); + org.junit.runner.JUnitCore.main(tstname); + } +} diff --git a/src/test/java/com/jogamp/gluegen/jcpp/TokenPastingWhitespaceTest.java b/src/test/java/com/jogamp/gluegen/jcpp/TokenPastingWhitespaceTest.java new file mode 100644 index 0000000..756425b --- /dev/null +++ b/src/test/java/com/jogamp/gluegen/jcpp/TokenPastingWhitespaceTest.java @@ -0,0 +1,55 @@ +package com.jogamp.gluegen.jcpp; + +import com.jogamp.common.util.IOUtil; + +import java.io.IOException; +import java.io.Reader; + +import org.junit.Test; + +import com.jogamp.gluegen.Logging; +import com.jogamp.gluegen.Logging.LoggerIf; + +import static org.junit.Assert.*; + +/** + * https://github.com/shevek/jcpp/issues/25 + * + * @author shevek + */ +public class TokenPastingWhitespaceTest { + + private static final LoggerIf LOG = Logging.getLogger(TokenPastingWhitespaceTest.class); + + @Test + public void test01WhitespacePasting() throws IOException { + final Preprocessor pp = new Preprocessor(); + testWhitespacePastingImpl(pp); + } + void testWhitespacePastingImpl(final Preprocessor pp) throws IOException { + pp.addInput(new StringLexerSource( + "#define ONE(arg) one_##arg\n" + + "#define TWO(arg) ONE(two_##arg)\n" + + "\n" + + "TWO(good)\n" + + "TWO( /* evil newline */\n" + + " bad)\n" + + "\n" + + "ONE(good)\n" + + "ONE( /* evil newline */\n" + + " bad)\n", true)); + final Reader r = new CppReader(pp); + final String text = IOUtil.appendCharStream(new StringBuilder(), r).toString().trim(); + LOG.info("Output is:\n" + text); + assertEquals("one_two_good\n" + + "one_two_bad\n" + + "\n" + + "one_good\n" + + "one_bad", text); + } + + public static void main(final String args[]) throws IOException { + final String tstname = TokenPastingWhitespaceTest.class.getName(); + org.junit.runner.JUnitCore.main(tstname); + } +} |