Skip to content
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
18 changes: 18 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -258,5 +258,23 @@ target_link_libraries(test-frameforge-validator PRIVATE common)
add_test(NAME test-frameforge-validator COMMAND $<TARGET_FILE:test-frameforge-validator>)
set_property(TEST test-frameforge-validator PROPERTY LABELS "main")

# FrameForge JSON loader tests
add_executable(test-frameforge-json-loader test-frameforge-json-loader.cpp
${CMAKE_SOURCE_DIR}/tools/frameforge/frameforge-schema.cpp)
target_include_directories(test-frameforge-json-loader PRIVATE ${CMAKE_SOURCE_DIR}/tools/frameforge ${CMAKE_SOURCE_DIR}/vendor)
target_link_libraries(test-frameforge-json-loader PRIVATE common)
add_test(NAME test-frameforge-json-loader COMMAND $<TARGET_FILE:test-frameforge-json-loader> WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests)
set_property(TEST test-frameforge-json-loader PROPERTY LABELS "main")

# FrameForge new features tests (master verbs, timestamp, etc.)
add_executable(test-frameforge-new-features test-frameforge-new-features.cpp
${CMAKE_SOURCE_DIR}/tools/frameforge/frameforge-schema.cpp
${CMAKE_SOURCE_DIR}/tools/frameforge/frameforge-validator.cpp
${CMAKE_SOURCE_DIR}/tools/frameforge/frameforge-json.cpp)
target_include_directories(test-frameforge-new-features PRIVATE ${CMAKE_SOURCE_DIR}/tools/frameforge ${CMAKE_SOURCE_DIR}/vendor)
target_link_libraries(test-frameforge-new-features PRIVATE common)
add_test(NAME test-frameforge-new-features COMMAND $<TARGET_FILE:test-frameforge-new-features> WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests)
set_property(TEST test-frameforge-new-features PROPERTY LABELS "main")

llama_build_and_test(test-alloc.cpp)
target_include_directories(test-alloc PRIVATE ${PROJECT_SOURCE_DIR}/ggml/src)
108 changes: 108 additions & 0 deletions tests/test-frameforge-json-loader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#include "../tools/frameforge/frameforge-schema.h"
#include <cassert>
#include <iostream>
#include <string>

using namespace frameforge;

static void test_load_from_json() {
std::cout << "Testing JSON loading..." << std::endl;

std::string json_path = "../tools/frameforge/verb-definitions.json";
bool loaded = load_verb_definitions(json_path);

assert(loaded && "Failed to load verb definitions");
assert(are_verb_definitions_loaded() && "Definitions not marked as loaded");

std::cout << " ✓ JSON loaded successfully" << std::endl;
}

static void test_verb_conversion_with_json() {
std::cout << "Testing verb conversion with JSON..." << std::endl;

// Test basic verb
assert(string_to_verb("PAN") == Verb::PAN);
assert(verb_to_string(Verb::PAN) == "PAN");

// Test alias (PIN -> PAN)
assert(string_to_verb("PIN") == Verb::PAN);

// Test case insensitivity
assert(string_to_verb("pan") == Verb::PAN);

// Test DELETE alias (REMOVE -> DELETE)
assert(string_to_verb("REMOVE") == Verb::DELETE);

std::cout << " ✓ Verb conversion with JSON tests passed" << std::endl;
}

static void test_action_group_with_json() {
std::cout << "Testing action group mapping with JSON..." << std::endl;

assert(get_action_group_for_verb(Verb::PAN) == ActionGroup::CAMERA_CONTROL);
assert(get_action_group_for_verb(Verb::SET_POSE) == ActionGroup::ACTOR_POSE);
assert(get_action_group_for_verb(Verb::ADD) == ActionGroup::OBJECT_MGMT);
assert(get_action_group_for_verb(Verb::SHOT) == ActionGroup::SHOT_MGMT);

std::cout << " ✓ Action group with JSON tests passed" << std::endl;
}

static void test_required_parameters_with_json() {
std::cout << "Testing required parameters with JSON..." << std::endl;

auto pan_params = get_required_parameters(Verb::PAN);
assert(pan_params.size() == 1);
assert(pan_params[0] == "direction");

auto dolly_params = get_required_parameters(Verb::DOLLY);
assert(dolly_params.size() == 1);
assert(dolly_params[0] == "direction");

auto lean_params = get_required_parameters(Verb::LEAN);
assert(lean_params.size() == 2);
assert(lean_params[0] == "direction");
assert(lean_params[1] == "degrees");

auto add_params = get_required_parameters(Verb::ADD);
assert(add_params.size() == 1);
assert(add_params[0] == "target");

std::cout << " ✓ Required parameters with JSON tests passed" << std::endl;
}

