# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
#
# Copyright (c) 2015-2016, Lars Asplund lars.anders.asplund@gmail.com

# pylint: disable=too-many-public-methods
# pylint: disable=unused-wildcard-import
# pylint: disable=wildcard-import

"""
Test of the Verilog preprocessor
"""

from os.path import join, dirname, exists
import os
from unittest import TestCase
import shutil
from vunit.ostools import renew_path, write_file
from vunit.parsing.verilog.preprocess import VerilogPreprocessor, Macro
from vunit.parsing.verilog.tokenizer import VerilogTokenizer
from vunit.parsing.tokenizer import Token
from vunit.test.mock_2or3 import mock


class TestVerilogPreprocessor(TestCase):
    """
    Test of the Verilog preprocessor
    """

    def setUp(self):
        self.output_path = join(dirname(__file__), "test_verilog_preprocessor_out")
        renew_path(self.output_path)
        self.cwd = os.getcwd()
        os.chdir(self.output_path)

    def tearDown(self):
        os.chdir(self.cwd)
        shutil.rmtree(self.output_path)

    def test_non_preprocess_tokens_are_kept(self):
        result = self.preprocess('"hello"ident/*comment*///comment')
        result.assert_has_tokens('"hello"ident/*comment*///comment')
        result.assert_no_defines()

    def test_preprocess_define_without_value(self):
        result = self.preprocess("`define foo")
        result.assert_has_tokens("")
        result.assert_has_defines({"foo": Macro("foo")})

        result = self.preprocess("`define foo\nkeep")
        result.assert_has_tokens("keep")
        result.assert_has_defines({"foo": Macro("foo")})

    def test_preprocess_define_with_value(self):
        result = self.preprocess("`define foo bar \"abc\"")
        result.assert_has_tokens("")
        result.assert_has_defines({"foo": Macro("foo", tokenize("bar \"abc\""))})

    def test_preprocess_define_with_lpar_value(self):
        result = self.preprocess("`define foo (bar)")
        result.assert_has_tokens("")
        result.assert_has_defines({"foo": Macro("foo", tokenize("(bar)"))})

    def test_preprocess_define_with_one_arg(self):
        result = self.preprocess("`define foo(arg)arg 123")
        result.assert_has_tokens("")
        result.assert_has_defines({"foo": Macro("foo", tokenize("arg 123"), args=("arg",))})

    def test_preprocess_define_with_one_arg_ignores_initial_space(self):
        result = self.preprocess("`define foo(arg) arg 123")
        result.assert_has_tokens("")
        result.assert_has_defines({"foo": Macro("foo", tokenize("arg 123"), args=("arg",))})

    def test_preprocess_define_with_multiple_args(self):
        result = self.preprocess("`define foo( arg1, arg2)arg1 arg2")
        result.assert_has_tokens("")
        result.assert_has_defines({"foo": Macro("foo", tokenize("arg1 arg2"), args=("arg1", "arg2"))})

    def test_preprocess_define_with_default_values(self):
        result = self.preprocess("`define foo(arg1, arg2=default)arg1 arg2")
        result.assert_has_tokens("")
        result.assert_has_defines({"foo": Macro("foo",
                                                tokenize("arg1 arg2"),
                                                args=("arg1", "arg2"),
                                                defaults={"arg2": tokenize("default")})})

    def test_preprocess_substitute_define_without_args(self):
        result = self.preprocess("""\
`define foo bar \"abc\"
`foo""")
        result.assert_has_tokens("bar \"abc\"")

    def test_preprocess_substitute_define_with_one_arg(self):
        result = self.preprocess("""\
`define foo(arg)arg 123
`foo(hello hey)""")
        result.assert_has_tokens("hello hey 123")

    def test_preprocess_substitute_define_with_multile_args(self):
        result = self.preprocess("""\
`define foo(arg1, arg2)arg1,arg2
`foo(1 2, hello)""")
        result.assert_has_tokens("1 2, hello")

    def test_preprocess_substitute_define_with_default_values(self):
        result = self.preprocess("""\
`define foo(arg1, arg2=default)arg1 arg2
`foo(1)""")
        result.assert_has_tokens("1 default")

    def test_preprocess_include_directive(self):
        self.write_file("include.svh", "hello hey")
        result = self.preprocess('`include "include.svh"',
                                 include_paths=[self.output_path])
        result.assert_has_tokens("hello hey")
        result.assert_included_files([join(self.output_path, "include.svh")])

    def test_detects_circular_includes(self):
        self.write_file("include1.svh", '`include "include2.svh"')
        self.write_file("include2.svh", '`include "include1.svh"')
        result = self.preprocess('`include "include1.svh"',
                                 include_paths=[self.output_path])
        result.logger.error.assert_called_once_with(
            'Circular `include of include2.svh detected\n%s',
            'from fn.v line 1:\n'
            '`include "include1.svh"\n'
            '~~~~~~~~\n'
            'from include1.svh line 1:\n'
            '`include "include2.svh"\n'
            '~~~~~~~~\n'
            'from include2.svh line 1:\n'
            '`include "include1.svh"\n'
            '~~~~~~~~\n'
            'at include1.svh line 1:\n'
            '`include "include2.svh"\n'
            '         ~~~~~~~~~~~~~~')

    def test_detects_circular_include_of_self(self):
        self.write_file("include.svh", '`include "include.svh"')
        result = self.preprocess('`include "include.svh"',
                                 include_paths=[self.output_path])
        result.logger.error.assert_called_once_with(
            'Circular `include of include.svh detected\n%s',
            'from fn.v line 1:\n'
            '`include "include.svh"\n'
            '~~~~~~~~\n'
            'from include.svh line 1:\n'
            '`include "include.svh"\n'
            '~~~~~~~~\n'
            'at include.svh line 1:\n'
            '`include "include.svh"\n'
            '         ~~~~~~~~~~~~~')

    def test_does_not_detect_non_circular_includes(self):
        self.write_file("include3.svh", 'keep')
        self.write_file("include1.svh", '`include "include3.svh"\n`include "include2.svh"')
        self.write_file("include2.svh", '`include "include3.svh"')
        result = self.preprocess('`include "include1.svh"\n`include "include2.svh"',
                                 include_paths=[self.output_path])
        result.assert_no_log()

    def test_detects_circular_macro_expansion_of_self(self):
        result = self.preprocess('''
`define foo `foo
`foo
''')
        result.logger.error.assert_called_once_with(
            'Circular macro expansion of foo detected\n%s',
            'from fn.v line 3:\n'
            '`foo\n'
            '~~~~\n'
            'from fn.v line 2:\n'
            '`define foo `foo\n'
            '            ~~~~\n'
            'at fn.v line 2:\n'
            '`define foo `foo\n'
            '            ~~~~')

    def test_detects_circular_macro_expansion(self):
        result = self.preprocess('''
`define foo `bar
`define bar `foo
`foo
''')
        result.logger.error.assert_called_once_with(
            'Circular macro expansion of bar detected\n%s',
            'from fn.v line 4:\n'
            '`foo\n'
            '~~~~\n'
            'from fn.v line 2:\n'
            '`define foo `bar\n'
            '            ~~~~\n'
            'from fn.v line 3:\n'
            '`define bar `foo\n'
            '            ~~~~\n'
            'at fn.v line 2:\n'
            '`define foo `bar\n'
            '            ~~~~')

    def test_does_not_detect_non_circular_macro_expansion(self):
        result = self.preprocess('''
`define foo bar
`foo
`foo
''')
        result.assert_no_log()

    def test_preprocess_include_directive_from_define(self):
        self.write_file("include.svh", "hello hey")
        result = self.preprocess('''\
`define inc "include.svh"
`include `inc''',
                                 include_paths=[self.output_path])
        result.assert_has_tokens('hello hey')
        result.assert_included_files([join(self.output_path, "include.svh")])

    def test_preprocess_include_directive_from_define_with_args(self):
        self.write_file("include.svh", "hello hey")
        result = self.preprocess('''\
`define inc(a) a
`include `inc("include.svh")''', include_paths=[self.output_path])
        result.assert_has_tokens('hello hey')
        result.assert_included_files([join(self.output_path, "include.svh")])

    def test_preprocess_macros_are_recursively_expanded(self):
        result = self.preprocess('''\
`define foo `bar
`define bar xyz
`foo
`define bar abc
`foo
''',
                                 include_paths=[self.output_path])
        result.assert_has_tokens('xyz\nabc\n')

    def test_ifndef_taken(self):
        result = self.preprocess('''\
`ifndef foo
taken
`endif
keep''')
        result.assert_has_tokens("taken\nkeep")

    def test_ifdef_taken(self):
        result = self.preprocess('''\
`define foo
`ifdef foo
taken
`endif
keep''')
        result.assert_has_tokens("taken\nkeep")

    def test_ifdef_else_taken(self):
        result = self.preprocess('''\
`define foo
`ifdef foo
taken
`else
else
`endif
keep''')
        result.assert_has_tokens("taken\nkeep")

    def test_ifdef_not_taken(self):
        result = self.preprocess('''\
`ifdef foo
taken
`endif
keep''')
        result.assert_has_tokens("keep")

    def test_ifdef_else_not_taken(self):
        result = self.preprocess('''\
`ifdef foo
taken
`else
else
`endif
keep''')
        result.assert_has_tokens("else\nkeep")

    def test_ifdef_elsif_taken(self):
        result = self.preprocess('''\
`define foo
`ifdef foo
taken
`elsif bar
elsif_taken
`else
else_taken
`endif
keep''')
        result.assert_has_tokens("taken\nkeep")

    def test_ifdef_elsif_elseif_taken(self):
        result = self.preprocess('''\
`define bar
`ifdef foo
taken
`elsif bar
elsif_taken
`else
else_taken
`endif
keep''')
        result.assert_has_tokens("elsif_taken\nkeep")

    def test_ifdef_elsif_else_taken(self):
        result = self.preprocess('''\
`ifdef foo
taken
`elsif bar
elsif_taken
`else
else_taken
`endif
keep''')
        result.assert_has_tokens("else_taken\nkeep")

    def test_nested_ifdef(self):
        result = self.preprocess('''\
`define foo
`ifdef foo
outer_before
`ifdef bar
inner_ifndef
`else
inner_else
`endif
`ifdef bar
inner_ifndef
`elsif foo
inner_elsif
`endif
outer_after
`endif
keep''')
        result.assert_has_tokens("outer_before\n"
                                 "inner_else\n"
                                 "inner_elsif\n"
                                 "outer_after\n"
                                 "keep")

    def test_preprocess_broken_define(self):
        result = self.preprocess("`define")
        result.assert_has_tokens("")
        result.assert_no_defines()
        result.logger.warning.assert_called_once_with(
            "Verilog `define without argument\n%s",
            "at fn.v line 1:\n"
            "`define\n"
            "~~~~~~~")

    def test_preprocess_broken_define_first_argument(self):
        result = self.preprocess('`define "foo"')
        result.assert_has_tokens("")
        result.assert_no_defines()
        result.logger.warning.assert_called_once_with(
            "Verilog `define invalid name\n%s",
            "at fn.v line 1:\n"
            '`define "foo"\n'
            "        ~~~~~")

    def test_preprocess_broken_define_argument_list(self):
        result = self.preprocess('`define foo(')
        result.assert_has_tokens("")
        result.assert_no_defines()
        result.logger.warning.assert_called_once_with(
            "EOF reached when parsing `define argument list\n%s",
            "at fn.v line 1:\n"
            '`define foo(\n'
            "           ~")

        result = self.preprocess('`define foo(a')
        result.assert_has_tokens("")
        result.assert_no_defines()
        result.logger.warning.assert_called_once_with(
            "EOF reached when parsing `define argument list\n%s",
            "at fn.v line 1:\n"
            '`define foo(a\n'
            "           ~")

        result = self.preprocess('`define foo(a=')
        result.assert_has_tokens("")
        result.assert_no_defines()
        result.logger.warning.assert_called_once_with(
            "EOF reached when parsing `define argument list\n%s",
            "at fn.v line 1:\n"
            '`define foo(a=\n'
            "           ~")

        result = self.preprocess('`define foo(a=b')
        result.assert_has_tokens("")
        result.assert_no_defines()
        result.logger.warning.assert_called_once_with(
            "EOF reached when parsing `define argument list\n%s",
            "at fn.v line 1:\n"
            '`define foo(a=b\n'
            "           ~")

        result = self.preprocess('`define foo(a=)')
        result.assert_has_tokens("")
        result.assert_no_defines()
        result.logger.warning.assert_called_once_with(
            "EOF reached when parsing `define argument list\n%s",
            "at fn.v line 1:\n"
            '`define foo(a=)\n'
            "           ~")

        result = self.preprocess('`define foo("a"')
        result.assert_has_tokens("")
        result.assert_no_defines()
        result.logger.warning.assert_called_once_with(
            "EOF reached when parsing `define argument list\n%s",
            "at fn.v line 1:\n"
            '`define foo("a"\n'
            "           ~")

        result = self.preprocess('`define foo("a"=')
        result.assert_has_tokens("")
        result.assert_no_defines()
        result.logger.warning.assert_called_once_with(
            "EOF reached when parsing `define argument list\n%s",
            "at fn.v line 1:\n"
            '`define foo("a"=\n'
            "           ~")

    def test_preprocess_substitute_define_broken_args(self):
        result = self.preprocess("""\
`define foo(arg1, arg2)arg1,arg2
`foo(1 2)""")
        result.assert_has_tokens("")

        result = self.preprocess("""\
`define foo(arg1, arg2)arg1,arg2
`foo""")
        result.assert_has_tokens("")

        result = self.preprocess("""\
`define foo(arg1, arg2)arg1,arg2
`foo(""")
        result.assert_has_tokens("")

        result = self.preprocess("""\
`define foo(arg1, arg2)arg1,arg2
`foo(1""")
        result.assert_has_tokens("")

    def test_preprocess_substitute_define_missing_argument(self):
        result = self.preprocess("""\
`define foo(arg1, arg2)arg1,arg2
`foo(1)""")
        result.assert_has_tokens("")
        result.logger.warning.assert_called_once_with(
            "Missing value for argument arg2\n%s",
            "at fn.v line 2:\n"
            '`foo(1)\n'
            "~~~~")

    def test_preprocess_substitute_define_too_many_argument(self):
        result = self.preprocess("""\
`define foo(arg1)arg1
`foo(1, 2)""")
        result.assert_has_tokens("")
        result.logger.warning.assert_called_once_with(
            "Too many arguments got 2 expected 1\n%s",
            "at fn.v line 2:\n"
            '`foo(1, 2)\n'
            "~~~~")

    def test_preprocess_substitute_define_eof(self):
        result = self.preprocess("""\
`define foo(arg1, arg2)arg1,arg2
`foo(1 2""")
        result.assert_has_tokens("")
        result.logger.warning.assert_called_once_with(
            "EOF reached when parsing `define actuals\n%s",
            "at fn.v line 2:\n"
            '`foo(1 2\n'
            "~~~~")

    def test_substitute_undefined(self):
        result = self.preprocess('`foo')
        result.assert_has_tokens("")
        # Debug since there are many custon `names in tools
        result.logger.debug.assert_called_once_with(
            "Verilog undefined name\n%s",
            "at fn.v line 1:\n"
            '`foo\n'
            "~~~~")

    def test_preprocess_include_directive_missing_file(self):
        result = self.preprocess('`include "missing.svh"',
                                 include_paths=[self.output_path])
        result.assert_has_tokens("")
        result.assert_included_files([])
        # Is debug message since there are so many builtin includes in tools
        result.logger.debug.assert_called_once_with(
            "Could not find `include file missing.svh\n%s",
            "at fn.v line 1:\n"
            '`include "missing.svh"\n'
            "         ~~~~~~~~~~~~~")

    def test_preprocess_include_directive_missing_argument(self):
        result = self.preprocess('`include',
                                 include_paths=[self.output_path])
        result.assert_has_tokens("")
        result.assert_included_files([])
        result.logger.warning.assert_called_once_with(
            "EOF reached when parsing `include argument\n%s",
            "at fn.v line 1:\n"
            '`include\n'
            "~~~~~~~~")

    def test_preprocess_include_directive_bad_argument(self):
        self.write_file("include.svh", "hello hey")
        result = self.preprocess('`include foo "include.svh"',
                                 include_paths=[self.output_path])
        result.assert_has_tokens(' "include.svh"')
        result.assert_included_files([])
        result.logger.warning.assert_called_once_with(
            "Verilog `include bad argument\n%s",
            "at fn.v line 1:\n"
            '`include foo "include.svh"\n'
            "         ~~~")

    def test_preprocess_include_directive_from_define_bad_argument(self):
        result = self.preprocess('''\
`define inc foo
`include `inc
keep''',
                                 include_paths=[self.output_path])
        result.assert_has_tokens('\nkeep')
        result.assert_included_files([])
        result.logger.warning.assert_called_once_with(
            "Verilog `include has bad argument\n%s",
            "from fn.v line 2:\n"
            '`include `inc\n'
            '         ~~~~\n'
            "at fn.v line 1:\n"
            '`define inc foo\n'
            "            ~~~")

    def test_preprocess_include_directive_from_empty_define(self):
        result = self.preprocess('''\
`define inc
`include `inc
keep''', include_paths=[self.output_path])
        result.assert_has_tokens('\nkeep')
        result.assert_included_files([])
        result.logger.warning.assert_called_once_with(
            "Verilog `include has bad argument, empty define `inc\n%s",
            "at fn.v line 2:\n"
            '`include `inc\n'
            "         ~~~~")

    def test_preprocess_include_directive_from_define_not_defined(self):
        result = self.preprocess('`include `inc', include_paths=[self.output_path])
        result.assert_has_tokens('')
        result.assert_included_files([])
        result.logger.warning.assert_called_once_with(
            "Verilog `include argument not defined\n%s",
            "at fn.v line 1:\n"
            '`include `inc\n'
            "         ~~~~")

    def test_preprocess_error_in_include_file(self):
        self.write_file("include.svh", '`include foo')
        result = self.preprocess('\n\n`include "include.svh"',
                                 include_paths=[self.output_path])
        result.assert_has_tokens('\n\n')
        result.assert_included_files([join(self.output_path, "include.svh")])
        result.logger.warning.assert_called_once_with(
            "Verilog `include bad argument\n%s",
            "from fn.v line 3:\n"
            '`include "include.svh"\n'
            "~~~~~~~~\n"
            "at include.svh line 1:\n"
            '`include foo\n'
            '         ~~~')

    def test_preprocess_error_in_expanded_define(self):
        result = self.preprocess('''\
`define foo `include wrong
`foo
''', include_paths=[self.output_path])
        result.assert_has_tokens('\n')
        result.assert_included_files([])
        result.logger.warning.assert_called_once_with(
            "Verilog `include bad argument\n%s",
            "from fn.v line 2:\n"
            '`foo\n'
            '~~~~\n'
            "at fn.v line 1:\n"
            '`define foo `include wrong\n'
            "                     ~~~~~")

    def test_ifdef_eof(self):
        result = self.preprocess('''\
`ifdef foo
taken''')
        result.assert_has_tokens("")
        result.logger.warning.assert_called_once_with(
            "EOF reached when parsing `ifdef\n%s",
            "at fn.v line 1:\n"
            '`ifdef foo\n'
            '~~~~~~')

    def test_ifdef_bad_argument(self):
        result = self.preprocess('''\
`ifdef "hello"
keep''')
        result.assert_has_tokens("\nkeep")
        result.logger.warning.assert_called_once_with(
            "Bad argument to `ifdef\n%s",
            "at fn.v line 1:\n"
            '`ifdef "hello"\n'
            '       ~~~~~~~')

    def test_elsif_bad_argument(self):
        result = self.preprocess('''\
`ifdef bar
`elsif "hello"
keep''')
        result.assert_has_tokens("\nkeep")
        result.logger.warning.assert_called_once_with(
            "Bad argument to `elsif\n%s",
            "at fn.v line 2:\n"
            '`elsif "hello"\n'
            '       ~~~~~~~')

    def test_undefineall(self):
        result = self.preprocess('''\
`define foo keep
`define bar keep2
`foo
`undefineall''')
        result.assert_has_tokens("keep\n")
        result.assert_no_defines()

    def test_resetall(self):
        result = self.preprocess('''\
`define foo keep
`define bar keep2
`foo
`resetall''')
        result.assert_has_tokens("keep\n")
        result.assert_no_defines()

    def test_undef(self):
        result = self.preprocess('''\
`define foo keep
`define bar keep2
`foo
`undef foo''')
        result.assert_has_tokens("keep\n")
        result.assert_has_defines({"bar": Macro("bar", tokenize("keep2"))})

    def test_undef_eof(self):
        result = self.preprocess('`undef')
        result.assert_has_tokens("")
        result.assert_no_defines()
        result.logger.warning.assert_called_once_with(
            "EOF reached when parsing `undef\n%s",
            "at fn.v line 1:\n"
            '`undef\n'
            '~~~~~~')

    def test_undef_bad_argument(self):
        result = self.preprocess('`undef "foo"')
        result.assert_has_tokens("")
        result.assert_no_defines()
        result.logger.warning.assert_called_once_with(
            "Bad argument to `undef\n%s",
            "at fn.v line 1:\n"
            '`undef "foo"\n'
            '       ~~~~~')

    def test_undef_not_defined(self):
        result = self.preprocess('`undef foo')
        result.assert_has_tokens("")
        result.assert_no_defines()
        result.logger.warning.assert_called_once_with(
            "`undef argument was not previously defined\n%s",
            "at fn.v line 1:\n"
            '`undef foo\n'
            '       ~~~')

    def test_ignores_celldefine(self):
        result = self.preprocess('`celldefine`endcelldefine keep')
        result.assert_has_tokens(" keep")
        result.assert_no_log()

    def test_ignores_timescale(self):
        result = self.preprocess('`timescale 1 ns / 1 ps\nkeep')
        result.assert_has_tokens("\nkeep")
        result.assert_no_log()

    def test_ignores_default_nettype(self):
        result = self.preprocess('`default_nettype none\nkeep')
        result.assert_has_tokens("\nkeep")
        result.assert_no_log()

    def test_ignores_nounconnected_drive(self):
        result = self.preprocess('`nounconnected_drive keep')
        result.assert_has_tokens(" keep")
        result.assert_no_log()

    def test_ignores_unconnected_drive(self):
        result = self.preprocess('`unconnected_drive pull1\nkeep')
        result.assert_has_tokens("\nkeep")
        result.assert_no_log()

    def preprocess(self, code, file_name="fn.v", include_paths=None):
        """
        Tokenize & Preprocess
        """
        tokenizer = VerilogTokenizer()
        preprocessor = VerilogPreprocessor(tokenizer)
        write_file(file_name, code)
        tokens = tokenizer.tokenize(code, file_name=file_name)
        defines = {}
        included_files = []
        with mock.patch("vunit.parsing.verilog.preprocess.LOGGER", autospec=True) as logger:
            tokens = preprocessor.preprocess(tokens, defines, include_paths, included_files)
        return PreprocessResult(self, tokens, defines,
                                [file_name for _, file_name in included_files if file_name is not None],
                                logger)

    def write_file(self, file_name, contents):
        """
        Write file with contents into output path
        """
        full_name = join(self.output_path, file_name)
        full_path = dirname(full_name)
        if not exists(full_path):
            os.makedirs(full_path)
        with open(full_name, "w") as fptr:
            fptr.write(contents)


