# SPDX-License-Identifier: LGPL-2.1-only # Copyright 2022 Jookia <contact@jookia.org> from hypothesis import given, assume from hypothesis.strategies import ( booleans, characters, composite, integers, lists, one_of, sampled_from, text, ) from src.syntax import Syntax, SyntaxLocation, SyntaxStream, SyntaxType # Keywords recognized by the language keywords = [ "NewLang", "Done", "Set", "To", "EndSet", "If", "Then", "Else", "EndIf", "StartNote", "EndNote", "StartText", "EndText", ] # Draws a random token location @composite def draw_token_location(draw): line = draw(integers()) column = draw(integers()) filename = draw(text()) return SyntaxLocation(line, column, filename) # Draws a random syntax type @composite def draw_syntax_type(draw): return draw(sampled_from(list(SyntaxType))) # Draws a token syntax value @composite def draw_syntax_token(draw): value = draw(draw_token_classified()) location = draw(draw_token_location()) type = SyntaxType.TOKEN return Syntax(value.value, location, type) # Draws a text syntax value @composite def draw_syntax_text(draw): value = draw(text()) location = draw(draw_token_location()) type = SyntaxType.TEXT return Syntax(value, location, type) # Draws a random syntax @composite def draw_syntax_random(draw): strategies = [ draw_syntax_token(), draw_syntax_text(), ] return draw(one_of(strategies)) # Test syntax getters @given(text(), draw_token_location(), draw_syntax_type()) def test_syntax_syntax_getters(value, location, type): # Use text as a somewhat random value test = Syntax(value, location, type) assert test.value == value assert test.location == location assert test.type == type # Test syntax equals @given(draw_syntax_random(), draw_syntax_random()) def test_syntax_syntax_equality(syntax1, syntax2): equals = ( syntax1.type == syntax2.type and syntax1.value == syntax2.value and syntax1.location == syntax2.location ) assert (syntax1 == syntax2) == equals # Draws a random token @composite def draw_token_random(draw): value = draw(text()) location = draw(draw_token_location()) return Syntax(value, location, SyntaxType.TOKEN) # Draws an unknown token @composite def draw_token_unknown(draw): reserved = " \n\t" token = draw(draw_token_random()) chars = characters(blacklist_characters=reserved) value = draw(text(alphabet=chars, min_size=1)) assume(value not in ["True", "False"]) assume(value not in keywords) assume(value[0:2] != "#!") return Syntax(value, token.location, SyntaxType.TOKEN) # Draws a space token @composite def draw_token_space(draw): space = " \t" token = draw(draw_token_random()) value = draw(sampled_from(space)) return Syntax(value, token.location, SyntaxType.TOKEN) # Draws a new line token @composite def draw_token_newline(draw): token = draw(draw_token_random()) value = "\n" return Syntax(value, token.location, SyntaxType.TOKEN) # Draws a bool token @composite def draw_token_bool(draw): token = draw(draw_token_random()) if draw(booleans()): value = "True" else: value = "False" return Syntax(value, token.location, SyntaxType.TOKEN) # Draws a keyword token @composite def draw_token_keyword(draw): token = draw(draw_token_random()) value = draw(sampled_from(keywords)) return Syntax(value, token.location, SyntaxType.TOKEN) # Draws a shebang token @composite def draw_token_shebang(draw): token = draw(draw_token_random()) value = "#!" + draw(text()) return Syntax(value, token.location, SyntaxType.TOKEN) # Draws a classified token @composite def draw_token_classified(draw): strategies = [ draw_token_unknown(), draw_token_space(), draw_token_newline(), draw_token_bool(), draw_token_keyword(), draw_token_shebang(), ] token = draw(one_of(strategies)) return token # Test location getters @given(integers(), integers(), text()) def test_syntax_location_getters(line, column, filename): test = SyntaxLocation(line, column, filename) assert test.line == line assert test.column == column assert test.file == filename # Test location equals @given(draw_token_location(), draw_token_location()) def test_syntax_location_equality(location1, location2): equals = ( location1.line == location2.line and location1.column == location2.column and location1.file == location2.file ) assert (location1 == location2) == equals # Test token getters @given(text(), draw_token_location()) def test_syntax_token_getters(value, location): test = Syntax(value, location, SyntaxType.TOKEN) assert test.value == value assert test.location == location # Test token equals @given(draw_token_random(), draw_token_random()) def test_syntax_token_equality(token1, token2): equals = token1.value == token2.value and token1.location == token2.location assert (token1 == token2) == equals # Tests that a syntax stream reads items correctly # We expect the following behaviour: # - All items are popped in order # - None is returned at the end of the stream @given(lists(draw_syntax_random())) def test_syntax_syntax_stream(nodes): stream = SyntaxStream(nodes.copy()) read = [] node = stream.pop() while node is not None: read.append(node) node = stream.pop() assert read == nodes assert stream.pop() is None