aboutsummaryrefslogtreecommitdiffstats
path: root/src/test/java/com
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2015-03-24 03:22:05 +0100
committerSven Gothel <[email protected]>2015-03-24 03:22:05 +0100
commita2bf42eafb8d6bc270f832c6bd49793465a593d4 (patch)
treeb7d091648e7da78cfc8675a1206560469d822fd3 /src/test/java/com
parent2ed80887326a12a2b47826c5ea0256f1b0c180d5 (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')
-rw-r--r--src/test/java/com/jogamp/gluegen/jcpp/CppReaderTest.java73
-rw-r--r--src/test/java/com/jogamp/gluegen/jcpp/ErrorTest.java65
-rw-r--r--src/test/java/com/jogamp/gluegen/jcpp/IncludeAbsoluteTest.java46
-rw-r--r--src/test/java/com/jogamp/gluegen/jcpp/JavaFileSystemTest.java39
-rw-r--r--src/test/java/com/jogamp/gluegen/jcpp/JoinReaderTest.java41
-rw-r--r--src/test/java/com/jogamp/gluegen/jcpp/LexerSourceTest.java143
-rw-r--r--src/test/java/com/jogamp/gluegen/jcpp/NumericValueTest.java95
-rw-r--r--src/test/java/com/jogamp/gluegen/jcpp/PreprocessorTest.java372
-rw-r--r--src/test/java/com/jogamp/gluegen/jcpp/TokenPastingWhitespaceTest.java55
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);
+ }
+}