# encoding: utf-8
from mo_future import text

from mo_parsing.core import ParserElement

from mo_parsing.exceptions import ParseException
from mo_parsing.results import ParseResults
from mo_parsing.tokens import Literal
from mo_parsing.utils import Log


def run_tests(
    self,
    tests,
    parse_all=True,
    comment="#",
    fullDump=True,
    printResults=True,
    failureTests=False,
    postParse=None,
    file=None,
):
    """
    Execute the parse expression on a series of test strings, showing each
    test, the parsed results or where the parse failed. Quick and easy way to
    run a parse expression against a list of sample strings.

    Parameters:
     - tests - a list of separate test strings, or a multiline string of test strings
     - parse_all - (default= ``True``) - flag to pass to `parse_string` when running tests
     - comment - (default= ``'#'``) - expression for indicating embedded comments in the test
          string; pass None to disable comment filtering
     - fullDump - (default= ``True``) - dump results as list followed by results names in nested outline;
          if False, only dump nested list
     - printResults - (default= ``True``) prints test output to stdout
     - failureTests - (default= ``False``) indicates if these tests are expected to fail parsing
     - postParse - (default= ``None``) optional callback for successful parse results; called as
          `fn(test_string, parse_results)` and returns a string to be added to the test output
     - file - (default=``None``) optional file-like object to which test output will be written;
          if None, will default to ``sys.stdout``

    Returns: a (success, results) tuple, where success indicates that all tests succeeded
    (or failed if ``failureTests`` is True), and the results contain a list of lines of each
    test's output

    Example::

        number_expr = number.copy()

        result = test.run_tests(number_expr, '''
            # unsigned integer
            100
            # negative integer
            -100
            # float with scientific notation
            6.02e23
            # integer with scientific notation
            1e-12
            ''')


        result = test.run_tests(number_expr, '''
            # stray character
            100Z
            # missing leading digit before '.'
            -.100
            # too many '.'
            3.14.159
            ''', failureTests=True)


    prints::

        # unsigned integer
        100
        [100]

        # negative integer
        -100
        [-100]

        # float with scientific notation
        6.02e23
        [6.02e+23]

        # integer with scientific notation
        1e-12
        [1e-12]

        Success

        # stray character
        100Z
           ^
        FAIL: Expected end of text (at char 3), (line:1, col:4)

        # missing leading digit before '.'
        -.100
        ^
        FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1)

        # too many '.'
        3.14.159
            ^
        FAIL: Expected end of text (at char 4), (line:1, col:5)

        Success

    Each test string must be on a single line. If you want to test a string that spans multiple
    lines, create a test like this::

        expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines")

    (Note that this is a raw string literal, you must include the leading 'r'.)
    """
    error = Log.error
    if isinstance(tests, text):
        tests = [tt for t in tests.rstrip().splitlines() for tt in [t.strip()] if tt]
    if isinstance(comment, text):
        comment = Literal(comment)

    allResults = []
    NL = Literal(r"\n").add_parse_action(lambda: "\n")
    BOM = u"\ufeff"
    for i, (t, failureTest) in enumerate(zip(
        tests,
        [failureTests] * len(tests)
        if not isinstance(failureTests, list)
        else failureTests,
    )):
        if comment is not None and comment.matches(t, False):
            continue
        if not t:
            continue
        try:
            # convert newline marks to actual newlines, and strip leading BOM if present
            t = NL.streamline().transform_string(t.lstrip(BOM))
            result = self.parse_string(t, parse_all=parse_all)
        except ParseException as pe:
            if not failureTest:
                error("can not parse {{sql}}", sql=t, cause=pe)

            result = pe
        except Exception as exc:
            if not failureTest:
                error("FAIL-EXCEPTION", cause=exc)
            result = exc
        else:
            if failureTest:
                error("EXPECTING FAIL")

            if postParse:
                try:
                    postParse(t, result)
                except Exception as cause:
                    Log.error(
                        "postParse {{name}} failed",
                        name=postParse.__name__,
                        cause=cause,
                    )

        allResults.append((t, result))

    return True, allResults


ParserElement.run_tests = run_tests

# encoding: utf-8
from mo_future import text

from mo_parsing.core import ParserElement

from mo_parsing.exceptions import ParseException
from mo_parsing.results import ParseResults
from mo_parsing.tokens import Literal
from mo_parsing.utils import Log