static void test_fallback_without_json() {
std::cout << "Testing fallback without JSON (hard-coded defaults)..." << std::endl;

// Test that the system still works without loading JSON
assert(string_to_verb("TILT") == Verb::TILT);
assert(get_action_group_for_verb(Verb::TILT) == ActionGroup::CAMERA_CONTROL);

auto tilt_params = get_required_parameters(Verb::TILT);
assert(tilt_params.size() == 1);
assert(tilt_params[0] == "direction");

std::cout << " ✓ Fallback tests passed" << std::endl;
}

int main() {
std::cout << "Running FrameForge JSON Loader Tests..." << std::endl;
std::cout << "========================================" << std::endl;

try {
// First test fallback without JSON
test_fallback_without_json();

// Then test with JSON loaded
test_load_from_json();
test_verb_conversion_with_json();
test_action_group_with_json();
test_required_parameters_with_json();

std::cout << "========================================" << std::endl;
std::cout << "All tests passed! ✓" << std::endl;
return 0;
} catch (const std::exception & e) {
std::cerr << "Test failed with exception: " << e.what() << std::endl;
return 1;
}
}
240 changes: 240 additions & 0 deletions tests/test-frameforge-new-features.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
#include "../tools/frameforge/frameforge-schema.h"
#include "../tools/frameforge/frameforge-validator.h"
#include "../tools/frameforge/frameforge-json.h"

#include <cassert>
#include <iostream>
#include <string>

using namespace frameforge;

static void test_master_verb_detection() {
std::cout << "Testing master verb detection..." << std::endl;

assert(is_master_verb(Verb::START));
assert(is_master_verb(Verb::BEGIN));
assert(is_master_verb(Verb::HAVE));
assert(is_master_verb(Verb::MAKE));
assert(is_master_verb(Verb::STOP));

assert(!is_master_verb(Verb::PAN));
assert(!is_master_verb(Verb::MOVE));

std::cout << " ✓ Master verb detection passed" << std::endl;
}

static void test_timestamp_generation() {
std::cout << "Testing timestamp generation..." << std::endl;

std::string ts = get_current_timestamp();
assert(!ts.empty());
assert(ts.find('T') != std::string::npos); // Should contain ISO 8601 separator
assert(ts.find('Z') != std::string::npos); // Should end with Z

std::cout << " Generated timestamp: " << ts << std::endl;
std::cout << " ✓ Timestamp generation passed" << std::endl;
}

static void test_subject_in_parameters() {
std::cout << "Testing subject in parameters..." << std::endl;

Command cmd;
cmd.verb = Verb::PAN;
cmd.action_group = ActionGroup::CAMERA_CONTROL;
cmd.timestamp = get_current_timestamp();
cmd.parameters.subject = "Camera1";
cmd.parameters.direction = Direction::LEFT;
cmd.valid = true;

// Serialize to JSON
std::string json = command_to_json(cmd);

// Check that subject is in parameters, not at root
assert(json.find("\"parameters\"") != std::string::npos);
assert(json.find("\"subject\": \"Camera1\"") != std::string::npos ||
json.find("\"subject\":\"Camera1\"") != std::string::npos);

// Parse back
Command parsed = json_to_command(json);
assert(parsed.parameters.subject.has_value());
assert(parsed.parameters.subject.value() == "Camera1");

std::cout << " ✓ Subject in parameters passed" << std::endl;
}

static void test_timestamp_in_json() {
std::cout << "Testing timestamp in JSON..." << std::endl;

Command cmd;
cmd.verb = Verb::TILT;
cmd.action_group = ActionGroup::CAMERA_CONTROL;
cmd.timestamp = "2024-01-01T12:00:00.000Z";
cmd.parameters.direction = Direction::UP;
cmd.valid = true;

std::string json = command_to_json(cmd);

assert(json.find("timestamp") != std::string::npos);
assert(json.find("2024-01-01T12:00:00.000Z") != std::string::npos);

std::cout << " ✓ Timestamp in JSON passed" << std::endl;
}

static void test_master_verb_command() {
std::cout << "Testing master verb command..." << std::endl;

// Create a command like "START PANNING LEFT"
Command cmd;
cmd.verb = Verb::PAN;
cmd.master_verb = Verb::START;
cmd.action_group = ActionGroup::CAMERA_CONTROL;
cmd.timestamp = get_current_timestamp();
cmd.parameters.direction = Direction::LEFT;
cmd.parameters.speed = 5.0f;
cmd.valid = true;

std::string json = command_to_json(cmd);

// Should have both verb and master_verb
assert(json.find("\"verb\": \"PAN\"") != std::string::npos ||
json.find("\"verb\":\"PAN\"") != std::string::npos);
assert(json.find("\"master_verb\": \"START\"") != std::string::npos ||
json.find("\"master_verb\":\"START\"") != std::string::npos);

// Parse back
Command parsed = json_to_command(json);
assert(parsed.verb == Verb::PAN);
assert(parsed.master_verb.has_value());
assert(parsed.master_verb.value() == Verb::START);

std::cout << " ✓ Master verb command passed" << std::endl;
}

