Skip to content

[libc] created integration tests for newhdrgen #97361

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions libc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,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.
Expand Down
17 changes: 17 additions & 0 deletions libc/newhdrgen/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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()
2 changes: 1 addition & 1 deletion libc/newhdrgen/class_implementation/classes/enumeration.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@


class Enumeration:
def __init__(self, name, value=None):
def __init__(self, name, value):
self.name = name
self.value = value

Expand Down
6 changes: 3 additions & 3 deletions libc/newhdrgen/class_implementation/classes/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion libc/newhdrgen/class_implementation/classes/object.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ def __init__(self, name, type):
self.type = type

def __str__(self):
return f"extern {self.type} {self.name}"
return f"extern {self.type} {self.name};"
24 changes: 12 additions & 12 deletions libc/newhdrgen/header.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,24 +44,24 @@ def __str__(self):
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)
42 changes: 42 additions & 0 deletions libc/newhdrgen/tests/expected_output/test_header.h
Original file line number Diff line number Diff line change
@@ -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 <llvm-libc-types/type_a.h>
#include <llvm-libc-types/type_b.h>

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
17 changes: 17 additions & 0 deletions libc/newhdrgen/tests/input/test_small.h.def
Original file line number Diff line number Diff line change
@@ -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
36 changes: 36 additions & 0 deletions libc/newhdrgen/tests/input/test_small.yaml
Original file line number Diff line number Diff line change
@@ -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
42 changes: 42 additions & 0 deletions libc/newhdrgen/tests/output/test_small.h
Original file line number Diff line number Diff line change
@@ -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 <llvm-libc-types/type_a.h>
#include <llvm-libc-types/type_b.h>

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
74 changes: 74 additions & 0 deletions libc/newhdrgen/tests/test_integration.py
Original file line number Diff line number Diff line change
@@ -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()
7 changes: 5 additions & 2 deletions libc/newhdrgen/yaml_to_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down Expand Up @@ -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)


Expand Down
Loading