def run_tests(
    self,
    tests,
    parse_all=True,
    comment="#",
    fullDump=True,
    printResults=True,
    failureTests=False,
    postParse=None,
    file=None,
):
    """
    Execute the parse expression on a series of test strings, showing each
    test, the parsed results or where the parse failed. Quick and easy way to
    run a parse expression against a list of sample strings.

    Parameters:
     - tests - a list of separate test strings, or a multiline string of test strings
     - parse_all - (default= ``True``) - flag to pass to `parse_string` when running tests
     - comment - (default= ``'#'``) - expression for indicating embedded comments in the test
          string; pass None to disable comment filtering
     - fullDump - (default= ``True``) - dump results as list followed by results names in nested outline;
          if False, only dump nested list
     - printResults - (default= ``True``) prints test output to stdout
     - failureTests - (default= ``False``) indicates if these tests are expected to fail parsing
     - postParse - (default= ``None``) optional callback for successful parse results; called as
          `fn(test_string, parse_results)` and returns a string to be added to the test output
     - file - (default=``None``) optional file-like object to which test output will be written;
          if None, will default to ``sys.stdout``

    Returns: a (success, results) tuple, where success indicates that all tests succeeded
    (or failed if ``failureTests`` is True), and the results contain a list of lines of each
    test's output

    Example::

        number_expr = number.copy()

        result = test.run_tests(number_expr, '''
            # unsigned integer
            100
            # negative integer
            -100
            # float with scientific notation
            6.02e23
            # integer with scientific notation
            1e-12
            ''')


        result = test.run_tests(number_expr, '''
            # stray character
            100Z
            # missing leading digit before '.'
            -.100
            # too many '.'
            3.14.159
            ''', failureTests=True)


    prints::

        # unsigned integer
        100
        [100]

        # negative integer
        -100
        [-100]

        # float with scientific notation
        6.02e23
        [6.02e+23]

        # integer with scientific notation
        1e-12
        [1e-12]

        Success

        # stray character
        100Z
           ^
        FAIL: Expected end of text (at char 3), (line:1, col:4)

        # missing leading digit before '.'
        -.100
        ^
        FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1)

        # too many '.'
        3.14.159
            ^
        FAIL: Expected end of text (at char 4), (line:1, col:5)

        Success

    Each test string must be on a single line. If you want to test a string that spans multiple
    lines, create a test like this::

        expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines")

    (Note that this is a raw string literal, you must include the leading 'r'.)
    """
    error = Log.error
    if isinstance(tests, text):
        tests = [tt for t in tests.rstrip().splitlines() for tt in [t.strip()] if tt]
    if isinstance(comment, text):
        comment = Literal(comment)

    allResults = []
    NL = Literal(r"\n").add_parse_action(lambda: "\n")
    BOM = u"\ufeff"
    for i, (t, failureTest) in enumerate(zip(
        tests,
        [failureTests] * len(tests)
        if not isinstance(failureTests, list)
        else failureTests,
    )):
        if comment is not None and comment.matches(t, False):
            continue
        if not t:
            continue
        try:
            # convert newline marks to actual newlines, and strip leading BOM if present
            t = NL.streamline().transform_string(t.lstrip(BOM))
            result = self.parse_string(t, parse_all=parse_all)
        except ParseException as pe:
            if not failureTest:
                error("can not parse {{sql}}", sql=t, cause=pe)

            result = pe
        except Exception as exc:
            if not failureTest:
                error("FAIL-EXCEPTION", cause=exc)
            result = exc
        else:
            if failureTest:
                error("EXPECTING FAIL")

            if postParse:
                try:
                    postParse(t, result)
                except Exception as cause:
                    Log.error(
                        "postParse {{name}} failed",
                        name=postParse.__name__,
                        cause=cause,
                    )

        allResults.append((t, result))

    return True, allResults


ParserElement.run_tests = run_tests

# encoding: utf-8 Execute the parse expression on a series of test strings, showing each     test, the parsed results or where the parse failed. Quick and easy way to     run a parse expression against a list of sample strings.      Parameters:      - tests - a list of separate test strings, or a multiline string of test strings      - parse_all - (default= ``True``) - flag to pass to `parse_string` when running tests      - comment - (default= ``'#'``) - expression for indicating embedded comments in the test           string; pass None to disable comment filtering      - fullDump - (default= ``True``) - dump results as list followed by results names in nested outline;           if False, only dump nested list      - printResults - (default= ``True``) prints test output to stdout      - failureTests - (default= ``False``) indicates if these tests are expected to fail parsing      - postParse - (default= ``None``) optional callback for successful parse results; called as           `fn(test_string, parse_results)` and returns a string to be added to the test output      - file - (default=``None``) optional file-like object to which test output will be written;           if None, will default to ``sys.stdout``      Returns: a (success, results) tuple, where success indicates that all tests succeeded     (or failed if ``failureTests`` is True), and the results contain a list of lines of each     test's output      Example::          number_expr = number.copy()          result = test.run_tests(number_expr, '''             # unsigned integer             100             # negative integer             -100             # float with scientific notation             6.02e23             # integer with scientific notation             1e-12             ''')           result = test.run_tests(number_expr, '''             # stray character             100Z             # missing leading digit before '.'             -.100             # too many '.'             3.14.159             ''', failureTests=True)       prints::          # unsigned integer         100         [100]          # negative integer         -100         [-100]          # float with scientific notation         6.02e23         [6.02e+23]          # integer with scientific notation         1e-12         [1e-12]          Success          # stray character         100Z            ^         FAIL: Expected end of text (at char 3), (line:1, col:4)          # missing leading digit before '.'         -.100         ^         FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1)          # too many '.'         3.14.159             ^         FAIL: Expected end of text (at char 4), (line:1, col:5)          Success      Each test string must be on a single line. If you want to test a string that spans multiple     lines, create a test like this::          expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines")      (Note that this is a raw string literal, you must include the leading 'r'.) # convert newline marks to actual newlines, and strip leading BOM if present