Skip to content
Open
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
80 changes: 72 additions & 8 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,75 @@ def print_summary(results: list[tuple[str, bool, float, str, Optional[str]]]):
f"{color(str(failed) + ' failed', Colors.RED)}, "
f"{total_time:.1f}s total")

# ---------------------------------------------------------------------------
# MODULE VALIDATION HELPERS
# ---------------------------------------------------------------------------

def parse_module_selection(module_str: str) -> list[str]:
"""Parse comma-separated module names with optional spaces.

Args:
module_str: Comma-separated module names (e.g., "backend, frontend")

Returns:
List of stripped module names

Examples:
>>> parse_module_selection("backend")
['backend']
>>> parse_module_selection("backend, frontend , market")
['backend', 'frontend', 'market']
>>> parse_module_selection("all")
['all']
"""
if not module_str or module_str.strip() == "all":
return ["all"]
return [n.strip() for n in module_str.split(",") if n.strip()]


def validate_module_names(names: list[str], available_modules: list['Module']) -> tuple[list['Module'], list[str]]:
"""Validate module names against available modules.

Args:
names: List of module names to validate
available_modules: List of available Module objects

Returns:
Tuple of (valid_modules, invalid_names)
"""
valid = []
invalid = []
available_names = {m.name for m in available_modules}

for name in names:
if name == "all":
return available_modules, []
found = [m for m in available_modules if m.name == name]
if found:
valid.extend(found)
else:
invalid.append(name)

return valid, invalid


def list_modules(available_modules: list['Module']) -> None:
"""Print available modules with details.

Args:
available_modules: List of Module objects to display
"""
print(f" {color('Available modules:', Colors.BOLD)}")
for m in available_modules:
print(f" {color(m.name, Colors.CYAN)} ({m.language})")
print(f" dir: {m.dir.relative_to(ROOT)}")
print(f" build: {' '.join(m.build_cmd)}")
if m.clean_cmd:
print(f" clean: {' '.join(m.clean_cmd)}")
if m.build_dir:
print(f" output: {m.build_dir.relative_to(ROOT)}")


def main():
parser = argparse.ArgumentParser(
description="Tent of Trials - Multi-Language Build System",
Expand Down Expand Up @@ -646,11 +715,7 @@ def main():
print()

if args.list:
print(f" {color('Available modules:', Colors.BOLD)}")
for m in MODULES:
print(f" {color(m.name, Colors.CYAN)} ({m.language})")
print(f" dir: {m.dir.relative_to(ROOT)}")
print(f" build: {' '.join(m.build_cmd)}")
list_modules(MODULES)
return 0

print(f" {color('Checking prerequisites...', Colors.GRAY)}")
Expand All @@ -666,9 +731,8 @@ def main():
if args.module == "all":
selected = MODULES
else:
names = [n.strip() for n in args.module.split(",")]
selected = [m for m in MODULES if m.name in names]
not_found = set(names) - {m.name for m in MODULES}
names = parse_module_selection(args.module)
selected, not_found = validate_module_names(names, MODULES)
if not_found:
print(f" {color('✗ Unknown modules:', Colors.RED)} {', '.join(not_found)}")
print(f" Available: {', '.join(m.name for m in MODULES)}")
Expand Down
55 changes: 55 additions & 0 deletions tests/test_module_validation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env python3
"""Tests for module validation helpers."""
import os
import sys
import unittest

sys.path.insert(0, os.path.dirname(__file__))
from build import parse_module_selection, validate_module_selection, list_modules, MODULES


class TestParseModuleSelection(unittest.TestCase):
def test_single_module(self):
self.assertEqual(parse_module_selection("backend"), ["backend"])

def test_multiple_modules(self):
self.assertEqual(parse_module_selection("backend,frontend"), ["backend", "frontend"])

def test_with_spaces(self):
self.assertEqual(parse_module_selection("backend , frontend , market"), ["backend", "frontend", "market"])

def test_all_keyword(self):
self.assertEqual(parse_module_selection("all"), ["all"])

def test_empty_string(self):
self.assertEqual(parse_module_selection(""), ["all"])

def test_whitespace_only(self):
self.assertEqual(parse_module_selection(" "), ["all"])


class TestValidateModuleNames(unittest.TestCase):
def test_valid_module(self):
valid, invalid = validate_module_selection(["backend"], MODULES)
self.assertEqual(len(valid), 1)
self.assertEqual(valid[0].name, "backend")
self.assertEqual(invalid, [])

def test_invalid_module(self):
valid, invalid = validate_module_selection(["nonexistent"], MODULES)
self.assertEqual(valid, [])
self.assertEqual(invalid, ["nonexistent"])

def test_mixed_valid_invalid(self):
valid, invalid = validate_module_selection(["backend", "nonexistent"], MODULES)
self.assertEqual(len(valid), 1)
self.assertEqual(invalid, ["nonexistent"])

def test_all_keyword(self):
valid, invalid = validate_module_selection(["all"], MODULES)
self.assertEqual(len(valid), len(MODULES))
self.assertEqual(invalid, [])


if __name__ == "__main__":
unittest.main()