class PreprocessResult(object):
    """
    Helper object to test preprocessing
    """

    def __init__(self,  # pylint: disable=too-many-arguments
                 test, tokens, defines, included_files, logger):
        self.test = test
        self.tokens = tokens
        self.defines = defines
        self.included_files = included_files
        self.logger = logger

    def assert_has_tokens(self, code, noloc=True):
        """
        Check that tokens are the same as code
        """
        expected = tokenize(code)

        if noloc:
            self.test.assertEqual(strip_loc(self.tokens), strip_loc(expected))
        else:
            self.test.assertEqual(self.tokens, expected)
        return self

    def assert_no_defines(self):
        """
        Assert that there were no defines
        """
        self.test.assertEqual(self.defines, {})

    def assert_included_files(self, included_files):
        """
        Assert that these files where included
        """
        self.test.assertEqual(self.included_files, included_files)

    def assert_has_defines(self, defines):
        """
        Assert that these defines were made
        """
        self.test.assertEqual(self.defines.keys(), defines.keys())

        def macro_strip_loc(define):
            """
            Strip location information from a Macro
            """
            define.tokens = strip_loc(define.tokens)
            for key, value in define.defaults.items():
                define.defaults[key] = strip_loc(value)

        for key in self.defines:
            self.test.assertEqual(macro_strip_loc(self.defines[key]),
                                  macro_strip_loc(defines[key]))

    def assert_no_log(self):
        """
        Assert that no log call were made
        """
        self.test.assertEqual(self.logger.debug.mock_calls, [])
        self.test.assertEqual(self.logger.info.mock_calls, [])
        self.test.assertEqual(self.logger.warning.mock_calls, [])
        self.test.assertEqual(self.logger.error.mock_calls, [])


def tokenize(code, file_name="fn.v"):
    """
    Tokenize
    """
    tokenizer = VerilogTokenizer()
    return tokenizer.tokenize(code, file_name=file_name)


def strip_loc(tokens):
    """
    Strip location information
    """
    return [Token(token.kind, token.value, None) for token in tokens]
