From 6aed0d5afb53434068f37f1b2909590e9dda3d8f Mon Sep 17 00:00:00 2001 From: aaryanshukla <53713108+aaryanshukla@users.noreply.github.com> Date: Wed, 3 Jul 2024 14:29:51 -0700 Subject: [libc] created integration tests for newhdrgen (#97361) - created integration tests for libc hdrgen - implemented sorting function names in yaml files through script --- libc/CMakeLists.txt | 4 ++ libc/newhdrgen/CMakeLists.txt | 17 +++++ .../class_implementation/classes/enumeration.py | 2 +- .../class_implementation/classes/function.py | 6 +- .../class_implementation/classes/object.py | 2 +- libc/newhdrgen/header.py | 24 +++---- libc/newhdrgen/tests/expected_output/test_header.h | 42 ++++++++++++ libc/newhdrgen/tests/input/test_small.h.def | 17 +++++ libc/newhdrgen/tests/input/test_small.yaml | 36 +++++++++++ libc/newhdrgen/tests/output/test_small.h | 42 ++++++++++++ libc/newhdrgen/tests/test_integration.py | 74 ++++++++++++++++++++++ libc/newhdrgen/yaml_to_classes.py | 7 +- 12 files changed, 254 insertions(+), 19 deletions(-) create mode 100644 libc/newhdrgen/CMakeLists.txt create mode 100644 libc/newhdrgen/tests/expected_output/test_header.h create mode 100644 libc/newhdrgen/tests/input/test_small.h.def create mode 100644 libc/newhdrgen/tests/input/test_small.yaml create mode 100644 libc/newhdrgen/tests/output/test_small.h create mode 100644 libc/newhdrgen/tests/test_integration.py (limited to 'libc') diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt index 013b17b..6ba5447 100644 --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -50,6 +50,10 @@ set(LIBC_NAMESPACE ${default_namespace} CACHE STRING "The namespace to use to enclose internal implementations. Must start with '__llvm_libc'." ) + +add_subdirectory(newhdrgen) + + if(LLVM_LIBC_FULL_BUILD OR LLVM_LIBC_GPU_BUILD) if(NOT LIBC_HDRGEN_EXE) # We need to set up hdrgen first since other targets depend on it. diff --git a/libc/newhdrgen/CMakeLists.txt b/libc/newhdrgen/CMakeLists.txt new file mode 100644 index 0000000..33750d1 --- /dev/null +++ b/libc/newhdrgen/CMakeLists.txt @@ -0,0 +1,17 @@ +if(LLVM_LIBC_FULL_BUILD) + + enable_testing() + + set(NEWHDGEN_TESTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tests) + + add_test( + NAME newhdrgen_integration_test + COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR} python3 ${NEWHDGEN_TESTS_DIR}/test_integration.py + ) + + add_custom_target(check-newhdrgen + COMMAND ${CMAKE_CTEST_COMMAND} -R newhdrgen_integration_test + ) + + message(STATUS "Integration test for newhdrgen added.") +endif() diff --git a/libc/newhdrgen/class_implementation/classes/enumeration.py b/libc/newhdrgen/class_implementation/classes/enumeration.py index be03dbf..a01fa74 100644 --- a/libc/newhdrgen/class_implementation/classes/enumeration.py +++ b/libc/newhdrgen/class_implementation/classes/enumeration.py @@ -10,7 +10,7 @@ class Enumeration: - def __init__(self, name, value=None): + def __init__(self, name, value): self.name = name self.value = value diff --git a/libc/newhdrgen/class_implementation/classes/function.py b/libc/newhdrgen/class_implementation/classes/function.py index 3c464e4..f79b53d 100644 --- a/libc/newhdrgen/class_implementation/classes/function.py +++ b/libc/newhdrgen/class_implementation/classes/function.py @@ -11,19 +11,19 @@ class Function: def __init__( - self, standards, return_type, name, arguments, guard=None, attributes=[] + self, return_type, name, arguments, standards, guard=None, attributes=[] ): - self.standards = standards self.return_type = return_type self.name = name self.arguments = [ arg if isinstance(arg, str) else arg["type"] for arg in arguments ] + self.standards = standards self.guard = guard self.attributes = attributes or [] def __str__(self): - attributes_str = " ".join(self.attributes) + attributes_str = self.attributes arguments_str = ", ".join(self.arguments) result = f"{self.return_type} {self.name}({arguments_str}){attributes_str};" if self.guard: diff --git a/libc/newhdrgen/class_implementation/classes/object.py b/libc/newhdrgen/class_implementation/classes/object.py index c65a82e..02f30cb 100644 --- a/libc/newhdrgen/class_implementation/classes/object.py +++ b/libc/newhdrgen/class_implementation/classes/object.py @@ -15,4 +15,4 @@ class Object: self.type = type def __str__(self): - return f"extern {self.type} {self.name}" + return f"extern {self.type} {self.name};" diff --git a/libc/newhdrgen/header.py b/libc/newhdrgen/header.py index 7ce3568..4eaf7dc 100644 --- a/libc/newhdrgen/header.py +++ b/libc/newhdrgen/header.py @@ -44,24 +44,24 @@ class HeaderFile: content.append(str(include)) for macro in self.macros: - content.append(str(macro)) - - for object in self.objects: - content.append(str(object)) + content.append(f"{macro}\n") for type_ in self.types: - content.append(str(type_)) + content.append(f"{type_}") if self.enumerations: - content.append("enum {") - for enum in self.enumerations: - content.append(f"\t{str(enum)},") - content.append("};") + combined_enum_content = ",\n ".join( + str(enum) for enum in self.enumerations + ) + content.append(f"\nenum {{\n {combined_enum_content},\n}};") + + content.append("\n__BEGIN_C_DECLS\n") - # TODO: replace line below with common.h functionality - content.append("__BEGIN_C_DECLS\n") for function in self.functions: content.append(str(function)) content.append("") - content.append("__END_C_DECLS\n") + for object in self.objects: + content.append(str(object)) + content.append("\n__END_C_DECLS") + return "\n".join(content) diff --git a/libc/newhdrgen/tests/expected_output/test_header.h b/libc/newhdrgen/tests/expected_output/test_header.h new file mode 100644 index 0000000..d6ae0d0 --- /dev/null +++ b/libc/newhdrgen/tests/expected_output/test_header.h @@ -0,0 +1,42 @@ +//===-- C standard library header test_small-------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_TEST_SMALL_H +#define LLVM_LIBC_TEST_SMALL_H + +#include "__llvm-libc-common.h" +#include "llvm-libc-macros/test_small-macros.h" + +#define MACRO_A 1 + +#define MACRO_B 2 + +#include +#include + +enum { + enum_a = value_1, + enum_b = value_2, +}; + +__BEGIN_C_DECLS + +#ifdef FUNC_A_16 +void func_a()CONST_FUNC_A; +#endif // FUNC_A_16 + +#ifdef FUNC_B_16 +int func_b(int, float)CONST_FUNC_B; +#endif // FUNC_B_16 + +extern obj object_1; +extern obj object_2; + +__END_C_DECLS + +#endif // LLVM_LIBC_TEST_SMALL_H diff --git a/libc/newhdrgen/tests/input/test_small.h.def b/libc/newhdrgen/tests/input/test_small.h.def new file mode 100644 index 0000000..de39a8b --- /dev/null +++ b/libc/newhdrgen/tests/input/test_small.h.def @@ -0,0 +1,17 @@ +//===-- C standard library header test_small-------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_TEST_SMALL_H +#define LLVM_LIBC_TEST_SMALL_H + +#include "__llvm-libc-common.h" +#include "llvm-libc-macros/test_small-macros.h" + +%%public_api() + +#endif // LLVM_LIBC_TEST_SMALL_H diff --git a/libc/newhdrgen/tests/input/test_small.yaml b/libc/newhdrgen/tests/input/test_small.yaml new file mode 100644 index 0000000..0bc292d --- /dev/null +++ b/libc/newhdrgen/tests/input/test_small.yaml @@ -0,0 +1,36 @@ +header: test_header.h +macros: + - macro_name: MACRO_A + macro_value: 1 + - macro_name: MACRO_B + macro_value: 2 +types: + - type_name: type_a + - type_name: type_b +enums: + - name: enum_a + value: value_1 + - name: enum_b + value: value_2 +objects: + - object_name: object_1 + object_type: obj + - object_name: object_2 + object_type: obj +functions: + - name: func_a + return_type: void + arguments: [] + standards: + - stdc + guard: FUNC_A_16 + attributes: CONST_FUNC_A + - name: func_b + return_type: int + arguments: + - type: int + - type: float + standards: + - stdc + guard: FUNC_B_16 + attributes: CONST_FUNC_B diff --git a/libc/newhdrgen/tests/output/test_small.h b/libc/newhdrgen/tests/output/test_small.h new file mode 100644 index 0000000..d6ae0d0 --- /dev/null +++ b/libc/newhdrgen/tests/output/test_small.h @@ -0,0 +1,42 @@ +//===-- C standard library header test_small-------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_TEST_SMALL_H +#define LLVM_LIBC_TEST_SMALL_H + +#include "__llvm-libc-common.h" +#include "llvm-libc-macros/test_small-macros.h" + +#define MACRO_A 1 + +#define MACRO_B 2 + +#include +#include + +enum { + enum_a = value_1, + enum_b = value_2, +}; + +__BEGIN_C_DECLS + +#ifdef FUNC_A_16 +void func_a()CONST_FUNC_A; +#endif // FUNC_A_16 + +#ifdef FUNC_B_16 +int func_b(int, float)CONST_FUNC_B; +#endif // FUNC_B_16 + +extern obj object_1; +extern obj object_2; + +__END_C_DECLS + +#endif // LLVM_LIBC_TEST_SMALL_H diff --git a/libc/newhdrgen/tests/test_integration.py b/libc/newhdrgen/tests/test_integration.py new file mode 100644 index 0000000..228fecc --- /dev/null +++ b/libc/newhdrgen/tests/test_integration.py @@ -0,0 +1,74 @@ +import subprocess +import unittest +from pathlib import Path +import os +import argparse + + +class TestHeaderGenIntegration(unittest.TestCase): + def setUp(self): + parser = argparse.ArgumentParser( + description="TestHeaderGenIntegration arguments" + ) + parser.add_argument( + "--output_dir", type=str, help="Output directory for generated headers" + ) + args, _ = parser.parse_known_args() + output_dir_env = os.getenv("TEST_OUTPUT_DIR") + + self.output_dir = Path( + args.output_dir + if args.output_dir + else output_dir_env if output_dir_env else "libc/newhdrgen/tests/output" + ) + + self.maxDiff = None + # Adjust based on your directory structure such as being in build etc. + self.source_dir = Path(__file__).resolve().parent.parent.parent.parent + + def run_script(self, yaml_file, h_def_file, output_dir): + yaml_file = self.source_dir / yaml_file + h_def_file = self.source_dir / h_def_file + result = subprocess.run( + [ + "python3", + str(self.source_dir / "libc/newhdrgen/yaml_to_classes.py"), + str(yaml_file), + str(h_def_file), + "--output_dir", + str(output_dir), + ], + capture_output=True, + text=True, + ) + + print("STDOUT:", result.stdout) + print("STDERR:", result.stderr) + result.check_returncode() + + def compare_files(self, generated_file, expected_file): + with generated_file.open("r") as gen_file: + gen_content = gen_file.read() + with expected_file.open("r") as exp_file: + exp_content = exp_file.read() + + self.assertEqual(gen_content, exp_content) + + def test_generate_header(self): + yaml_file = "libc/newhdrgen/tests/input/test_small.yaml" + h_def_file = "libc/newhdrgen/tests/input/test_small.h.def" + expected_output_file = ( + self.source_dir / "libc/newhdrgen/tests/expected_output/test_header.h" + ) + output_file = self.output_dir / "test_small.h" + + if not self.output_dir.exists(): + self.output_dir.mkdir(parents=True) + + self.run_script(yaml_file, h_def_file, self.output_dir) + + self.compare_files(output_file, expected_output_file) + + +if __name__ == "__main__": + unittest.main() diff --git a/libc/newhdrgen/yaml_to_classes.py b/libc/newhdrgen/yaml_to_classes.py index 9b52c9c..9e0337f 100644 --- a/libc/newhdrgen/yaml_to_classes.py +++ b/libc/newhdrgen/yaml_to_classes.py @@ -46,11 +46,13 @@ def yaml_to_classes(yaml_data): Enumeration(enum_data["name"], enum_data.get("value", None)) ) - for function_data in yaml_data.get("functions", []): + functions = yaml_data.get("functions", []) + sorted_functions = sorted(functions, key=lambda x: x["name"]) + for function_data in sorted_functions: arguments = [arg["type"] for arg in function_data["arguments"]] guard = function_data.get("guard", None) attributes = function_data.get("attributes", None) - standards = (function_data.get("standards", None),) + standards = function_data.get("standards", None) header.add_function( Function( function_data["return_type"], @@ -99,6 +101,7 @@ def fill_public_api(header_str, h_def_content): Returns: The final header content with the public API filled in. """ + header_str = header_str.strip() return h_def_content.replace("%%public_api()", header_str, 1) -- cgit v1.1