commit 3945b687d47a37cb9eb0c051a5ab4cb0e583cc82 Author: Detlef Jenett Date: Thu Feb 26 17:41:05 2026 +0100 Initial commit diff --git a/Documentation/Test Streamlining .pptx b/Documentation/Test Streamlining .pptx new file mode 100644 index 0000000..72ad544 Binary files /dev/null and b/Documentation/Test Streamlining .pptx differ diff --git a/SMT_PlayGround/hw_ext_tests/InstrumentConfig.json b/SMT_PlayGround/hw_ext_tests/InstrumentConfig.json new file mode 100644 index 0000000..8463e83 --- /dev/null +++ b/SMT_PlayGround/hw_ext_tests/InstrumentConfig.json @@ -0,0 +1,6 @@ +{"Instrument": { + "Variant": ["AVI64","FVI16"], + "Option": ["activeTransition", "diffVoltmeter", "differential", "highCurrent", "highResolution", "highSpeed", "highVoltage", "highVoltageH", "highVoltageL", "precisionGangRange", "siteInterlacingOn"], + "PinType": ["single","ganged","stacked"], + "PinNumber": ["1","128"] +}} \ No newline at end of file diff --git a/SMT_PlayGround/hw_ext_tests/VariantConfig.json b/SMT_PlayGround/hw_ext_tests/VariantConfig.json new file mode 100644 index 0000000..d689c9a --- /dev/null +++ b/SMT_PlayGround/hw_ext_tests/VariantConfig.json @@ -0,0 +1,5 @@ +{ + "Variants": [ + { "Variant": "AVI64","Directory": "hw_ext_variants\\Avi64\\Driver\\TestSetup","FacadeName": "AVI64" } +] +} \ No newline at end of file diff --git a/SMT_PlayGround/hw_ext_tests/setups/Setup_Simple_Typ.json b/SMT_PlayGround/hw_ext_tests/setups/Setup_Simple_Typ.json new file mode 100644 index 0000000..55836cf --- /dev/null +++ b/SMT_PlayGround/hw_ext_tests/setups/Setup_Simple_Typ.json @@ -0,0 +1,66 @@ +{ + "Options": [ + "", "highCurrent" + ], + "FileNotes" : [ + "This is a simple test setup with 1 instrument. It powers up the instrument, and disconnects after measurement.", + "Firsst setup will trigger a problem message, which will be fixed in the second setup.", + "Clamps are set to the minimum clamp distance of largest range, to support proper ganging.", + "Range values are smaller than the actual used ranges, so we can verify proper ranging in states." + ], + "Variables": [ + {"$forceValue": "1.0"}, + {"$clampCurrent": "0.03*gangCount"}, + {"$irange": "0.033*gangCount"} + ], + "TestSetups" : + [ + { + "Setup" : + { + "Name": "Simple_Typ", + "Notes" : [ + "This setup will do the main setup, but trigger a problem message.", + "The force value is larger than the actual used ranges, so we receive a problem message." + ], + "InstrumentSetup" : { + "Name": "pin1", + "Properties": [ + { "level.vforce": "$forceValue" }, + { "level.iclampSource": "$clampCurrent" }, + { "level.iclampSink": "$clampCurrent" }, + { "level.vrange": "0.0" }, + { "level.irange": "$irange" }, + { "connect": "true" }, + { "disconnect": "true" }, + { "disconnectMode": "hiz" } + ], + "Expects": [ + { "type" : "problem", "checks" : + [ {"proplemMsg" : "The $forceValue exceeds the level.vrange*"}] + } + ] + } + } + } + , + { + "Setup" : + { + "Name": "Simple2_Typ", + "Notes" : [ + "This setup will fix the problem frome above setup." + ], + "InstrumentSetup" : { + "Name": "pin1", + "Properties": [ + { "level.vrange": "$forceValue" } + ], + "Expects": [ + { "type" : "state", "checks" :[ {"property" : "forceValue" , "value" : "forceValue" }] } + ] + } + } + } + ] +} diff --git a/SMT_PlayGround/hw_ext_tests/templates/Test_Level_Simple.json b/SMT_PlayGround/hw_ext_tests/templates/Test_Level_Simple.json new file mode 100644 index 0000000..a051478 --- /dev/null +++ b/SMT_PlayGround/hw_ext_tests/templates/Test_Level_Simple.json @@ -0,0 +1,31 @@ +{ + "GroupId": 1000, + "UniqueTemplateId": "auto_generated", + "Description": [ + "Simple test level with 1 instrument. Powers up the instrument, and disconnects after measurement.", + "Can check states and logsets after execution." + ], + "Features": [ + "PowerUpWoPowerSequence", + "PowerDownNoPowerSequence" + ], + "TestInput": [ + { "Instruments" : "Instruments" }, + { "SetupFileName" :"Setup_Simple_Typ", + "setups" : [ + {"InitialSetup": "Simple_Typ"}, + {"FinalSetup": "Simple2_Typ"} + ] } + ], + "Template": [ + { "call" : "createMeasurement", "args": [ "Instruments"], "return" :"Measurement" }, + { "call" : "applySetup", "args": ["Measurement", "InitialSetup"] }, + { "call" : "buildMeasurement", "args": ["Measurement", "false"] }, + { "call" : "ProblemCheck", "args": ["Measurement", "InitialSetup"] }, + { "call" : "applySetup", "args": ["Measurement", "FinalSetup"] }, + { "call" : "buildMeasurement", "args": ["Measurement", "true"] }, + { "call" : "executeMeasurement", "args": ["Measurement", "true"] }, + { "call" : "StateCheck", "args": ["Measurement", "FinalSetup"] }, + { "call" : "LogSetCheck", "args": ["Measurement", "FinalSetup"] } + ] +} \ No newline at end of file diff --git a/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/ExecutionConfig.json b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/ExecutionConfig.json new file mode 100644 index 0000000..2be0cbb --- /dev/null +++ b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/ExecutionConfig.json @@ -0,0 +1,25 @@ +{ + "Tests": [ + { + "Name": "Test_Level_Simple", + "Configurations": [ + { + "Option": "", + "PinType": "single" + }, + { + "Option": "", + "PinType": "ganged" + }, + { + "Option": "highCurrent", + "PinType": "single" + }, + { + "Option": "highCurrent", + "PinType": "ganged" + } + ] + } + ] +} diff --git a/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/InstrumentConfig.json b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/InstrumentConfig.json new file mode 100644 index 0000000..5d0dc5a --- /dev/null +++ b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/InstrumentConfig.json @@ -0,0 +1,32 @@ +{ + "Instruments": [ + { + "InstrumentName": "pin1", + "Variant": "AVI64", + "Option": [], + "PinType": "single", + "PinNumbers": ["1"] + }, + { + "InstrumentName": "pin1", + "Variant": "AVI64", + "Option": [], + "PinType": "ganged", + "PinNumbers": ["1","2"] + }, + { + "InstrumentName": "pin1", + "Variant": "AVI64", + "Option": ["highCurrent"], + "PinType": "single", + "PinNumbers": ["1"] + }, + { + "InstrumentName": "meas1", + "Variant": "AVI64", + "Option": [], + "PinType": "single", + "PinNumbers": ["2"] + } +] +} diff --git a/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/Setup_Simple_Typ.json b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/Setup_Simple_Typ.json new file mode 100644 index 0000000..aa48ed1 --- /dev/null +++ b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/Setup_Simple_Typ.json @@ -0,0 +1,60 @@ +{ + "Options": [ + "", "highCurrent" + ], + "FileNotes" : [""], + "Variables": [ + {"$clampCurrent": "0.05*$gangCount"}, + {"$irange": "0.06*$gangCount"}, + {"$irangeExp": "0.1*$gangCount"} + ], + "TestSetups" : + [ + { + "Setup" : + { + "Name": "Simple_Typ", + "Notes" : [""], + "InstrumentSetup" : { + "Name": "pin1", + "Properties": [ + { "level.iclampSource": "$clampCurrent" }, + { "level.iclampSink": "$clampCurrent" }, + { "level.irange": "$irange" } + ] + } + } + }, + { + "Setup" : + { + "Name": "Simple2_Typ", + "Notes" : [""], + "InstrumentSetup" : { + "Name": "pin1", + "Properties": [ + { "level.vrange": "2.0" }, + { "level.iclampSource": "-" } + ], + "Expects": [ + { "type" : "state", "checks" :[ + {"property" : "iclampSource" , "value" : "$clampCurrent" }, + {"property" : "iclampSink" , "value" : "$clampCurrent" }, + {"property" : "irange" , "value" : "$irangeExp" }, + {"property" : "vrange" , "value" : "3.0" } + ] + }, + { "type" : "logset", "checks" :[ + {"logset" : "avi64.DcRangeLogicalSet", "properties" : [ + { "property" : "vrange" , "value" : "3.0"}, + { "property" : "irange" , "value" : "$irangeExp"} + ] + } + ] + } + ] + } + } + } + ] +} \ No newline at end of file diff --git a/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/Instruments.h b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/Instruments.h new file mode 100644 index 0000000..d85aab2 --- /dev/null +++ b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/Instruments.h @@ -0,0 +1,54 @@ +#ifndef INSTRUMENTS_H +#define INSTRUMENTS_H + +#include +#include + +struct Instruments { + std::string variant; + std::vector options; + std::string pinType; + std::vector pinNumbers; + int gangCount; + + static Instruments pin1_single() { + Instruments inst; + inst.variant = "AVI64"; + inst.pinType = "single"; + inst.pinNumbers.push_back(1); + inst.gangCount = 1; + return inst; + } + + static Instruments pin1_ganged() { + Instruments inst; + inst.variant = "AVI64"; + inst.pinType = "ganged"; + inst.pinNumbers.push_back(1); + inst.pinNumbers.push_back(2); + inst.gangCount = 2; + return inst; + } + + static Instruments pin1_single_highCurrent() { + Instruments inst; + inst.variant = "AVI64"; + inst.options.push_back("highCurrent"); + inst.pinType = "single"; + inst.pinNumbers.push_back(1); + inst.gangCount = 1; + return inst; + } + + static Instruments meas1_single() { + Instruments inst; + inst.variant = "AVI64"; + inst.pinType = "single"; + inst.pinNumbers.push_back(2); + inst.gangCount = 1; + return inst; + } + +}; + +#endif diff --git a/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/Makefile b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/Makefile new file mode 100644 index 0000000..5241a04 --- /dev/null +++ b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/Makefile @@ -0,0 +1,18 @@ +# Makefile for generated tests + +CXX = g++ +CXXFLAGS = -std=c++11 -Wall -O2 +TARGET = test_runner + +all: $(TARGET) + +$(TARGET): main.cpp Instruments.h Measurement.h Test_Level_Simple.cpp + $(CXX) $(CXXFLAGS) -o $(TARGET) main.cpp + +clean: + rm -f $(TARGET) $(TARGET).exe + +run: $(TARGET) + ./$(TARGET) + +.PHONY: all clean run diff --git a/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/Measurement.h b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/Measurement.h new file mode 100644 index 0000000..3778970 --- /dev/null +++ b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/Measurement.h @@ -0,0 +1,31 @@ +#ifndef MEASUREMENT_H +#define MEASUREMENT_H + +#include "Instruments.h" + +class Measurement { +public: + Measurement(const Instruments& inst) {} + void build(bool flag) {} + void execute(bool flag) {} + void ProblemCheck() {} + void StateCheck() {} + void LogSetCheck() {} + + // Property setters + template + void level_vforce(const std::string& name, T value) {} + template + void level_iclampSource(const std::string& name, T value) {} + template + void level_iclampSink(const std::string& name, T value) {} + template + void level_vrange(const std::string& name, T value) {} + template + void level_irange(const std::string& name, T value) {} + void connect(bool flag) {} + void disconnect(bool flag) {} + void disconnectMode(const std::string& mode) {} +}; + +#endif diff --git a/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/README.md b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/README.md new file mode 100644 index 0000000..19ffd29 --- /dev/null +++ b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/README.md @@ -0,0 +1,44 @@ +# Generated Test Files + +This directory contains automatically generated C++ test code. + +## Files + +- `Instruments.h` - Instrument configuration definitions +- `Measurement.h` - Measurement interface (stub implementation) +- `Test_Level_Simple.cpp` - Test class: Test_Level_Simple +- `main.cpp` - Main entry point that executes all tests +- `build.bat` - Windows build script +- `Makefile` - Linux/Mac build script + +## Building + +**Windows:** +```bash +build.bat +``` + +**Linux/Mac:** +```bash +make +``` + +## Running + +After building, run the tests: + +**Windows:** +```bash +test_runner.exe +``` + +**Linux/Mac:** +```bash +./test_runner +``` + +## Note + +The `Measurement.h` file contains stub implementations. You should replace +this with your actual measurement implementation or link against your +measurement library when building the tests. diff --git a/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/Test_Level_Simple.cpp b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/Test_Level_Simple.cpp new file mode 100644 index 0000000..9dc51ea --- /dev/null +++ b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/Test_Level_Simple.cpp @@ -0,0 +1,57 @@ +#include "Instruments.h" +#include "Measurement.h" + +// Test class generated from template: auto_generated +class Test_Level_Simple { +public: + void Simple_Typ(Measurement& measurement, int gangCount) { + // Setup: Simple_Typ + double clampCurrent = 0.05*gangCount; + double forceValue = 1.0; + double irange = 0.06*gangCount; + double irangeExp = 0.1*gangCount; + + measurement.level_vforce("pin1", 1.0); + measurement.level_iclampSource("pin1", clampCurrent); + measurement.level_iclampSink("pin1", clampCurrent); + measurement.level_vrange("pin1", 0.0); + measurement.level_irange("pin1", irange); + measurement.connect(true); + measurement.disconnect(true); + measurement.disconnectMode("hiz"); + } + + void Simple2_Typ(Measurement& measurement, int gangCount) { + // Setup: Simple2_Typ + double clampCurrent = 0.05*gangCount; + double forceValue = 1.0; + double irange = 0.06*gangCount; + double irangeExp = 0.1*gangCount; + + measurement.level_vrange("pin1", 2.0); + } + + void run(const Instruments& instruments) { + int gangCount = instruments.gangCount; + + // createMeasurement + Measurement MeasurementObj(instruments); + // applySetup + Simple_Typ(MeasurementObj, gangCount); + // buildMeasurement + MeasurementObj.build(false); + // ProblemCheck + MeasurementObj.ProblemCheck(); + // applySetup + Simple2_Typ(MeasurementObj, gangCount); + // buildMeasurement + MeasurementObj.build(true); + // executeMeasurement + MeasurementObj.execute(true); + // StateCheck + MeasurementObj.StateCheck(); + // LogSetCheck + MeasurementObj.LogSetCheck(); + } +}; + diff --git a/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/build.bat b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/build.bat new file mode 100644 index 0000000..a2a2a3f --- /dev/null +++ b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/build.bat @@ -0,0 +1,18 @@ +@echo off +REM Build script for generated tests + +echo Building generated tests... + +echo Compiling main.cpp... +g++ -std=c++11 -Wall -O2 -o test_runner.exe main.cpp + +if errorlevel 1 ( + echo. + echo Build failed! + exit /b 1 +) + +echo. +echo Build successful! Executable: test_runner.exe +echo. +echo To run the tests, execute: test_runner.exe diff --git a/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/main.cpp b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/main.cpp new file mode 100644 index 0000000..b9fdeda --- /dev/null +++ b/SMT_PlayGround/hw_ext_variants/Avi64/Driver/TestSetup/generatedTests/main.cpp @@ -0,0 +1,34 @@ +#include +#include "Instruments.h" +#include "Test_Level_Simple.cpp" + +int main() { + std::cout << "Running generated tests..." << std::endl; + + { + Test_Level_Simple test; + test.run(Instruments::pin1_single()); + std::cout << "Completed: Test_Level_Simple with pin1_single" << std::endl; + } + + { + Test_Level_Simple test; + test.run(Instruments::pin1_ganged()); + std::cout << "Completed: Test_Level_Simple with pin1_ganged" << std::endl; + } + + { + Test_Level_Simple test; + test.run(Instruments::pin1_single_highCurrent()); + std::cout << "Completed: Test_Level_Simple with pin1_single_highCurrent" << std::endl; + } + + { + Test_Level_Simple test; + test.run(Instruments::meas1_single()); + std::cout << "Completed: Test_Level_Simple with meas1_single" << std::endl; + } + + std::cout << "All tests completed." << std::endl; + return 0; +} diff --git a/SMT_PlayGround/hw_ext_variants/Avi64/generatedTests/InstrumentDefs.h b/SMT_PlayGround/hw_ext_variants/Avi64/generatedTests/InstrumentDefs.h new file mode 100644 index 0000000..ee55c84 --- /dev/null +++ b/SMT_PlayGround/hw_ext_variants/Avi64/generatedTests/InstrumentDefs.h @@ -0,0 +1,45 @@ +// Auto-generated instrument definitions - do not edit + +#pragma once +#include + +// Instrument definitions +struct InstrumentDef { + std::string name; + std::string variant; + std::vector options; + std::string pinType; + std::vector pinNumbers; +}; + +// All configured instruments +static const std::vector allInstruments = { + { + "pin1", // name + "AVI64", // variant + {}, // options + "single", // pinType + {1} // pinNumbers + }, + { + "pin1", // name + "AVI64", // variant + {}, // options + "ganged", // pinType + {1, 2} // pinNumbers + }, + { + "pin1", // name + "AVI64", // variant + {"highCurrent"}, // options + "single", // pinType + {1} // pinNumbers + }, + { + "meas1", // name + "AVI64", // variant + {}, // options + "single", // pinType + {2} // pinNumbers + } +}; diff --git a/SMT_PlayGround/hw_ext_variants/Avi64/generatedTests/Test_Level_Simple.cpp b/SMT_PlayGround/hw_ext_variants/Avi64/generatedTests/Test_Level_Simple.cpp new file mode 100644 index 0000000..8b95ffc --- /dev/null +++ b/SMT_PlayGround/hw_ext_variants/Avi64/generatedTests/Test_Level_Simple.cpp @@ -0,0 +1,66 @@ +// Auto-generated test file - do not edit +// Test: Test_Level_Simple + +class Test_Level_Simple { +public: + Test_Level_Simple(Instruments instruments) + : instruments_(instruments) {} + + // Setup method: Simple_Typ + void setup_Simple_Typ(Measurement measurement) { + + // Variables + double clampCurrent = 0.05*gangCount; + double irange = 0.06*gangCount; + double irangeExp = 0.1*gangCount; + double forceValue = 1.0; + + // Properties for pin1 + setProperty(measurement, "pin1", "disconnectMode", "hiz"); + setProperty(measurement, "pin1", "level.iclampSink", clampCurrent); + setProperty(measurement, "pin1", "level.vforce", forceValue); + setProperty(measurement, "pin1", "level.iclampSource", clampCurrent); + setProperty(measurement, "pin1", "level.vrange", 0.0); + setProperty(measurement, "pin1", "level.irange", irange); + setProperty(measurement, "pin1", "connect", true); + setProperty(measurement, "pin1", "disconnect", true); + + checkProblem(measurement, "pin1", "The " + std::to_string(forceValue) + " exceeds the level.vrange*"); + } + + // Setup method: Simple2_Typ + void setup_Simple2_Typ(Measurement measurement) { + + // Variables + double clampCurrent = 0.05*gangCount; + double irange = 0.06*gangCount; + double irangeExp = 0.1*gangCount; + double forceValue = 1.0; + + // Properties for pin1 + setProperty(measurement, "pin1", "level.vrange", 2.0); + + checkState(measurement, "pin1", "iclampSource", clampCurrent); + checkState(measurement, "pin1", "iclampSink", clampCurrent); + checkState(measurement, "pin1", "irange", irangeExp); + checkState(measurement, "pin1", "vrange", 3.0); + checkLogset(measurement, "pin1", "avi64.DcRangeLogicalSet", "vrange", 3.0); + checkLogset(measurement, "pin1", "avi64.DcRangeLogicalSet", "irange", irangeExp); + } + + // Execute test sequence + void execute() { + auto Measurement = createMeasurement(instruments_); + setup_Simple_Typ(Measurement); + buildMeasurement(Measurement, false); + ProblemCheck(Measurement, "Simple_Typ"); + setup_Simple2_Typ(Measurement); + buildMeasurement(Measurement, true); + executeMeasurement(Measurement, true); + StateCheck(Measurement, "Simple2_Typ"); + LogSetCheck(Measurement, "Simple2_Typ"); + } + +private: + Instruments instruments_; +}; diff --git a/SMT_PlayGround/hw_ext_variants/Avi64/generatedTests/main.cpp b/SMT_PlayGround/hw_ext_variants/Avi64/generatedTests/main.cpp new file mode 100644 index 0000000..06a3f29 --- /dev/null +++ b/SMT_PlayGround/hw_ext_variants/Avi64/generatedTests/main.cpp @@ -0,0 +1,20 @@ +// Auto-generated main file - do not edit + +#include "InstrumentDefs.h" +#include "Test_Level_Simple.cpp" + +int main(int argc, char* argv[]) { + // Initialize instruments (implementation specific) + Instruments instruments; + // TODO: Initialize instruments from configuration + + // Execute test: Test_Level_Simple + { + Test_Level_Simple test(instruments); + // Call setup methods as needed + // test.setup_(instruments); + test.execute(); + } + + return 0; +} diff --git a/TestGenerator/Makefile b/TestGenerator/Makefile new file mode 100644 index 0000000..7082415 --- /dev/null +++ b/TestGenerator/Makefile @@ -0,0 +1,53 @@ +# Makefile for Test Generator + +CXX = g++ +CXXFLAGS = -std=c++11 -Wall -O2 +TARGET = testgen +SRCDIR = src +OBJDIR = obj + +# Source files +SOURCES = $(SRCDIR)/main.cpp \ + $(SRCDIR)/json_parser.cpp \ + $(SRCDIR)/file_utils.cpp \ + $(SRCDIR)/code_generator.cpp + +# Object files +OBJECTS = $(OBJDIR)/main.o \ + $(OBJDIR)/json_parser.o \ + $(OBJDIR)/file_utils.o \ + $(OBJDIR)/code_generator.o + +# Default target +all: $(OBJDIR) $(TARGET) + +# Create object directory +$(OBJDIR): + mkdir -p $(OBJDIR) + +# Link executable +$(TARGET): $(OBJECTS) + $(CXX) $(CXXFLAGS) -o $(TARGET) $(OBJECTS) + +# Compile source files +$(OBJDIR)/main.o: $(SRCDIR)/main.cpp + $(CXX) $(CXXFLAGS) -c $(SRCDIR)/main.cpp -o $(OBJDIR)/main.o + +$(OBJDIR)/json_parser.o: $(SRCDIR)/json_parser.cpp $(SRCDIR)/json_parser.h + $(CXX) $(CXXFLAGS) -c $(SRCDIR)/json_parser.cpp -o $(OBJDIR)/json_parser.o + +$(OBJDIR)/file_utils.o: $(SRCDIR)/file_utils.cpp $(SRCDIR)/file_utils.h + $(CXX) $(CXXFLAGS) -c $(SRCDIR)/file_utils.cpp -o $(OBJDIR)/file_utils.o + +$(OBJDIR)/code_generator.o: $(SRCDIR)/code_generator.cpp $(SRCDIR)/code_generator.h $(SRCDIR)/data_structures.h + $(CXX) $(CXXFLAGS) -c $(SRCDIR)/code_generator.cpp -o $(OBJDIR)/code_generator.o + +# Clean build artifacts +clean: + rm -rf $(OBJDIR) $(TARGET) $(TARGET).exe + +# Install (optional) +install: $(TARGET) + cp $(TARGET) /usr/local/bin/ + +.PHONY: all clean install diff --git a/TestGenerator/README.md b/TestGenerator/README.md new file mode 100644 index 0000000..2006796 --- /dev/null +++ b/TestGenerator/README.md @@ -0,0 +1,115 @@ +# Test Generator + +A command-line tool that reads JSON configuration files and generates C++ test code. + +## Features + +- Parses JSON configuration files without external dependencies +- Generates C++ test classes from templates +- Supports multiple instrument configurations +- Merges common and variant-specific setups +- Variable substitution in configurations + +## Building + +### Requirements +- GCC (g++) with C++11 support +- Standard C++ library only (no external dependencies) + +### On Windows +```bash +build.bat +``` + +### On Linux/Mac +```bash +make +``` + +This will create the `testgen` (or `testgen.exe` on Windows) executable. + +## Usage + +### Generate Test Code +```bash +testgen -commonPath -variantPath +``` + +Example: +```bash +testgen -commonPath ..\SMT_PlayGround\hw_ext_tests -variantPath ..\SMT_PlayGround\hw_ext_variants\Avi64\Driver\TestSetup +``` + +### Validate JSON File +```bash +testgen -dry +``` + +Example: +```bash +testgen -dry test.json +``` + +### Display Help +```bash +testgen -help +``` + +## Generated Files + +The tool generates C++ files in `/generatedTests/`: + +- `Instruments.h` - Instrument definitions +- `Measurement.h` - Measurement interface (stub) +- `Test_*.cpp` - Generated test classes +- `main.cpp` - Main entry point that runs all tests +- `build.bat` - Windows build script for generated tests +- `Makefile` - Linux/Mac build script for generated tests + +## Building Generated Tests + +After generation, build and run the tests: + +**Windows:** +```bash +cd /generatedTests +build.bat +test_runner.exe +``` + +**Linux/Mac:** +```bash +cd /generatedTests +make +./test_runner +``` + +## Project Structure + +``` +TestGenerator/ +├── src/ +│ ├── main.cpp # Entry point and command-line parsing +│ ├── json_parser.h/.cpp # JSON parser implementation +│ ├── file_utils.h/.cpp # File I/O utilities +│ ├── code_generator.h/.cpp # Code generation logic +│ └── data_structures.h # Data structure definitions +├── Makefile # Build script for Linux/Mac +├── build.bat # Build script for Windows +└── README.md # This file +``` + +## Architecture + +1. **JSON Parser** - Simple recursive descent parser for JSON +2. **Data Structures** - Type-safe structures for configuration data +3. **File Utils** - Cross-platform file and directory operations +4. **Code Generator** - Template-based C++ code generation +5. **Main** - Command-line interface and orchestration + +## Notes + +- All code uses only the C++ standard library +- No terminal/console APIs are used in the domain logic +- Generated code requires linking with actual Measurement implementation +- Variable substitution supports expressions like `0.03*gangCount` diff --git a/TestGenerator/build.bat b/TestGenerator/build.bat new file mode 100644 index 0000000..0cdafdb --- /dev/null +++ b/TestGenerator/build.bat @@ -0,0 +1,37 @@ +@echo off +REM Build script for Windows using g++ (MinGW or similar) + +echo Building Test Generator... + +if not exist obj mkdir obj + +echo Compiling json_parser.cpp... +g++ -std=c++11 -Wall -O2 -c src\json_parser.cpp -o obj\json_parser.o +if errorlevel 1 goto error + +echo Compiling file_utils.cpp... +g++ -std=c++11 -Wall -O2 -c src\file_utils.cpp -o obj\file_utils.o +if errorlevel 1 goto error + +echo Compiling code_generator.cpp... +g++ -std=c++11 -Wall -O2 -c src\code_generator.cpp -o obj\code_generator.o +if errorlevel 1 goto error + +echo Compiling main.cpp... +g++ -std=c++11 -Wall -O2 -c src\main.cpp -o obj\main.o +if errorlevel 1 goto error + +echo Linking testgen.exe... +g++ -std=c++11 -Wall -O2 -o testgen.exe obj\main.o obj\json_parser.o obj\file_utils.o obj\code_generator.o +if errorlevel 1 goto error + +echo. +echo Build successful! Executable: testgen.exe +goto end + +:error +echo. +echo Build failed! +exit /b 1 + +:end diff --git a/TestGenerator/copilot_instrucations b/TestGenerator/copilot_instrucations new file mode 100644 index 0000000..a37242b --- /dev/null +++ b/TestGenerator/copilot_instrucations @@ -0,0 +1,49 @@ +You are an expert programmer. +Create a command-line tool which can read json files and generate c++ code based on data in json files. + +CONSTRAINTS +- Use ONLY the language's standard library (no external dependencies). +- Organize code into multiple a sub dirctory having multiple files / modules / packages as appropriate. +- Logic MUST NOT depend on terminal/console APIs. +- Use gcc (c++ language) + +ARCHITECTURE REQUIREMENTS + - Data structures for reading and checking json data + - Pure helper functions (file I/O, string manipulation, etc.) + +TOOL REQUIREMENTS (MVP) +- Different modes for execution selected via commandline arguments: + - `-commonPath `: specify common location (hw_ext_tests) + - `-variantPath `: specify variant location (hw_ext_variants\Avi64) + - `-dry` : dry run to check if the json file is well-formed and prints "VALID" or "INVALID". + - `-help`: display usage information and exit +- All generated cpp code shall be in variant sub directory, called /generatedTests. +- Before starting a new generation clean the output directories +- Definitions in "Globals" introduce global variables which are USED in the setup. +- Definitions in "Variables" define local variables. +- Section options define variant options for which the setup or test can be applied +- All words in strings which starting with `$` represent a variable. + Separate the string and combine the string with proper syntax with the variable. +- "property", "value" pairs shall become function calls with parameters Measurement, Name, property, value. + +- Generate ONE file which includes the instrument definitions as specified in InstrumentConfig + - All members MUST be filled as specified + +- Generate ONE cpp file with one class per test + - This class shall be based on data in /templates and shall have the same name as the json (`testFileName`) + - This class MUST have setup methods per instrumentSetup from /setups as base and over write them with setup specified by setups in /TestSetup the method shall be called with `Measurement`. + - The class shall be called with one structure based on `Instruments` + - Call in `Template` named `applySetup` shall call the setup method in class specified with second argument + +- Generate ONE file which hosts main function. + - Call every generated test + +ENGINE REQUIREMENTS +- Keep the code short but readable. +- Add brief comments only where needed. +DELIVERABLE +- Provide the complete code, separated into at least: + 1) a domain module/package (no terminal dependencies) + 2) a small entrypoint/main that wires retrieves commandline arguments and calls the domain logic and delivers output + 3) build executable + \ No newline at end of file diff --git a/TestGenerator/src/code_generator.cpp b/TestGenerator/src/code_generator.cpp new file mode 100644 index 0000000..bec25e8 --- /dev/null +++ b/TestGenerator/src/code_generator.cpp @@ -0,0 +1,820 @@ +#include "code_generator.h" +#include "file_utils.h" +#include +#include +#include + +CodeGenerator::CodeGenerator(const Config& cfg) : config(cfg) {} + +Instrument CodeGenerator::parseInstrument(std::shared_ptr json) { + Instrument inst; + inst.instrumentName = json->get("InstrumentName")->getString(); + inst.variant = json->get("Variant")->getString(); + inst.pinType = json->get("PinType")->getString(); + + auto options = json->get("Option"); + if (options->isArray()) { + for (const auto& opt : options->getArray()) { + inst.options.push_back(opt->getString()); + } + } + + auto pins = json->get("PinNumbers"); + if (pins->isArray()) { + for (const auto& pin : pins->getArray()) { + inst.pinNumbers.push_back(pin->getString()); + } + } + + return inst; +} + +Property CodeGenerator::parseProperty(std::shared_ptr json) { + Property prop; + if (json->isObject()) { + for (const auto& pair : json->getObject()) { + prop.key = pair.first; + prop.value = pair.second->getString(); + break; + } + } + return prop; +} + +Variable CodeGenerator::parseVariable(std::shared_ptr json) { + Variable var; + if (json->isObject()) { + for (const auto& pair : json->getObject()) { + var.name = pair.first; + var.expression = pair.second->getString(); + break; + } + } + return var; +} + +Setup CodeGenerator::parseSetup(std::shared_ptr json) { + Setup setup; + auto setupObj = json->get("Setup"); + + setup.name = setupObj->get("Name")->getString(); + + auto notes = setupObj->get("Notes"); + if (notes->isArray()) { + for (const auto& note : notes->getArray()) { + setup.notes.push_back(note->getString()); + } + } + + auto instSetup = setupObj->get("InstrumentSetup"); + setup.instrumentSetup.name = instSetup->get("Name")->getString(); + + auto props = instSetup->get("Properties"); + if (props->isArray()) { + for (const auto& prop : props->getArray()) { + setup.instrumentSetup.properties.push_back(parseProperty(prop)); + } + } + + auto expects = instSetup->get("Expects"); + if (expects->isArray()) { + for (const auto& expectJson : expects->getArray()) { + Expect expect; + expect.type = expectJson->get("type")->getString(); + + auto checks = expectJson->get("checks"); + if (checks->isArray()) { + for (const auto& check : checks->getArray()) { + if (check->isObject()) { + for (const auto& pair : check->getObject()) { + Check c; + c.key = pair.first; + c.value = pair.second->getString(); + expect.checks.push_back(c); + } + } + } + } + + setup.instrumentSetup.expects.push_back(expect); + } + } + + return setup; +} + +SetupFile CodeGenerator::parseSetupFile(std::shared_ptr json) { + SetupFile setupFile; + + auto options = json->get("Options"); + if (options->isArray()) { + for (const auto& opt : options->getArray()) { + setupFile.options.push_back(opt->getString()); + } + } + + auto notes = json->get("FileNotes"); + if (notes->isArray()) { + for (const auto& note : notes->getArray()) { + setupFile.fileNotes.push_back(note->getString()); + } + } + + auto vars = json->get("Variables"); + if (vars->isArray()) { + for (const auto& var : vars->getArray()) { + setupFile.variables.push_back(parseVariable(var)); + } + } + + auto setups = json->get("TestSetups"); + if (setups->isArray()) { + for (const auto& setup : setups->getArray()) { + setupFile.setups.push_back(parseSetup(setup)); + } + } + + return setupFile; +} + +Template CodeGenerator::parseTemplate(std::shared_ptr json) { + Template tmpl; + tmpl.groupId = (int)json->get("GroupId")->getNumber(); + tmpl.uniqueTemplateId = json->get("UniqueTemplateId")->getString(); + + auto desc = json->get("Description"); + if (desc->isArray()) { + for (const auto& d : desc->getArray()) { + tmpl.description.push_back(d->getString()); + } + } + + auto features = json->get("Features"); + if (features->isArray()) { + for (const auto& f : features->getArray()) { + tmpl.features.push_back(f->getString()); + } + } + + auto testInput = json->get("TestInput"); + if (testInput->isArray()) { + for (const auto& input : testInput->getArray()) { + TemplateInput ti; + // Process all keys in the object + for (const auto& pair : input->getObject()) { + if (pair.second->isString()) { + // Store string values in key and value + if (ti.key.empty()) { + ti.key = pair.first; + ti.value = pair.second->getString(); + } + } else if (pair.second->isArray()) { + // If it's the setups array, store it + if (pair.first == "setups") { + for (const auto& setupItem : pair.second->getArray()) { + std::map setupMap; + for (const auto& setupPair : setupItem->getObject()) { + setupMap[setupPair.first] = setupPair.second->getString(); + } + ti.setups.push_back(setupMap); + } + } + } + // Also store SetupFileName separately if needed + if (pair.first == "SetupFileName" && pair.second->isString()) { + ti.value = pair.second->getString(); + if (ti.key.empty() || ti.key == "Instruments") { + ti.key = pair.first; + } + } + } + tmpl.testInput.push_back(ti); + } + } + + auto templateCalls = json->get("Template"); + if (templateCalls->isArray()) { + for (const auto& call : templateCalls->getArray()) { + TemplateCall tc; + tc.call = call->get("call")->getString(); + + auto args = call->get("args"); + if (args->isArray()) { + for (const auto& arg : args->getArray()) { + tc.args.push_back(arg->getString()); + } + } + + auto ret = call->get("return"); + if (ret->isString()) { + tc.returnVar = ret->getString(); + } + + tmpl.templateCalls.push_back(tc); + } + } + + return tmpl; +} + +TestExecution CodeGenerator::parseTestExecution(std::shared_ptr json) { + TestExecution testExec; + testExec.name = json->get("Name")->getString(); + + auto configs = json->get("Configurations"); + if (configs->isArray()) { + for (const auto& config : configs->getArray()) { + TestConfiguration tc; + tc.option = config->get("Option")->getString(); + tc.pinType = config->get("PinType")->getString(); + testExec.configurations.push_back(tc); + } + } + + return testExec; +} + +bool CodeGenerator::loadConfigurations() { + JsonParser parser; + + // Load InstrumentConfig from variant + std::string instConfigPath = FileUtils::joinPath(config.variantPath, "InstrumentConfig.json"); + std::string instConfigContent = FileUtils::readFile(instConfigPath); + if (instConfigContent.empty()) { + std::cerr << "Failed to read: " << instConfigPath << std::endl; + return false; + } + + auto instConfigJson = parser.parse(instConfigContent); + auto instruments = instConfigJson->get("Instruments"); + if (instruments->isArray()) { + for (const auto& inst : instruments->getArray()) { + config.instruments.push_back(parseInstrument(inst)); + } + } + + // Load setups from common path + std::string commonSetupsPath = FileUtils::joinPath(config.commonPath, "setups"); + auto setupFiles = FileUtils::listFiles(commonSetupsPath, ".json"); + for (const auto& filename : setupFiles) { + std::string filepath = FileUtils::joinPath(commonSetupsPath, filename); + std::string content = FileUtils::readFile(filepath); + if (!content.empty()) { + auto setupJson = parser.parse(content); + std::string baseName = filename.substr(0, filename.rfind('.')); + commonSetups[baseName] = parseSetupFile(setupJson); + } + } + + // Load setups from variant path + std::string variantSetupsPath = config.variantPath; + setupFiles = FileUtils::listFiles(variantSetupsPath, ".json"); + for (const auto& filename : setupFiles) { + if (filename.find("Setup_") == 0) { + std::string filepath = FileUtils::joinPath(variantSetupsPath, filename); + std::string content = FileUtils::readFile(filepath); + if (!content.empty()) { + auto setupJson = parser.parse(content); + std::string baseName = filename.substr(0, filename.rfind('.')); + variantSetups[baseName] = parseSetupFile(setupJson); + } + } + } + + // Load templates from common path + std::string templatesPath = FileUtils::joinPath(config.commonPath, "templates"); + auto templateFiles = FileUtils::listFiles(templatesPath, ".json"); + for (const auto& filename : templateFiles) { + std::string filepath = FileUtils::joinPath(templatesPath, filename); + std::string content = FileUtils::readFile(filepath); + if (!content.empty()) { + auto tmplJson = parser.parse(content); + config.templates.push_back(parseTemplate(tmplJson)); + } + } + + // Load execution config from variant path + std::string execConfigPath = FileUtils::joinPath(config.variantPath, "ExecutionConfig.json"); + std::string execConfigContent = FileUtils::readFile(execConfigPath); + if (!execConfigContent.empty()) { + auto execJson = parser.parse(execConfigContent); + auto tests = execJson->get("Tests"); + if (tests->isArray()) { + for (const auto& test : tests->getArray()) { + config.testExecutions.push_back(parseTestExecution(test)); + } + } + } + + return true; +} + +std::string CodeGenerator::substituteVariables(const std::string& expr, const std::map& localVars) { + std::string result = expr; + + // Find all variables (starting with $) + size_t pos = 0; + while ((pos = result.find('$', pos)) != std::string::npos) { + size_t end = pos + 1; + while (end < result.length() && (std::isalnum(result[end]) || result[end] == '_')) { + end++; + } + + std::string varName = result.substr(pos, end - pos); + std::string replacement = varName; + + // Look up variable value + auto it = localVars.find(varName); + if (it != localVars.end()) { + replacement = it->second; + } else if (globalVariables.find(varName) != globalVariables.end()) { + replacement = globalVariables[varName].expression; + } + + // If expression contains multiply operator, convert to code + if (replacement.find('*') != std::string::npos) { + // Keep as expression for now, will be evaluated in generated code + replacement = varName; + } + + result.replace(pos, end - pos, replacement); + pos += replacement.length(); + } + + return result; +} + +std::string CodeGenerator::processVariableExpression(const std::string& expr) { + // Convert expressions like "0.03*gangCount" to C++ code + std::string result = expr; + + // Replace variables with their C++ equivalents + result = FileUtils::replaceAll(result, "$gangCount", "gangCount"); + result = FileUtils::replaceAll(result, "$", ""); + + return result; +} + +Setup CodeGenerator::mergeSetup(const std::string& setupName, const std::string& instrumentName) { + Setup merged; + merged.name = setupName; + + // Search through all common setup files for a setup with matching name + for (const auto& filePair : commonSetups) { + const auto& commonFile = filePair.second; + for (const auto& setup : commonFile.setups) { + if (setup.name == setupName) { + merged = setup; + break; + } + } + if (!merged.instrumentSetup.name.empty()) break; + } + + // Search through all variant setup files and merge/override + for (const auto& filePair : variantSetups) { + const auto& variantFile = filePair.second; + for (const auto& setup : variantFile.setups) { + if (setup.name == setupName) { + // Merge properties + for (const auto& prop : setup.instrumentSetup.properties) { + bool found = false; + for (auto& existingProp : merged.instrumentSetup.properties) { + if (existingProp.key == prop.key) { + existingProp.value = prop.value; + found = true; + break; + } + } + if (!found) { + merged.instrumentSetup.properties.push_back(prop); + } + } + + // Merge expects + if (!setup.instrumentSetup.expects.empty()) { + merged.instrumentSetup.expects = setup.instrumentSetup.expects; + } + + // Merge variables + for (const auto& var : variantFile.variables) { + globalVariables[var.name] = var; + } + break; + } + } + } + + return merged; +} + +std::string CodeGenerator::generateSetupMethod(const std::string& setupName, const std::string& instrumentName) { + Setup setup = mergeSetup(setupName, instrumentName); + + std::stringstream ss; + ss << " void " << setupName << "(Measurement& measurement, int gangCount) {\n"; + ss << " // Setup: " << setupName << "\n"; + + // Collect variables from all setup files that contain this setup + std::map variables; + for (const auto& filePair : commonSetups) { + for (const auto& fileSetup : filePair.second.setups) { + if (fileSetup.name == setupName) { + // Add variables from this file + for (const auto& var : filePair.second.variables) { + variables[var.name] = var.expression; + } + break; + } + } + } + for (const auto& filePair : variantSetups) { + for (const auto& fileSetup : filePair.second.setups) { + if (fileSetup.name == setupName) { + // Add/override variables from this file + for (const auto& var : filePair.second.variables) { + variables[var.name] = var.expression; + } + break; + } + } + } + + // Generate variable declarations + for (const auto& var : variables) { + std::string expr = processVariableExpression(var.second); + std::string varNameClean = var.first.substr(1); // Remove $ + ss << " double " << varNameClean << " = " << expr << ";\n"; + } + + if (!variables.empty()) { + ss << "\n"; + } + + // Generate property calls + for (const auto& prop : setup.instrumentSetup.properties) { + std::string value = substituteVariables(prop.value, variables); + value = processVariableExpression(value); + + // Convert property name: level.vforce -> level_vforce + std::string methodName = FileUtils::replaceAll(prop.key, ".", "_"); + + if (value == "true" || value == "false") { + ss << " measurement." << methodName << "(" << value << ");\n"; + } else if (value == "\"-\"" || value == "-" || value.empty()) { + // Skip removals or empty values + } else { + // Check if value is a number or variable + bool isNumeric = !value.empty() && (std::isdigit(value[0]) || value[0] == '-' || value[0] == '.'); + bool isVariable = false; + for (const auto& var : variables) { + if (var.first.substr(1) == value) { + isVariable = true; + break; + } + } + + // Special handling for methods that don't need instrument name + if (methodName == "connect" || methodName == "disconnect" || methodName == "disconnectMode") { + if (isNumeric || isVariable) { + ss << " measurement." << methodName << "(" << value << ");\n"; + } else { + ss << " measurement." << methodName << "(\"" << value << "\");\n"; + } + } else { + // Regular property setters with instrument name + if (isNumeric || isVariable) { + ss << " measurement." << methodName << "(\"" << instrumentName << "\", " << value << ");\n"; + } else { + ss << " measurement." << methodName << "(\"" << instrumentName << "\", \"" << value << "\");\n"; + } + } + } + } + + ss << " }\n\n"; + return ss.str(); +} + +std::string CodeGenerator::generateInstrumentDefinitions() { + std::stringstream ss; + + ss << "#ifndef INSTRUMENTS_H\n"; + ss << "#define INSTRUMENTS_H\n\n"; + ss << "#include \n"; + ss << "#include \n\n"; + + ss << "struct Instruments {\n"; + ss << " std::string variant;\n"; + ss << " std::vector options;\n"; + ss << " std::string pinType;\n"; + ss << " std::vector pinNumbers;\n"; + ss << " int gangCount;\n\n"; + + for (const auto& inst : config.instruments) { + std::string varName = inst.instrumentName + "_" + inst.pinType; + if (!inst.options.empty() && inst.options[0] != "") { + for (const auto& opt : inst.options) { + varName += "_" + opt; + } + } + + ss << " static Instruments " << varName << "() {\n"; + ss << " Instruments inst;\n"; + ss << " inst.variant = \"" << inst.variant << "\";\n"; + + for (const auto& opt : inst.options) { + if (!opt.empty()) { + ss << " inst.options.push_back(\"" << opt << "\");\n"; + } + } + + ss << " inst.pinType = \"" << inst.pinType << "\";\n"; + + for (const auto& pin : inst.pinNumbers) { + ss << " inst.pinNumbers.push_back(" << pin << ");\n"; + } + + ss << " inst.gangCount = " << inst.pinNumbers.size() << ";\n"; + ss << " return inst;\n"; + ss << " }\n\n"; + } + + ss << "};\n\n"; + ss << "#endif\n"; + + return ss.str(); +} + +std::string CodeGenerator::generateTestClass(const Template& tmpl, const TestExecution& testExec) { + std::stringstream ss; + + ss << "#include \"Instruments.h\"\n"; + ss << "#include \"Measurement.h\"\n\n"; + + ss << "// Test class generated from template: " << tmpl.uniqueTemplateId << "\n"; + ss << "class " << testExec.name << " {\n"; + ss << "public:\n"; + + // Generate setup methods + std::map setupMapping; + for (const auto& input : tmpl.testInput) { + if (input.key == "SetupFileName") { + for (const auto& setupMap : input.setups) { + for (const auto& pair : setupMap) { + setupMapping[pair.first] = pair.second; + ss << generateSetupMethod(pair.second, "pin1"); + } + } + } + } + + // Generate run method + ss << " void run(const Instruments& instruments) {\n"; + ss << " int gangCount = instruments.gangCount;\n\n"; + + for (const auto& call : tmpl.templateCalls) { + ss << " // " << call.call << "\n"; + + if (call.call == "createMeasurement") { + if (!call.returnVar.empty()) { + ss << " Measurement " << call.returnVar << "Obj(instruments);\n"; + } + } else if (call.call == "applySetup") { + if (call.args.size() >= 2) { + std::string setupName = call.args[1]; + auto it = setupMapping.find(setupName); + if (it != setupMapping.end()) { + ss << " " << it->second << "(" << call.args[0] << "Obj, gangCount);\n"; + } + } + } else if (call.call == "buildMeasurement") { + ss << " " << call.args[0] << "Obj.build(" << call.args[1] << ");\n"; + } else if (call.call == "executeMeasurement") { + ss << " " << call.args[0] << "Obj.execute(" << call.args[1] << ");\n"; + } else if (call.call == "ProblemCheck" || call.call == "StateCheck" || call.call == "LogSetCheck") { + ss << " " << call.args[0] << "Obj." << call.call << "();\n"; + } + } + + ss << " }\n"; + ss << "};\n\n"; + + return ss.str(); +} + +std::string CodeGenerator::generateMainFile(const std::vector& testNames) { + std::stringstream ss; + + ss << "#include \n"; + ss << "#include \"Instruments.h\"\n"; + + for (const auto& testName : testNames) { + ss << "#include \"" << testName << ".cpp\"\n"; + } + + ss << "\nint main() {\n"; + ss << " std::cout << \"Running generated tests...\" << std::endl;\n\n"; + + // Generate test runs for each configuration + for (size_t i = 0; i < config.instruments.size(); i++) { + const auto& inst = config.instruments[i]; + std::string varName = inst.instrumentName + "_" + inst.pinType; + if (!inst.options.empty() && inst.options[0] != "") { + for (const auto& opt : inst.options) { + varName += "_" + opt; + } + } + + for (const auto& testName : testNames) { + ss << " {\n"; + ss << " " << testName << " test;\n"; + ss << " test.run(Instruments::" << varName << "());\n"; + ss << " std::cout << \"Completed: " << testName << " with " << varName << "\" << std::endl;\n"; + ss << " }\n\n"; + } + } + + ss << " std::cout << \"All tests completed.\" << std::endl;\n"; + ss << " return 0;\n"; + ss << "}\n"; + + return ss.str(); +} + +bool CodeGenerator::generateCode() { + // Create output directory + std::string outputPath = FileUtils::joinPath(config.variantPath, "generatedTests"); + FileUtils::createDirectory(outputPath); + FileUtils::cleanDirectory(outputPath); + + // Generate Instruments.h + std::string instrumentsHeader = generateInstrumentDefinitions(); + std::string instrumentsPath = FileUtils::joinPath(outputPath, "Instruments.h"); + if (!FileUtils::writeFile(instrumentsPath, instrumentsHeader)) { + std::cerr << "Failed to write Instruments.h" << std::endl; + return false; + } + + // Generate Measurement stub header + std::stringstream measurementHeader; + measurementHeader << "#ifndef MEASUREMENT_H\n"; + measurementHeader << "#define MEASUREMENT_H\n\n"; + measurementHeader << "#include \"Instruments.h\"\n\n"; + measurementHeader << "class Measurement {\n"; + measurementHeader << "public:\n"; + measurementHeader << " Measurement(const Instruments& inst) {}\n"; + measurementHeader << " void build(bool flag) {}\n"; + measurementHeader << " void execute(bool flag) {}\n"; + measurementHeader << " void ProblemCheck() {}\n"; + measurementHeader << " void StateCheck() {}\n"; + measurementHeader << " void LogSetCheck() {}\n"; + measurementHeader << " \n"; + measurementHeader << " // Property setters\n"; + measurementHeader << " template\n"; + measurementHeader << " void level_vforce(const std::string& name, T value) {}\n"; + measurementHeader << " template\n"; + measurementHeader << " void level_iclampSource(const std::string& name, T value) {}\n"; + measurementHeader << " template\n"; + measurementHeader << " void level_iclampSink(const std::string& name, T value) {}\n"; + measurementHeader << " template\n"; + measurementHeader << " void level_vrange(const std::string& name, T value) {}\n"; + measurementHeader << " template\n"; + measurementHeader << " void level_irange(const std::string& name, T value) {}\n"; + measurementHeader << " void connect(bool flag) {}\n"; + measurementHeader << " void disconnect(bool flag) {}\n"; + measurementHeader << " void disconnectMode(const std::string& mode) {}\n"; + measurementHeader << "};\n\n"; + measurementHeader << "#endif\n"; + + std::string measurementPath = FileUtils::joinPath(outputPath, "Measurement.h"); + if (!FileUtils::writeFile(measurementPath, measurementHeader.str())) { + std::cerr << "Failed to write Measurement.h" << std::endl; + return false; + } + + // Generate test classes + std::vector testNames; + for (const auto& testExec : config.testExecutions) { + // Find matching template + for (const auto& tmpl : config.templates) { + std::string testFileName = testExec.name + ".cpp"; + std::string testContent = generateTestClass(tmpl, testExec); + std::string testPath = FileUtils::joinPath(outputPath, testFileName); + + if (!FileUtils::writeFile(testPath, testContent)) { + std::cerr << "Failed to write " << testFileName << std::endl; + return false; + } + + testNames.push_back(testExec.name); + } + } + + // Generate main.cpp + std::string mainContent = generateMainFile(testNames); + std::string mainPath = FileUtils::joinPath(outputPath, "main.cpp"); + if (!FileUtils::writeFile(mainPath, mainContent)) { + std::cerr << "Failed to write main.cpp" << std::endl; + return false; + } + + // Generate build script for Windows + std::stringstream buildBat; + buildBat << "@echo off\n"; + buildBat << "REM Build script for generated tests\n\n"; + buildBat << "echo Building generated tests...\n\n"; + buildBat << "echo Compiling main.cpp...\n"; + buildBat << "g++ -std=c++11 -Wall -O2 -o test_runner.exe main.cpp\n\n"; + buildBat << "if errorlevel 1 (\n"; + buildBat << " echo.\n"; + buildBat << " echo Build failed!\n"; + buildBat << " exit /b 1\n"; + buildBat << ")\n\n"; + buildBat << "echo.\n"; + buildBat << "echo Build successful! Executable: test_runner.exe\n"; + buildBat << "echo.\n"; + buildBat << "echo To run the tests, execute: test_runner.exe\n"; + + std::string buildBatPath = FileUtils::joinPath(outputPath, "build.bat"); + if (!FileUtils::writeFile(buildBatPath, buildBat.str())) { + std::cerr << "Failed to write build.bat" << std::endl; + return false; + } + + // Generate Makefile for Linux/Mac + std::stringstream makefile; + makefile << "# Makefile for generated tests\n\n"; + makefile << "CXX = g++\n"; + makefile << "CXXFLAGS = -std=c++11 -Wall -O2\n"; + makefile << "TARGET = test_runner\n\n"; + makefile << "all: $(TARGET)\n\n"; + makefile << "$(TARGET): main.cpp Instruments.h Measurement.h"; + for (const auto& testName : testNames) { + makefile << " " << testName << ".cpp"; + } + makefile << "\n"; + makefile << "\t$(CXX) $(CXXFLAGS) -o $(TARGET) main.cpp\n\n"; + makefile << "clean:\n"; + makefile << "\trm -f $(TARGET) $(TARGET).exe\n\n"; + makefile << "run: $(TARGET)\n"; + makefile << "\t./$(TARGET)\n\n"; + makefile << ".PHONY: all clean run\n"; + + std::string makefilePath = FileUtils::joinPath(outputPath, "Makefile"); + if (!FileUtils::writeFile(makefilePath, makefile.str())) { + std::cerr << "Failed to write Makefile" << std::endl; + return false; + } + + // Generate README for the generated tests + std::stringstream readme; + readme << "# Generated Test Files\n\n"; + readme << "This directory contains automatically generated C++ test code.\n\n"; + readme << "## Files\n\n"; + readme << "- `Instruments.h` - Instrument configuration definitions\n"; + readme << "- `Measurement.h` - Measurement interface (stub implementation)\n"; + for (const auto& testName : testNames) { + readme << "- `" << testName << ".cpp` - Test class: " << testName << "\n"; + } + readme << "- `main.cpp` - Main entry point that executes all tests\n"; + readme << "- `build.bat` - Windows build script\n"; + readme << "- `Makefile` - Linux/Mac build script\n\n"; + readme << "## Building\n\n"; + readme << "**Windows:**\n"; + readme << "```bash\n"; + readme << "build.bat\n"; + readme << "```\n\n"; + readme << "**Linux/Mac:**\n"; + readme << "```bash\n"; + readme << "make\n"; + readme << "```\n\n"; + readme << "## Running\n\n"; + readme << "After building, run the tests:\n\n"; + readme << "**Windows:**\n"; + readme << "```bash\n"; + readme << "test_runner.exe\n"; + readme << "```\n\n"; + readme << "**Linux/Mac:**\n"; + readme << "```bash\n"; + readme << "./test_runner\n"; + readme << "```\n\n"; + readme << "## Note\n\n"; + readme << "The `Measurement.h` file contains stub implementations. You should replace\n"; + readme << "this with your actual measurement implementation or link against your\n"; + readme << "measurement library when building the tests.\n"; + + std::string readmePath = FileUtils::joinPath(outputPath, "README.md"); + if (!FileUtils::writeFile(readmePath, readme.str())) { + std::cerr << "Failed to write README.md" << std::endl; + return false; + } + + std::cout << "Code generation completed successfully!" << std::endl; + std::cout << "Generated files in: " << outputPath << std::endl; + std::cout << "\nTo build the tests:" << std::endl; + std::cout << " Windows: cd " << outputPath << " && build.bat" << std::endl; + std::cout << " Linux/Mac: cd " << outputPath << " && make" << std::endl; + + return true; +} diff --git a/TestGenerator/src/code_generator.h b/TestGenerator/src/code_generator.h new file mode 100644 index 0000000..9a391d8 --- /dev/null +++ b/TestGenerator/src/code_generator.h @@ -0,0 +1,46 @@ +#ifndef CODE_GENERATOR_H +#define CODE_GENERATOR_H + +#include "data_structures.h" +#include "json_parser.h" +#include +#include +#include + +class CodeGenerator { +private: + Config config; + std::map commonSetups; + std::map variantSetups; + std::map globalVariables; + + // Parse JSON helpers + Instrument parseInstrument(std::shared_ptr json); + Property parseProperty(std::shared_ptr json); + Variable parseVariable(std::shared_ptr json); + Setup parseSetup(std::shared_ptr json); + SetupFile parseSetupFile(std::shared_ptr json); + Template parseTemplate(std::shared_ptr json); + TestExecution parseTestExecution(std::shared_ptr json); + + // Code generation helpers + std::string generateInstrumentDefinitions(); + std::string generateTestClass(const Template& tmpl, const TestExecution& testExec); + std::string generateSetupMethod(const std::string& setupName, const std::string& instrumentName); + std::string generateMainFile(const std::vector& testNames); + + // Variable substitution + std::string substituteVariables(const std::string& expr, const std::map& localVars); + std::string processVariableExpression(const std::string& expr); + + // Setup merging + Setup mergeSetup(const std::string& setupName, const std::string& instrumentName); + +public: + CodeGenerator(const Config& cfg); + + bool loadConfigurations(); + bool generateCode(); +}; + +#endif diff --git a/TestGenerator/src/data_structures.h b/TestGenerator/src/data_structures.h new file mode 100644 index 0000000..b5b7ed6 --- /dev/null +++ b/TestGenerator/src/data_structures.h @@ -0,0 +1,117 @@ +#ifndef DATA_STRUCTURES_H +#define DATA_STRUCTURES_H + +#include +#include +#include + +// Instrument definition +struct Instrument { + std::string instrumentName; + std::string variant; + std::vector options; + std::string pinType; + std::vector pinNumbers; +}; + +// Property pair for instrument setup +struct Property { + std::string key; + std::string value; +}; + +// Check definition for expects +struct Check { + std::string key; + std::string value; +}; + +// LogSet check +struct LogSetCheck { + std::string logset; + std::vector properties; +}; + +// Expects definition +struct Expect { + std::string type; // "problem", "state", "logset" + std::vector checks; + std::vector logsetChecks; +}; + +// Instrument setup definition +struct InstrumentSetup { + std::string name; + std::vector properties; + std::vector expects; +}; + +// Setup definition +struct Setup { + std::string name; + std::vector notes; + InstrumentSetup instrumentSetup; +}; + +// Variable definition +struct Variable { + std::string name; + std::string expression; +}; + +// Setup file data +struct SetupFile { + std::vector options; + std::vector fileNotes; + std::vector variables; + std::vector setups; +}; + +// Template call +struct TemplateCall { + std::string call; + std::vector args; + std::string returnVar; +}; + +// Template input +struct TemplateInput { + std::string key; + std::string value; + std::vector> setups; +}; + +// Template definition +struct Template { + int groupId; + std::string uniqueTemplateId; + std::vector description; + std::vector features; + std::vector testInput; + std::vector templateCalls; +}; + +// Test configuration for execution +struct TestConfiguration { + std::string option; + std::string pinType; +}; + +// Test execution configuration +struct TestExecution { + std::string name; + std::vector configurations; +}; + +// Configuration data +struct Config { + std::string commonPath; + std::string variantPath; + bool dryRun; + std::vector instruments; + std::vector setupFiles; + std::vector