static void test_verb_aliases() {
std::cout << "Testing verb aliases..." << std::endl;

// Load definitions to get aliases
std::string json_path = "../tools/frameforge/verb-definitions.json";
bool loaded = load_verb_definitions(json_path);
assert(loaded);

// Test aliases
assert(string_to_verb("PIN") == Verb::PAN);
assert(string_to_verb("ROOM") == Verb::ZOOM);
assert(string_to_verb("PUSH") == Verb::DOLLY);
assert(string_to_verb("REMOVE") == Verb::DELETE);
assert(string_to_verb("WALK") == Verb::MOVE);
assert(string_to_verb("RUN") == Verb::MOVE);
assert(string_to_verb("TURN") == Verb::ROTATE);

std::cout << " ✓ Verb aliases passed" << std::endl;
}

static void test_optional_parameters() {
std::cout << "Testing optional parameters..." << std::endl;

// Ensure definitions are loaded
if (!are_verb_definitions_loaded()) {
load_verb_definitions("../tools/frameforge/verb-definitions.json");
}

auto pan_optional = get_optional_parameters(Verb::PAN);
assert(!pan_optional.empty());

auto start_optional = get_optional_parameters(Verb::START);
assert(!start_optional.empty());

std::cout << " PAN optional params: " << pan_optional.size() << std::endl;
std::cout << " START optional params: " << start_optional.size() << std::endl;
std::cout << " ✓ Optional parameters passed" << std::endl;
}

static void test_have_command() {
std::cout << "Testing HAVE command (HAVE TOM WALK FORWARD)..." << std::endl;

CommandValidator validator;

std::string json_str = R"({
"verb": "MOVE",
"master_verb": "HAVE",
"action_group": "OBJECT_MGMT",
"timestamp": "2024-01-01T12:00:00.000Z",
"parameters": {
"subject": "Tom",
"target": "Tom",
"direction": "FORWARD",
"speed": 5.0
}
})";

Command cmd;
ValidationResult result = validator.validate_json(json_str, cmd);

assert(result.valid);
assert(cmd.verb == Verb::MOVE);
assert(cmd.master_verb.has_value());
assert(cmd.master_verb.value() == Verb::HAVE);
assert(cmd.parameters.subject.has_value());
assert(cmd.parameters.subject.value() == "Tom");

std::cout << " ✓ HAVE command passed" << std::endl;
}

static void test_new_json_format() {
std::cout << "Testing new Delphi Bridge JSON format..." << std::endl;

CommandValidator validator;

// Test complete JSON with all new features
std::string json_str = R"({
"verb": "ZOOM",
"action_group": "CAMERA_CONTROL",
"timestamp": "2024-12-30T10:00:00.000Z",
"parameters": {
"subject": "MainCamera",
"direction": "IN",
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test uses "direction": "IN" for a ZOOM command, but the Direction enum doesn't define IN or OUT values. The enum only includes LEFT, RIGHT, UP, DOWN, FORWARD, BACKWARD, and UNKNOWN. This test would fail because string_to_direction("IN") would return Direction::UNKNOWN. Either add IN and OUT to the Direction enum, or update the test and documentation to use appropriate existing direction values for ZOOM operations.

Suggested change
"direction": "IN",
"direction": "FORWARD",

Copilot uses AI. Check for mistakes.
"speed": 10.0
}
})";

Command cmd;
ValidationResult result = validator.validate_json(json_str, cmd);

assert(result.valid);
assert(cmd.verb == Verb::ZOOM);
assert(!cmd.timestamp.empty());
assert(cmd.parameters.subject.has_value());
assert(cmd.parameters.direction.has_value());

// Serialize back and ensure all fields present
std::string output_json = command_to_json(cmd);
assert(output_json.find("timestamp") != std::string::npos);
assert(output_json.find("parameters") != std::string::npos);
assert(output_json.find("subject") != std::string::npos);

std::cout << " ✓ New JSON format passed" << std::endl;
}

int main() {
std::cout << "Running FrameForge New Features Tests..." << std::endl;
std::cout << "==========================================" << std::endl;

try {
test_master_verb_detection();
test_timestamp_generation();
test_subject_in_parameters();
test_timestamp_in_json();
test_master_verb_command();
test_verb_aliases();
test_optional_parameters();
test_have_command();
test_new_json_format();

std::cout << "==========================================" << std::endl;
std::cout << "All new feature tests passed! ✓" << std::endl;
return 0;
} catch (const std::exception & e) {
std::cerr << "Test failed with exception: " << e.what() << std::endl;
return 1;
}
}
Loading