#!/usr/bin/env python
# encoding: utf-8
#

import sys
import unittest

from CodeCheck import CodeChecker, Preprocessor


class RuleTest(unittest.TestCase):
    """Base class for RuleTesting."""

    def __init__(self, checker, val):
        unittest.TestCase.__init__(self)
        self.preprocessor = Preprocessor()
        self.c = checker
        self.tv = val

    def runTest(self):
        self.rv = self.c.check_text(
            self.preprocessor, 'testdir/test.h', self.tv)
        self._do_test()


class AllowedTest(RuleTest):
    """Tests allowed code.

    The rule checker shouldn't find anything in this test
    """

    def _do_test(self):
        self.assertTrue(len(self.rv) == 0,
                        "Rule '%s' failed. Example '%s' should be ok, but wasn't" % (self.c.name, self.tv))


class ForbiddenTest(RuleTest):
    """Tests forbidden constructs.

    The rule checker should find errors in the example code.
    """

    def _do_test(self):
        self.assertTrue(len(self.rv) != 0,
                        "Rule '%s' failed. Example '%s' should fail, but passed" % (self.c.name, self.tv))


def _make_tests_from_checker(c):
    """Takes a checker class and turns its allowed and forbidden variables into
    TestCases."""
    allowed_tests = [AllowedTest(c, allowed) for allowed in c.allowed]
    forbidden_tests = [ForbiddenTest(c, forbidden)
                       for forbidden in c.forbidden]

    return allowed_tests + forbidden_tests


class CommentsTest(unittest.TestCase):
    """Tests that comment stripping is working correctly."""

    def __init__(self, before, after):
        unittest.TestCase.__init__(self)
        self.before = before
        self.after = after

    def runTest(self):
        preprocessor = Preprocessor()
        after = preprocessor.get_preprocessed_data(
            'test', self.before, True, False)
        self.assertTrue(after == self.after,
                        'Stripping comments from %r failed. Expected %r, got %r'
                        % (self.before, self.after, after)
                        )


comment_tests = [
    # Let's get the basics right.
    ('a b c',
     'a b c'),
    # Whitespace before comments should be stripped
    ('a b c       // a',
     'a b c'),
    # Single line comment shouldn't affect the next line
    ('a b c       // a\nd e',
     'a b c\nd e'),
    # Multi-line comments should retain original line numbering
    ('a /* \n b \n */ c',
     'a \n\n c'),
    # Multiple comments on one line should work
    ('int32_t estimate(Map & /* map */, FCoords /* pos */) const {return 0;}\ntest',
     'int32_t estimate(Map & '       ', FCoords '       ') const {return 0;}\ntest'),
]

if __name__ == '__main__':
    import sys

    d = CodeChecker()

    suite = unittest.TestSuite()

    if len(sys.argv) > 1:
        for checker in (d._checkers):
            if checker.name in sys.argv:
                suite.addTests(_make_tests_from_checker(checker))
    else:
        for checker in d._checkers:
            suite.addTests(_make_tests_from_checker(checker))

    for before, after in comment_tests:
        suite.addTest(CommentsTest(before, after.splitlines(True)))
    success = unittest.TextTestRunner(verbosity=1).run(suite).wasSuccessful()
    sys.exit(0 if success else 1)
