Skip to content
Draft
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
2 changes: 2 additions & 0 deletions codeplain_REST_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ def render_conformance_tests(
conformance_tests_folder_name,
conformance_tests_json,
all_acceptance_tests,
existing_conformance_tests_files,
run_state: RunState,
):
endpoint_url = f"{self.api_url}/render_conformance_tests"
Expand All @@ -333,6 +334,7 @@ def render_conformance_tests(
"conformance_tests_folder_name": conformance_tests_folder_name,
"conformance_tests_json": conformance_tests_json,
"all_acceptance_tests": all_acceptance_tests,
"existing_conformance_tests_files": existing_conformance_tests_files,
}

response = self.post_request(endpoint_url, headers, payload, run_state)
Expand Down
13 changes: 13 additions & 0 deletions render_machine/actions/render_conformance_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,18 @@ def _render_conformance_tests(self, render_context: RenderContext):

all_acceptance_tests = render_context.frid_context.specifications.get(plain_spec.ACCEPTANCE_TESTS, [])

# Existing conformance tests are sent as context so new tests don't duplicate their
# coverage. The current functionality's own subfolder is excluded - when re-rendering,
# its tests are the ones being replaced, not previous tests to deduplicate against.
current_subfolder_prefix = os.path.basename(conformance_tests_folder_name) + os.sep
existing_conformance_tests_files = {
file_name: content
for file_name, content in render_context.conformance_tests.fetch_all_existing_conformance_test_files(
render_context.module_name
).items()
if not file_name.startswith(current_subfolder_prefix)
}

response_files, implementation_plan_summary = render_context.codeplain_api.render_conformance_tests(
render_context.frid_context.frid,
render_context.conformance_tests_running_context.current_testing_frid,
Expand All @@ -125,6 +137,7 @@ def _render_conformance_tests(self, render_context: RenderContext):
render_context.conformance_tests_running_context.current_testing_module_name
),
all_acceptance_tests,
existing_conformance_tests_files,
run_state=render_context.run_state,
)

Expand Down
20 changes: 20 additions & 0 deletions render_machine/conformance_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,26 @@ def store_conformance_tests_files(
style=console.OUTPUT_STYLE,
)

def fetch_all_existing_conformance_test_files(self, module_name: str) -> dict[str, str]:
"""Fetch the content of all existing conformance test files of the module.

Files are collected from every conformance test subfolder of the module (one subfolder
per functional requirement) and keyed as "<subfolder>/<relative file path>" so each
file's suite remains identifiable. Hidden subfolders (copies of required modules' tests)
and the conformance tests definition file (stored at the module folder root) are not
included. Returns an empty dict when the module has no conformance tests yet.
"""
all_files_content: dict[str, str] = {}
module_folder = self.get_module_conformance_tests_folder(module_name)
for folder_name in sorted(self.fetch_existing_conformance_test_folder_names(module_name)):
folder_path = os.path.join(module_folder, folder_name)
file_names = file_utils.list_all_text_files(folder_path)
files_content = file_utils.get_existing_files_content(folder_path, file_names)
for file_name, content in files_content.items():
all_files_content[os.path.join(folder_name, file_name)] = content

return all_files_content

def fetch_existing_conformance_test_files(
self,
module_name: str,
Expand Down
78 changes: 78 additions & 0 deletions tests/test_conformance_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import json
import os
import tempfile

import pytest

from render_machine.conformance_tests import CONFORMANCE_TESTS_DEFINITION_FILE_NAME, ConformanceTests

MODULE_NAME = "my_module"


@pytest.fixture
def conformance_tests_dir():
with tempfile.TemporaryDirectory() as d:
yield d


@pytest.fixture
def conformance_tests(conformance_tests_dir):
return ConformanceTests(conformance_tests_dir, CONFORMANCE_TESTS_DEFINITION_FILE_NAME)


def _write_file(base_dir, relative_path, content):
full_path = os.path.join(base_dir, relative_path)
os.makedirs(os.path.dirname(full_path), exist_ok=True)
with open(full_path, "w") as f:
f.write(content)


def test_fetch_all_existing_conformance_test_files_empty_when_module_folder_missing(conformance_tests):
assert conformance_tests.fetch_all_existing_conformance_test_files(MODULE_NAME) == {}


def test_fetch_all_existing_conformance_test_files_multiple_folders(conformance_tests, conformance_tests_dir):
module_folder = os.path.join(conformance_tests_dir, MODULE_NAME)
_write_file(module_folder, os.path.join("first_functionality", "test_first.py"), "first test")
_write_file(module_folder, os.path.join("second_functionality", "test_second.py"), "second test")
_write_file(module_folder, os.path.join("second_functionality", "helpers", "util.py"), "helper")

files_content = conformance_tests.fetch_all_existing_conformance_test_files(MODULE_NAME)

assert files_content == {
os.path.join("first_functionality", "test_first.py"): "first test",
os.path.join("second_functionality", "test_second.py"): "second test",
os.path.join("second_functionality", "helpers", "util.py"): "helper",
}


def test_fetch_all_existing_conformance_test_files_excludes_hidden_folders(conformance_tests, conformance_tests_dir):
module_folder = os.path.join(conformance_tests_dir, MODULE_NAME)
_write_file(module_folder, os.path.join("own_functionality", "test_own.py"), "own test")
_write_file(module_folder, os.path.join(".required_module", "some_frid", "test_required.py"), "required test")

files_content = conformance_tests.fetch_all_existing_conformance_test_files(MODULE_NAME)

assert files_content == {os.path.join("own_functionality", "test_own.py"): "own test"}


def test_fetch_all_existing_conformance_test_files_excludes_definition_file(conformance_tests, conformance_tests_dir):
module_folder = os.path.join(conformance_tests_dir, MODULE_NAME)
_write_file(module_folder, os.path.join("some_functionality", "test_some.py"), "some test")
_write_file(module_folder, CONFORMANCE_TESTS_DEFINITION_FILE_NAME, json.dumps({"frid": {}}))

files_content = conformance_tests.fetch_all_existing_conformance_test_files(MODULE_NAME)

assert files_content == {os.path.join("some_functionality", "test_some.py"): "some test"}


def test_fetch_all_existing_conformance_test_files_skips_binary_files(conformance_tests, conformance_tests_dir):
module_folder = os.path.join(conformance_tests_dir, MODULE_NAME)
_write_file(module_folder, os.path.join("some_functionality", "test_some.py"), "some test")
binary_file_path = os.path.join(module_folder, "some_functionality", "fixture.bin")
with open(binary_file_path, "wb") as f:
f.write(b"\x89PNG\r\n\x1a\n\x00\x00\xff\xfe\xfd")

files_content = conformance_tests.fetch_all_existing_conformance_test_files(MODULE_NAME)

assert files_content == {os.path.join("some_functionality", "test_some.py"): "some test"}
Loading