mirror of
https://github.com/isledecomp/isle.git
synced 2025-10-22 07:54:23 +00:00

* Adjustments to "decomp" language * Fix a comment * Fix accidental clang-formatting * Fix order * Fix order * Remove junk * Fix OFFSET * Adjustments based on new suggestions * Annotate globals * Globals in ISLE * More globals * Merge from parser2 branch * Allow prepending space for exact marker match * To eliminate noise, require the 0x prefix on offset for marker match * fix test from previous * Count tab stops for indented functions to reduce MISSED_END_OF_FUNCTION noise * FUNCTION to SYNTHETIC where needed * Missed marker conversion on SetAtomId * pylint cleanup, remove unused code * Fix unexpected function end, add more unit tests * Be more strict about synthetic name syntax * Revert "Missed marker conversion on SetAtomId" This reverts commitd87d665127
. * Revert "FUNCTION to SYNTHETIC where needed" This reverts commit8c815418d2
. * Implicit lookup by name for functions * Fix VTABLE SYNTHETIC and other decomp markers * Get vtable class name * Vtable marker should identify struct * No colon for SIZE comment * Update README.md * Update README.md * Update CONTRIBUTING.md * Update README.md * Update README.md * Update CONTRIBUTING.md * Update README.md * Update CONTRIBUTING.md * Fix destructor/annotation * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md --------- Co-authored-by: disinvite <disinvite@users.noreply.github.com>
142 lines
4.5 KiB
Python
142 lines
4.5 KiB
Python
import os
|
|
from typing import List, TextIO
|
|
import pytest
|
|
from isledecomp.parser import DecompParser
|
|
from isledecomp.parser.node import ParserSymbol
|
|
|
|
SAMPLE_DIR = os.path.join(os.path.dirname(__file__), "samples")
|
|
|
|
|
|
def sample_file(filename: str) -> TextIO:
|
|
"""Wrapper for opening the samples from the directory that does not
|
|
depend on the cwd where we run the test"""
|
|
full_path = os.path.join(SAMPLE_DIR, filename)
|
|
return open(full_path, "r", encoding="utf-8")
|
|
|
|
|
|
def code_blocks_are_sorted(blocks: List[ParserSymbol]) -> bool:
|
|
"""Helper to make this more idiomatic"""
|
|
just_offsets = [block.offset for block in blocks]
|
|
return just_offsets == sorted(just_offsets)
|
|
|
|
|
|
@pytest.fixture(name="parser")
|
|
def fixture_parser():
|
|
return DecompParser()
|
|
|
|
|
|
# Tests are below #
|
|
|
|
|
|
def test_sanity(parser):
|
|
"""Read a very basic file"""
|
|
with sample_file("basic_file.cpp") as f:
|
|
parser.read_lines(f)
|
|
|
|
assert len(parser.functions) == 3
|
|
assert code_blocks_are_sorted(parser.functions) is True
|
|
# n.b. The parser returns line numbers as 1-based
|
|
# Function starts when we see the opening curly brace
|
|
assert parser.functions[0].line_number == 8
|
|
assert parser.functions[0].end_line == 10
|
|
|
|
|
|
def test_oneline(parser):
|
|
"""(Assuming clang-format permits this) This sample has a function
|
|
on a single line. This will test the end-of-function detection"""
|
|
with sample_file("oneline_function.cpp") as f:
|
|
parser.read_lines(f)
|
|
|
|
assert len(parser.functions) == 2
|
|
assert parser.functions[0].line_number == 5
|
|
assert parser.functions[0].end_line == 5
|
|
|
|
|
|
def test_missing_offset(parser):
|
|
"""What if the function doesn't have an offset comment?"""
|
|
with sample_file("missing_offset.cpp") as f:
|
|
parser.read_lines(f)
|
|
|
|
# TODO: For now, the function without the offset will just be ignored.
|
|
# Would be the same outcome if the comment was present but mangled and
|
|
# we failed to match it. We should detect these cases in the future.
|
|
assert len(parser.functions) == 1
|
|
|
|
|
|
def test_jumbled_case(parser):
|
|
"""The parser just reports what it sees. It is the responsibility of
|
|
the downstream tools to do something about a jumbled file.
|
|
Just verify that we are reading it correctly."""
|
|
with sample_file("out_of_order.cpp") as f:
|
|
parser.read_lines(f)
|
|
|
|
assert len(parser.functions) == 3
|
|
assert code_blocks_are_sorted(parser.functions) is False
|
|
|
|
|
|
def test_bad_file(parser):
|
|
with sample_file("poorly_formatted.cpp") as f:
|
|
parser.read_lines(f)
|
|
|
|
assert len(parser.functions) == 3
|
|
|
|
|
|
def test_indented(parser):
|
|
"""Offsets for functions inside of a class will probably be indented."""
|
|
with sample_file("basic_class.cpp") as f:
|
|
parser.read_lines(f)
|
|
|
|
# TODO: We don't properly detect the end of these functions
|
|
# because the closing brace is indented. However... knowing where each
|
|
# function ends is less important (for now) than capturing
|
|
# all the functions that are there.
|
|
|
|
assert len(parser.functions) == 2
|
|
assert parser.functions[0].offset == int("0x12345678", 16)
|
|
assert parser.functions[0].line_number == 16
|
|
# assert parser.functions[0].end_line == 19
|
|
|
|
assert parser.functions[1].offset == int("0xdeadbeef", 16)
|
|
assert parser.functions[1].line_number == 23
|
|
# assert parser.functions[1].end_line == 25
|
|
|
|
|
|
def test_inline(parser):
|
|
with sample_file("inline.cpp") as f:
|
|
parser.read_lines(f)
|
|
|
|
assert len(parser.functions) == 2
|
|
for fun in parser.functions:
|
|
assert fun.line_number is not None
|
|
assert fun.line_number == fun.end_line
|
|
|
|
|
|
def test_multiple_offsets(parser):
|
|
"""If multiple offset marks appear before for a code block, take them
|
|
all but ensure module name (case-insensitive) is distinct.
|
|
Use first module occurrence in case of duplicates."""
|
|
with sample_file("multiple_offsets.cpp") as f:
|
|
parser.read_lines(f)
|
|
|
|
assert len(parser.functions) == 4
|
|
assert parser.functions[0].module == "TEST"
|
|
assert parser.functions[0].line_number == 9
|
|
|
|
assert parser.functions[1].module == "HELLO"
|
|
assert parser.functions[1].line_number == 9
|
|
|
|
# Duplicate modules are ignored
|
|
assert parser.functions[2].line_number == 16
|
|
assert parser.functions[2].offset == 0x2345
|
|
|
|
assert parser.functions[3].module == "TEST"
|
|
assert parser.functions[3].offset == 0x2002
|
|
|
|
|
|
def test_variables(parser):
|
|
with sample_file("global_variables.cpp") as f:
|
|
parser.read_lines(f)
|
|
|
|
assert len(parser.functions) == 1
|
|
assert len(parser.variables) == 2
|