import unittest

from StringIO import StringIO
import os
import sys
import os.path
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))

from Preprocessor import Preprocessor

class NamedIO(StringIO):
  def __init__(self, name, content):
    self.name = name
    StringIO.__init__(self, content)

class TestPreprocessor(unittest.TestCase):
  """
  Unit tests for the Context class
  """

  def setUp(self):
    self.pp = Preprocessor()
    self.pp.out = StringIO()

  def test_conditional_if_0(self):
    f = NamedIO("conditional_if_0.in", """#if 0
FAIL
#else
PASS
#endif
""")
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")

  def test_string_value(self):
    f = NamedIO("string_value.in", """#define FOO STRING
#if FOO
string value is true
#else
string value is false
#endif
""")
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "string value is false\n")
  
  def test_number_value(self):
    f = NamedIO("string_value.in", """#define FOO 1
#if FOO
number value is true
#else
number value is false
#endif
""")
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "number value is true\n")
  
  def test_conditional_if_0_elif_1(self):
    f = NamedIO('conditional_if_0_elif_1.in', '''#if 0
#elif 1
PASS
#else
FAIL
#endif
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")
  
  def test_conditional_if_1(self):
    f = NamedIO('conditional_if_1.in', '''#if 1
PASS
#else
FAILE
#endif
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")
  
  def test_conditional_if_1_elif_1_else(self):
    f = NamedIO('conditional_if_1_elif_1_else.in', '''#if 1
PASS
#elif 1
FAIL
#else
FAIL
#endif
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")
  
  def test_conditional_if_1_if_1(self):
    f = NamedIO('conditional_if_1_if_1.in', '''#if 1
#if 1
PASS
#else
FAIL
#endif
#else
FAIL
#endif
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")
  
  def test_conditional_not_0(self):
    f = NamedIO('conditional_not_0.in', '''#if !0
PASS
#else
FAIL
#endif
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")
  
  def test_conditional_not_1(self):
    f = NamedIO('conditional_not_1.in', '''#if !1
FAIL
#else
PASS
#endif
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")
  
  def test_conditional_not_emptyval(self):
    f = NamedIO('conditional_not_emptyval.in', '''#define EMPTYVAL
#if !EMPTYVAL
FAIL
#else
PASS
#endif
#if EMPTYVAL
PASS
#else
FAIL
#endif
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\nPASS\n")
  
  def test_conditional_not_nullval(self):
    f = NamedIO('conditional_not_nullval.in', '''#define NULLVAL 0
#if !NULLVAL
PASS
#else
FAIL
#endif
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")
  
  def test_expand(self):
    f = NamedIO('expand.in', '''#define ASVAR AS
#expand P__ASVAR__S
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")

  def test_undef_defined(self):
    f = NamedIO('undef_defined.in', '''#define BAR
#undef BAR
BAR
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "BAR\n")

  def test_undef_undefined(self):
    f = NamedIO('undef_undefined.in', '''#undef VAR
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "")
  
  def test_filter_attemptSubstitution(self):
    f = NamedIO('filter_attemptSubstitution.in', '''#filter attemptSubstitution
P@VAR@ASS
#unfilter attemptSubstitution
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")
  
  def test_filter_emptyLines(self):
    f = NamedIO('filter_emptyLines.in', '''lines with a

blank line
#filter emptyLines
lines with

no blank lines
#unfilter emptyLines
yet more lines with

blank lines
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), '''lines with a

blank line
lines with
no blank lines
yet more lines with

blank lines
''')
  
  def test_filter_slashslash(self):
    f = NamedIO('filter_slashslash.in', '''#filter slashslash
PASS//FAIL  // FAIL
#unfilter slashslash
PASS // PASS
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\nPASS // PASS\n")
  
  def test_filter_spaces(self):
    f = NamedIO('filter_spaces.in', '''#filter spaces
You should see two nice ascii tables
 +-+-+-+
 | |   |     |
 +-+-+-+
#unfilter spaces
+-+---+
| |   |
+-+---+ 
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), """You should see two nice ascii tables
+-+-+-+
| | | |
+-+-+-+
+-+---+
| |   |
+-+---+ 
""")
  
  def test_filter_substitution(self):
    f = NamedIO('filter_substitution.in', '''#define VAR ASS
#filter substitution
P@VAR@
#unfilter substitution
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")

  def test_error(self):
    f = NamedIO('error.in', '''#error spit this message out
''')
    caught_msg = None
    try:
      self.pp.do_include(f)
    except Preprocessor.Error, e:
      caught_msg = e.args[0][-1]
    self.assertEqual(caught_msg, 'spit this message out')
  
  def test_javascript_line(self):
    f = NamedIO('javascript_line.js.in', '''// Line 1
#if 0
// line 3
#endif
// line 5
# comment
// line 7
// line 8
// line 9
# another comment
// line 11
#define LINE 1
// line 13, given line number overwritten with 2
''')
    self.pp.do_include(f)
    out = """// Line 1
//@line 5 "CWDjavascript_line.js.in"
// line 5
//@line 7 "CWDjavascript_line.js.in"
// line 7
// line 8
// line 9
//@line 11 "CWDjavascript_line.js.in"
// line 11
//@line 2 "CWDjavascript_line.js.in"
// line 13, given line number overwritten with 2
"""
    out = out.replace('CWD', os.getcwd() + os.path.sep)
    self.assertEqual(self.pp.out.getvalue(), out)
  
  def test_literal(self):
    f = NamedIO('literal.in', '''#literal PASS
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")
  
  def test_var_directory(self):
    f = NamedIO('var_directory.in', '''#ifdef DIRECTORY
PASS
#else
FAIL
#endif
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")
  
  def test_var_file(self):
    f = NamedIO('var_file.in', '''#ifdef FILE
PASS
#else
FAIL
#endif
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")
  
  def test_var_if_0(self):
    f = NamedIO('var_if_0.in', '''#define VAR 0
#if VAR
FAIL
#else
PASS
#endif
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")
  
  def test_var_if_0_elifdef(self):
    f = NamedIO('var_if_0_elifdef.in', '''#if 0
#elifdef FILE
PASS
#else
FAIL
#endif
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")
  
  def test_var_if_0_elifndef(self):
    f = NamedIO('var_if_0_elifndef.in', '''#if 0
#elifndef VAR
PASS
#else
FAIL
#endif
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")
  
  def test_var_ifdef_0(self):
    f = NamedIO('var_ifdef_0.in', '''#define VAR 0
#ifdef VAR
PASS
#else
FAIL
#endif
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")
  
  def test_var_ifdef_undef(self):
    f = NamedIO('var_ifdef_undef.in', '''#define VAR 0
#undef VAR
#ifdef VAR
FAIL
#else
PASS
#endif
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")
  
  def test_var_ifndef_0(self):
    f = NamedIO('var_ifndef_0.in', '''#define VAR 0
#ifndef VAR
FAIL
#else
PASS
#endif
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")
  
  def test_var_ifndef_undef(self):
    f = NamedIO('var_ifndef_undef.in', '''#define VAR 0
#undef VAR
#ifndef VAR
PASS
#else
FAIL
#endif
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")
  
  def test_var_line(self):
    f = NamedIO('var_line.in', '''#ifdef LINE
PASS
#else
FAIL
#endif
''')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "PASS\n")

  def test_lineEndings(self):
    f = NamedIO('lineEndings.in', '''first
#literal second
''')
    self.pp.setLineEndings('cr')
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "first\rsecond\r")

  def test_number_value_equals(self):
    f = NamedIO("number_value_equals.in", """#define FOO 1000
#if FOO == 1000
number value is equal
#else
number value is not equal
#endif
""")
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "number value is equal\n")

  def test_number_value_equals_defines(self):
    f = NamedIO("number_value_equals_defines.in", """#if FOO == 1000
number value is equal
#else
number value is not equal
#endif
""")
    self.pp.handleCommandLine(["-DFOO=1000"])
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "number value is equal\n")

  def test_octal_value_equals(self):
    f = NamedIO("octal_value_equals.in", """#define FOO 0100
#if FOO == 0100
octal value is equal
#else
octal value is not equal
#endif
""")
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "octal value is equal\n")

  def test_octal_value_equals_defines(self):
    f = NamedIO("octal_value_equals_defines.in", """#if FOO == 0100
octal value is equal
#else
octal value is not equal
#endif
""")
    self.pp.handleCommandLine(["-DFOO=0100"])
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "octal value is equal\n")

  def test_value_quoted_expansion(self):
    """
    Quoted values on the commandline don't currently have quotes stripped.
    Pike says this is for compat reasons.
    """
    f = NamedIO("value_quoted_expansion.in", """#filter substitution
@FOO@
""")
    self.pp.handleCommandLine(['-DFOO="ABCD"'])
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), '"ABCD"\n')

  def test_octal_value_quoted_expansion(self):
    f = NamedIO("value_quoted_expansion.in", """#filter substitution
@FOO@
""")
    self.pp.handleCommandLine(['-DFOO="0100"'])
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), '"0100"\n')

  def test_number_value_not_equals_quoted_defines(self):
    f = NamedIO("number_value_not_equals_quoted_defines.in", """#if FOO == 1000
number value is equal
#else
number value is not equal
#endif
""")
    self.pp.handleCommandLine(['-DFOO="1000"'])
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "number value is not equal\n")

  def test_octal_value_not_equals_quoted_defines(self):
    f = NamedIO("octal_value_not_equals_quoted_defines.in", """#if FOO == 0100
octal value is equal
#else
octal value is not equal
#endif
""")
    self.pp.handleCommandLine(['-DFOO="0100"'])
    self.pp.do_include(f)
    self.assertEqual(self.pp.out.getvalue(), "octal value is not equal\n")

if __name__ == '__main__':
  unittest.main()
