Initial commit
This commit is contained in:
BIN
Documentation/Test Streamlining .pptx
Normal file
BIN
Documentation/Test Streamlining .pptx
Normal file
Binary file not shown.
6
SMT_PlayGround/hw_ext_tests/InstrumentConfig.json
Normal file
6
SMT_PlayGround/hw_ext_tests/InstrumentConfig.json
Normal file
@@ -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"]
|
||||
}}
|
||||
5
SMT_PlayGround/hw_ext_tests/VariantConfig.json
Normal file
5
SMT_PlayGround/hw_ext_tests/VariantConfig.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"Variants": [
|
||||
{ "Variant": "AVI64","Directory": "hw_ext_variants\\Avi64\\Driver\\TestSetup","FacadeName": "AVI64" }
|
||||
]
|
||||
}
|
||||
66
SMT_PlayGround/hw_ext_tests/setups/Setup_Simple_Typ.json
Normal file
66
SMT_PlayGround/hw_ext_tests/setups/Setup_Simple_Typ.json
Normal file
@@ -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" }] }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
31
SMT_PlayGround/hw_ext_tests/templates/Test_Level_Simple.json
Normal file
31
SMT_PlayGround/hw_ext_tests/templates/Test_Level_Simple.json
Normal file
@@ -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"] }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"Tests": [
|
||||
{
|
||||
"Name": "Test_Level_Simple",
|
||||
"Configurations": [
|
||||
{
|
||||
"Option": "",
|
||||
"PinType": "single"
|
||||
},
|
||||
{
|
||||
"Option": "",
|
||||
"PinType": "ganged"
|
||||
},
|
||||
{
|
||||
"Option": "highCurrent",
|
||||
"PinType": "single"
|
||||
},
|
||||
{
|
||||
"Option": "highCurrent",
|
||||
"PinType": "ganged"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef INSTRUMENTS_H
|
||||
#define INSTRUMENTS_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct Instruments {
|
||||
std::string variant;
|
||||
std::vector<std::string> options;
|
||||
std::string pinType;
|
||||
std::vector<int> 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
|
||||
@@ -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
|
||||
@@ -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<typename T>
|
||||
void level_vforce(const std::string& name, T value) {}
|
||||
template<typename T>
|
||||
void level_iclampSource(const std::string& name, T value) {}
|
||||
template<typename T>
|
||||
void level_iclampSink(const std::string& name, T value) {}
|
||||
template<typename T>
|
||||
void level_vrange(const std::string& name, T value) {}
|
||||
template<typename T>
|
||||
void level_irange(const std::string& name, T value) {}
|
||||
void connect(bool flag) {}
|
||||
void disconnect(bool flag) {}
|
||||
void disconnectMode(const std::string& mode) {}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -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.
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,34 @@
|
||||
#include <iostream>
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Auto-generated instrument definitions - do not edit
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
|
||||
// Instrument definitions
|
||||
struct InstrumentDef {
|
||||
std::string name;
|
||||
std::string variant;
|
||||
std::vector<std::string> options;
|
||||
std::string pinType;
|
||||
std::vector<int> pinNumbers;
|
||||
};
|
||||
|
||||
// All configured instruments
|
||||
static const std::vector<InstrumentDef> 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
|
||||
}
|
||||
};
|
||||
@@ -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_;
|
||||
};
|
||||
20
SMT_PlayGround/hw_ext_variants/Avi64/generatedTests/main.cpp
Normal file
20
SMT_PlayGround/hw_ext_variants/Avi64/generatedTests/main.cpp
Normal file
@@ -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_<method_name>(instruments);
|
||||
test.execute();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
53
TestGenerator/Makefile
Normal file
53
TestGenerator/Makefile
Normal file
@@ -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
|
||||
115
TestGenerator/README.md
Normal file
115
TestGenerator/README.md
Normal file
@@ -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 <common_path> -variantPath <variant_path>
|
||||
```
|
||||
|
||||
Example:
|
||||
```bash
|
||||
testgen -commonPath ..\SMT_PlayGround\hw_ext_tests -variantPath ..\SMT_PlayGround\hw_ext_variants\Avi64\Driver\TestSetup
|
||||
```
|
||||
|
||||
### Validate JSON File
|
||||
```bash
|
||||
testgen -dry <json_file>
|
||||
```
|
||||
|
||||
Example:
|
||||
```bash
|
||||
testgen -dry test.json
|
||||
```
|
||||
|
||||
### Display Help
|
||||
```bash
|
||||
testgen -help
|
||||
```
|
||||
|
||||
## Generated Files
|
||||
|
||||
The tool generates C++ files in `<variantPath>/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 <variantPath>/generatedTests
|
||||
build.bat
|
||||
test_runner.exe
|
||||
```
|
||||
|
||||
**Linux/Mac:**
|
||||
```bash
|
||||
cd <variantPath>/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`
|
||||
37
TestGenerator/build.bat
Normal file
37
TestGenerator/build.bat
Normal file
@@ -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
|
||||
49
TestGenerator/copilot_instrucations
Normal file
49
TestGenerator/copilot_instrucations
Normal file
@@ -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 <commonPath>`: specify common location (hw_ext_tests)
|
||||
- `-variantPath <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 <variantPath>/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 <commonPath>/templates and shall have the same name as the json (`testFileName`)
|
||||
- This class MUST have setup methods per instrumentSetup from <commonPath>/setups as base and over write them with setup specified by setups in <variantPath>/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
|
||||
|
||||
820
TestGenerator/src/code_generator.cpp
Normal file
820
TestGenerator/src/code_generator.cpp
Normal file
@@ -0,0 +1,820 @@
|
||||
#include "code_generator.h"
|
||||
#include "file_utils.h"
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
CodeGenerator::CodeGenerator(const Config& cfg) : config(cfg) {}
|
||||
|
||||
Instrument CodeGenerator::parseInstrument(std::shared_ptr<JsonValue> 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<JsonValue> 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<JsonValue> 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<JsonValue> 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<JsonValue> 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<JsonValue> 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<std::string, std::string> 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<JsonValue> 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<std::string, std::string>& 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<std::string, std::string> 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 <string>\n";
|
||||
ss << "#include <vector>\n\n";
|
||||
|
||||
ss << "struct Instruments {\n";
|
||||
ss << " std::string variant;\n";
|
||||
ss << " std::vector<std::string> options;\n";
|
||||
ss << " std::string pinType;\n";
|
||||
ss << " std::vector<int> 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<std::string, std::string> 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<std::string>& testNames) {
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "#include <iostream>\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<typename T>\n";
|
||||
measurementHeader << " void level_vforce(const std::string& name, T value) {}\n";
|
||||
measurementHeader << " template<typename T>\n";
|
||||
measurementHeader << " void level_iclampSource(const std::string& name, T value) {}\n";
|
||||
measurementHeader << " template<typename T>\n";
|
||||
measurementHeader << " void level_iclampSink(const std::string& name, T value) {}\n";
|
||||
measurementHeader << " template<typename T>\n";
|
||||
measurementHeader << " void level_vrange(const std::string& name, T value) {}\n";
|
||||
measurementHeader << " template<typename T>\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<std::string> 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;
|
||||
}
|
||||
46
TestGenerator/src/code_generator.h
Normal file
46
TestGenerator/src/code_generator.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef CODE_GENERATOR_H
|
||||
#define CODE_GENERATOR_H
|
||||
|
||||
#include "data_structures.h"
|
||||
#include "json_parser.h"
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
class CodeGenerator {
|
||||
private:
|
||||
Config config;
|
||||
std::map<std::string, SetupFile> commonSetups;
|
||||
std::map<std::string, SetupFile> variantSetups;
|
||||
std::map<std::string, Variable> globalVariables;
|
||||
|
||||
// Parse JSON helpers
|
||||
Instrument parseInstrument(std::shared_ptr<JsonValue> json);
|
||||
Property parseProperty(std::shared_ptr<JsonValue> json);
|
||||
Variable parseVariable(std::shared_ptr<JsonValue> json);
|
||||
Setup parseSetup(std::shared_ptr<JsonValue> json);
|
||||
SetupFile parseSetupFile(std::shared_ptr<JsonValue> json);
|
||||
Template parseTemplate(std::shared_ptr<JsonValue> json);
|
||||
TestExecution parseTestExecution(std::shared_ptr<JsonValue> 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<std::string>& testNames);
|
||||
|
||||
// Variable substitution
|
||||
std::string substituteVariables(const std::string& expr, const std::map<std::string, std::string>& 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
|
||||
117
TestGenerator/src/data_structures.h
Normal file
117
TestGenerator/src/data_structures.h
Normal file
@@ -0,0 +1,117 @@
|
||||
#ifndef DATA_STRUCTURES_H
|
||||
#define DATA_STRUCTURES_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
// Instrument definition
|
||||
struct Instrument {
|
||||
std::string instrumentName;
|
||||
std::string variant;
|
||||
std::vector<std::string> options;
|
||||
std::string pinType;
|
||||
std::vector<std::string> 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<Property> properties;
|
||||
};
|
||||
|
||||
// Expects definition
|
||||
struct Expect {
|
||||
std::string type; // "problem", "state", "logset"
|
||||
std::vector<Check> checks;
|
||||
std::vector<LogSetCheck> logsetChecks;
|
||||
};
|
||||
|
||||
// Instrument setup definition
|
||||
struct InstrumentSetup {
|
||||
std::string name;
|
||||
std::vector<Property> properties;
|
||||
std::vector<Expect> expects;
|
||||
};
|
||||
|
||||
// Setup definition
|
||||
struct Setup {
|
||||
std::string name;
|
||||
std::vector<std::string> notes;
|
||||
InstrumentSetup instrumentSetup;
|
||||
};
|
||||
|
||||
// Variable definition
|
||||
struct Variable {
|
||||
std::string name;
|
||||
std::string expression;
|
||||
};
|
||||
|
||||
// Setup file data
|
||||
struct SetupFile {
|
||||
std::vector<std::string> options;
|
||||
std::vector<std::string> fileNotes;
|
||||
std::vector<Variable> variables;
|
||||
std::vector<Setup> setups;
|
||||
};
|
||||
|
||||
// Template call
|
||||
struct TemplateCall {
|
||||
std::string call;
|
||||
std::vector<std::string> args;
|
||||
std::string returnVar;
|
||||
};
|
||||
|
||||
// Template input
|
||||
struct TemplateInput {
|
||||
std::string key;
|
||||
std::string value;
|
||||
std::vector<std::map<std::string, std::string>> setups;
|
||||
};
|
||||
|
||||
// Template definition
|
||||
struct Template {
|
||||
int groupId;
|
||||
std::string uniqueTemplateId;
|
||||
std::vector<std::string> description;
|
||||
std::vector<std::string> features;
|
||||
std::vector<TemplateInput> testInput;
|
||||
std::vector<TemplateCall> templateCalls;
|
||||
};
|
||||
|
||||
// Test configuration for execution
|
||||
struct TestConfiguration {
|
||||
std::string option;
|
||||
std::string pinType;
|
||||
};
|
||||
|
||||
// Test execution configuration
|
||||
struct TestExecution {
|
||||
std::string name;
|
||||
std::vector<TestConfiguration> configurations;
|
||||
};
|
||||
|
||||
// Configuration data
|
||||
struct Config {
|
||||
std::string commonPath;
|
||||
std::string variantPath;
|
||||
bool dryRun;
|
||||
std::vector<Instrument> instruments;
|
||||
std::vector<SetupFile> setupFiles;
|
||||
std::vector<Template> templates;
|
||||
std::vector<TestExecution> testExecutions;
|
||||
};
|
||||
|
||||
#endif
|
||||
200
TestGenerator/src/file_utils.cpp
Normal file
200
TestGenerator/src/file_utils.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
#include "file_utils.h"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <sys/stat.h>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <direct.h>
|
||||
#define PATH_SEPARATOR "\\"
|
||||
#define mkdir(path, mode) _mkdir(path)
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#define PATH_SEPARATOR "/"
|
||||
#endif
|
||||
|
||||
std::string FileUtils::readFile(const std::string& filepath) {
|
||||
std::ifstream file(filepath);
|
||||
if (!file.is_open()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
bool FileUtils::writeFile(const std::string& filepath, const std::string& content) {
|
||||
std::ofstream file(filepath);
|
||||
if (!file.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
file << content;
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileUtils::fileExists(const std::string& filepath) {
|
||||
struct stat buffer;
|
||||
return (stat(filepath.c_str(), &buffer) == 0);
|
||||
}
|
||||
|
||||
bool FileUtils::createDirectory(const std::string& dirpath) {
|
||||
if (dirpath.empty()) return false;
|
||||
if (fileExists(dirpath)) return true;
|
||||
|
||||
// Create parent directories recursively
|
||||
size_t pos = 0;
|
||||
std::string path = dirpath;
|
||||
|
||||
// Normalize path separators
|
||||
std::replace(path.begin(), path.end(), '/', PATH_SEPARATOR[0]);
|
||||
std::replace(path.begin(), path.end(), '\\', PATH_SEPARATOR[0]);
|
||||
|
||||
// Skip drive letter on Windows
|
||||
if (path.length() > 1 && path[1] == ':') {
|
||||
pos = 2;
|
||||
}
|
||||
|
||||
while (pos != std::string::npos) {
|
||||
pos = path.find(PATH_SEPARATOR, pos + 1);
|
||||
std::string subpath = path.substr(0, pos);
|
||||
|
||||
if (!subpath.empty() && !fileExists(subpath)) {
|
||||
if (mkdir(subpath.c_str(), 0755) != 0 && !fileExists(subpath)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileUtils::cleanDirectory(const std::string& dirpath) {
|
||||
if (!fileExists(dirpath)) return true;
|
||||
|
||||
#ifdef _WIN32
|
||||
WIN32_FIND_DATA findData;
|
||||
std::string pattern = dirpath + "\\*";
|
||||
HANDLE hFind = FindFirstFile(pattern.c_str(), &findData);
|
||||
|
||||
if (hFind != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
std::string filename = findData.cFileName;
|
||||
if (filename != "." && filename != "..") {
|
||||
std::string filepath = dirpath + "\\" + filename;
|
||||
DeleteFile(filepath.c_str());
|
||||
}
|
||||
} while (FindNextFile(hFind, &findData));
|
||||
FindClose(hFind);
|
||||
}
|
||||
#else
|
||||
DIR* dir = opendir(dirpath.c_str());
|
||||
if (dir) {
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dir)) != nullptr) {
|
||||
std::string filename = entry->d_name;
|
||||
if (filename != "." && filename != "..") {
|
||||
std::string filepath = dirpath + "/" + filename;
|
||||
unlink(filepath.c_str());
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string FileUtils::joinPath(const std::string& path1, const std::string& path2) {
|
||||
if (path1.empty()) return path2;
|
||||
if (path2.empty()) return path1;
|
||||
|
||||
std::string result = path1;
|
||||
if (result.back() != '/' && result.back() != '\\') {
|
||||
result += PATH_SEPARATOR;
|
||||
}
|
||||
|
||||
// Skip leading separator in path2
|
||||
size_t start = 0;
|
||||
while (start < path2.length() && (path2[start] == '/' || path2[start] == '\\')) {
|
||||
start++;
|
||||
}
|
||||
|
||||
result += path2.substr(start);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> FileUtils::listFiles(const std::string& dirpath, const std::string& extension) {
|
||||
std::vector<std::string> files;
|
||||
|
||||
#ifdef _WIN32
|
||||
WIN32_FIND_DATA findData;
|
||||
std::string pattern = dirpath + "\\*";
|
||||
HANDLE hFind = FindFirstFile(pattern.c_str(), &findData);
|
||||
|
||||
if (hFind != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
std::string filename = findData.cFileName;
|
||||
if (filename != "." && filename != "..") {
|
||||
if (extension.empty() || filename.length() >= extension.length() &&
|
||||
filename.substr(filename.length() - extension.length()) == extension) {
|
||||
files.push_back(filename);
|
||||
}
|
||||
}
|
||||
} while (FindNextFile(hFind, &findData));
|
||||
FindClose(hFind);
|
||||
}
|
||||
#else
|
||||
DIR* dir = opendir(dirpath.c_str());
|
||||
if (dir) {
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dir)) != nullptr) {
|
||||
std::string filename = entry->d_name;
|
||||
if (filename != "." && filename != "..") {
|
||||
if (extension.empty() || filename.length() >= extension.length() &&
|
||||
filename.substr(filename.length() - extension.length()) == extension) {
|
||||
files.push_back(filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
#endif
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
std::string FileUtils::replaceAll(const std::string& str, const std::string& from, const std::string& to) {
|
||||
std::string result = str;
|
||||
size_t pos = 0;
|
||||
while ((pos = result.find(from, pos)) != std::string::npos) {
|
||||
result.replace(pos, from.length(), to);
|
||||
pos += to.length();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string FileUtils::trim(const std::string& str) {
|
||||
size_t first = str.find_first_not_of(" \t\n\r");
|
||||
if (first == std::string::npos) return "";
|
||||
|
||||
size_t last = str.find_last_not_of(" \t\n\r");
|
||||
return str.substr(first, last - first + 1);
|
||||
}
|
||||
|
||||
std::vector<std::string> FileUtils::split(const std::string& str, char delimiter) {
|
||||
std::vector<std::string> tokens;
|
||||
std::stringstream ss(str);
|
||||
std::string token;
|
||||
|
||||
while (std::getline(ss, token, delimiter)) {
|
||||
tokens.push_back(token);
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
41
TestGenerator/src/file_utils.h
Normal file
41
TestGenerator/src/file_utils.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifndef FILE_UTILS_H
|
||||
#define FILE_UTILS_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// File utility functions
|
||||
class FileUtils {
|
||||
public:
|
||||
// Read entire file content
|
||||
static std::string readFile(const std::string& filepath);
|
||||
|
||||
// Write content to file
|
||||
static bool writeFile(const std::string& filepath, const std::string& content);
|
||||
|
||||
// Check if file exists
|
||||
static bool fileExists(const std::string& filepath);
|
||||
|
||||
// Create directory if it doesn't exist (recursive)
|
||||
static bool createDirectory(const std::string& dirpath);
|
||||
|
||||
// Delete all files in a directory
|
||||
static bool cleanDirectory(const std::string& dirpath);
|
||||
|
||||
// Join paths with proper separators
|
||||
static std::string joinPath(const std::string& path1, const std::string& path2);
|
||||
|
||||
// Get files in directory matching pattern
|
||||
static std::vector<std::string> listFiles(const std::string& dirpath, const std::string& extension = "");
|
||||
|
||||
// Replace all occurrences in string
|
||||
static std::string replaceAll(const std::string& str, const std::string& from, const std::string& to);
|
||||
|
||||
// Trim whitespace from string
|
||||
static std::string trim(const std::string& str);
|
||||
|
||||
// Split string by delimiter
|
||||
static std::vector<std::string> split(const std::string& str, char delimiter);
|
||||
};
|
||||
|
||||
#endif
|
||||
194
TestGenerator/src/json_parser.cpp
Normal file
194
TestGenerator/src/json_parser.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
#include "json_parser.h"
|
||||
#include <cctype>
|
||||
#include <stdexcept>
|
||||
|
||||
void JsonParser::skipWhitespace() {
|
||||
while (pos < json.length() && std::isspace(json[pos])) {
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
char JsonParser::peek() {
|
||||
skipWhitespace();
|
||||
if (pos >= json.length()) return '\0';
|
||||
return json[pos];
|
||||
}
|
||||
|
||||
char JsonParser::next() {
|
||||
skipWhitespace();
|
||||
if (pos >= json.length()) return '\0';
|
||||
return json[pos++];
|
||||
}
|
||||
|
||||
bool JsonParser::match(char c) {
|
||||
skipWhitespace();
|
||||
if (pos < json.length() && json[pos] == c) {
|
||||
pos++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string JsonParser::parseString() {
|
||||
if (!match('"')) throw std::runtime_error("Expected '\"'");
|
||||
|
||||
std::string result;
|
||||
while (pos < json.length() && json[pos] != '"') {
|
||||
if (json[pos] == '\\') {
|
||||
pos++;
|
||||
if (pos >= json.length()) throw std::runtime_error("Incomplete escape sequence");
|
||||
char c = json[pos++];
|
||||
switch (c) {
|
||||
case '"': result += '"'; break;
|
||||
case '\\': result += '\\'; break;
|
||||
case '/': result += '/'; break;
|
||||
case 'b': result += '\b'; break;
|
||||
case 'f': result += '\f'; break;
|
||||
case 'n': result += '\n'; break;
|
||||
case 'r': result += '\r'; break;
|
||||
case 't': result += '\t'; break;
|
||||
default: result += c; break;
|
||||
}
|
||||
} else {
|
||||
result += json[pos++];
|
||||
}
|
||||
}
|
||||
|
||||
if (!match('"')) throw std::runtime_error("Expected '\"'");
|
||||
return result;
|
||||
}
|
||||
|
||||
double JsonParser::parseNumber() {
|
||||
size_t start = pos;
|
||||
if (json[pos] == '-') pos++;
|
||||
|
||||
if (!std::isdigit(json[pos])) throw std::runtime_error("Invalid number");
|
||||
|
||||
while (pos < json.length() && std::isdigit(json[pos])) pos++;
|
||||
|
||||
if (pos < json.length() && json[pos] == '.') {
|
||||
pos++;
|
||||
while (pos < json.length() && std::isdigit(json[pos])) pos++;
|
||||
}
|
||||
|
||||
if (pos < json.length() && (json[pos] == 'e' || json[pos] == 'E')) {
|
||||
pos++;
|
||||
if (pos < json.length() && (json[pos] == '+' || json[pos] == '-')) pos++;
|
||||
while (pos < json.length() && std::isdigit(json[pos])) pos++;
|
||||
}
|
||||
|
||||
return std::stod(json.substr(start, pos - start));
|
||||
}
|
||||
|
||||
bool JsonParser::parseKeyword(const std::string& keyword) {
|
||||
skipWhitespace();
|
||||
if (json.substr(pos, keyword.length()) == keyword) {
|
||||
pos += keyword.length();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<JsonValue> JsonParser::parseValue() {
|
||||
char c = peek();
|
||||
|
||||
auto value = std::make_shared<JsonValue>();
|
||||
|
||||
if (c == '"') {
|
||||
value->type = JsonValue::STRING;
|
||||
value->stringValue = parseString();
|
||||
} else if (c == '{') {
|
||||
return parseObject();
|
||||
} else if (c == '[') {
|
||||
return parseArray();
|
||||
} else if (c == 't' || c == 'f') {
|
||||
value->type = JsonValue::BOOL;
|
||||
if (parseKeyword("true")) {
|
||||
value->boolValue = true;
|
||||
} else if (parseKeyword("false")) {
|
||||
value->boolValue = false;
|
||||
} else {
|
||||
throw std::runtime_error("Invalid boolean");
|
||||
}
|
||||
} else if (c == 'n') {
|
||||
if (parseKeyword("null")) {
|
||||
value->type = JsonValue::NULL_TYPE;
|
||||
} else {
|
||||
throw std::runtime_error("Invalid null");
|
||||
}
|
||||
} else if (c == '-' || std::isdigit(c)) {
|
||||
value->type = JsonValue::NUMBER;
|
||||
value->numberValue = parseNumber();
|
||||
} else {
|
||||
throw std::runtime_error("Invalid value");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
std::shared_ptr<JsonValue> JsonParser::parseObject() {
|
||||
if (!match('{')) throw std::runtime_error("Expected '{'");
|
||||
|
||||
auto obj = std::make_shared<JsonValue>();
|
||||
obj->type = JsonValue::OBJECT;
|
||||
|
||||
if (peek() == '}') {
|
||||
match('}');
|
||||
return obj;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
std::string key = parseString();
|
||||
if (!match(':')) throw std::runtime_error("Expected ':'");
|
||||
obj->objectValue[key] = parseValue();
|
||||
|
||||
if (!match(',')) break;
|
||||
if (peek() == '}') break;
|
||||
}
|
||||
|
||||
if (!match('}')) throw std::runtime_error("Expected '}'");
|
||||
return obj;
|
||||
}
|
||||
|
||||
std::shared_ptr<JsonValue> JsonParser::parseArray() {
|
||||
if (!match('[')) throw std::runtime_error("Expected '['");
|
||||
|
||||
auto arr = std::make_shared<JsonValue>();
|
||||
arr->type = JsonValue::ARRAY;
|
||||
|
||||
if (peek() == ']') {
|
||||
match(']');
|
||||
return arr;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
arr->arrayValue.push_back(parseValue());
|
||||
if (!match(',')) break;
|
||||
if (peek() == ']') break;
|
||||
}
|
||||
|
||||
if (!match(']')) throw std::runtime_error("Expected ']'");
|
||||
return arr;
|
||||
}
|
||||
|
||||
std::shared_ptr<JsonValue> JsonParser::parse(const std::string& jsonString) {
|
||||
json = jsonString;
|
||||
pos = 0;
|
||||
try {
|
||||
return parseValue();
|
||||
} catch (const std::exception& e) {
|
||||
return std::make_shared<JsonValue>();
|
||||
}
|
||||
}
|
||||
|
||||
bool JsonParser::validate(const std::string& jsonString) {
|
||||
json = jsonString;
|
||||
pos = 0;
|
||||
try {
|
||||
parseValue();
|
||||
skipWhitespace();
|
||||
return pos >= json.length();
|
||||
} catch (const std::exception& e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
65
TestGenerator/src/json_parser.h
Normal file
65
TestGenerator/src/json_parser.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#ifndef JSON_PARSER_H
|
||||
#define JSON_PARSER_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
// Simple JSON parser for basic JSON structures
|
||||
class JsonValue {
|
||||
public:
|
||||
enum Type { NULL_TYPE, BOOL, NUMBER, STRING, ARRAY, OBJECT };
|
||||
|
||||
Type type;
|
||||
bool boolValue;
|
||||
double numberValue;
|
||||
std::string stringValue;
|
||||
std::vector<std::shared_ptr<JsonValue>> arrayValue;
|
||||
std::map<std::string, std::shared_ptr<JsonValue>> objectValue;
|
||||
|
||||
JsonValue() : type(NULL_TYPE), boolValue(false), numberValue(0.0) {}
|
||||
|
||||
bool isNull() const { return type == NULL_TYPE; }
|
||||
bool isBool() const { return type == BOOL; }
|
||||
bool isNumber() const { return type == NUMBER; }
|
||||
bool isString() const { return type == STRING; }
|
||||
bool isArray() const { return type == ARRAY; }
|
||||
bool isObject() const { return type == OBJECT; }
|
||||
|
||||
bool getBool() const { return boolValue; }
|
||||
double getNumber() const { return numberValue; }
|
||||
const std::string& getString() const { return stringValue; }
|
||||
const std::vector<std::shared_ptr<JsonValue>>& getArray() const { return arrayValue; }
|
||||
const std::map<std::string, std::shared_ptr<JsonValue>>& getObject() const { return objectValue; }
|
||||
|
||||
std::shared_ptr<JsonValue> get(const std::string& key) const {
|
||||
auto it = objectValue.find(key);
|
||||
if (it != objectValue.end()) return it->second;
|
||||
return std::make_shared<JsonValue>();
|
||||
}
|
||||
};
|
||||
|
||||
class JsonParser {
|
||||
private:
|
||||
std::string json;
|
||||
size_t pos;
|
||||
|
||||
void skipWhitespace();
|
||||
char peek();
|
||||
char next();
|
||||
bool match(char c);
|
||||
std::string parseString();
|
||||
double parseNumber();
|
||||
bool parseKeyword(const std::string& keyword);
|
||||
std::shared_ptr<JsonValue> parseValue();
|
||||
std::shared_ptr<JsonValue> parseObject();
|
||||
std::shared_ptr<JsonValue> parseArray();
|
||||
|
||||
public:
|
||||
JsonParser() : pos(0) {}
|
||||
std::shared_ptr<JsonValue> parse(const std::string& jsonString);
|
||||
bool validate(const std::string& jsonString);
|
||||
};
|
||||
|
||||
#endif
|
||||
149
TestGenerator/src/main.cpp
Normal file
149
TestGenerator/src/main.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
#include "json_parser.h"
|
||||
#include "file_utils.h"
|
||||
#include "code_generator.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
void printUsage() {
|
||||
std::cout << "Test Generator - Generate C++ test code from JSON configuration\n\n";
|
||||
std::cout << "Usage:\n";
|
||||
std::cout << " testgen -commonPath <path> -variantPath <path> Generate test code\n";
|
||||
std::cout << " testgen -dry <jsonfile> Validate JSON file\n";
|
||||
std::cout << " testgen -help Display this help\n\n";
|
||||
std::cout << "Options:\n";
|
||||
std::cout << " -commonPath <path> Path to common test directory (hw_ext_tests)\n";
|
||||
std::cout << " -variantPath <path> Path to variant directory (hw_ext_variants\\Avi64\\Driver\\TestSetup)\n";
|
||||
std::cout << " -dry <jsonfile> Dry run to validate JSON file format\n";
|
||||
std::cout << " -help Display this help message\n\n";
|
||||
std::cout << "Examples:\n";
|
||||
std::cout << " testgen -commonPath hw_ext_tests -variantPath hw_ext_variants\\Avi64\\Driver\\TestSetup\n";
|
||||
std::cout << " testgen -dry test.json\n";
|
||||
}
|
||||
|
||||
bool dryRun(const std::string& jsonFile) {
|
||||
std::string content = FileUtils::readFile(jsonFile);
|
||||
if (content.empty()) {
|
||||
std::cout << "INVALID - Cannot read file: " << jsonFile << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonParser parser;
|
||||
bool valid = parser.validate(content);
|
||||
|
||||
if (valid) {
|
||||
std::cout << "VALID" << std::endl;
|
||||
return true;
|
||||
} else {
|
||||
std::cout << "INVALID" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool parseArguments(int argc, char* argv[], Config& config) {
|
||||
if (argc < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string arg1 = argv[1];
|
||||
|
||||
if (arg1 == "-help") {
|
||||
printUsage();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arg1 == "-dry") {
|
||||
if (argc < 3) {
|
||||
std::cerr << "Error: -dry requires a JSON file path\n\n";
|
||||
printUsage();
|
||||
return false;
|
||||
}
|
||||
|
||||
config.dryRun = true;
|
||||
config.commonPath = argv[2];
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parse -commonPath and -variantPath
|
||||
for (int i = 1; i < argc; i++) {
|
||||
std::string arg = argv[i];
|
||||
|
||||
if (arg == "-commonPath") {
|
||||
if (i + 1 < argc) {
|
||||
config.commonPath = argv[++i];
|
||||
} else {
|
||||
std::cerr << "Error: -commonPath requires a path argument\n\n";
|
||||
printUsage();
|
||||
return false;
|
||||
}
|
||||
} else if (arg == "-variantPath") {
|
||||
if (i + 1 < argc) {
|
||||
config.variantPath = argv[++i];
|
||||
} else {
|
||||
std::cerr << "Error: -variantPath requires a path argument\n\n";
|
||||
printUsage();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.commonPath.empty() || config.variantPath.empty()) {
|
||||
std::cerr << "Error: Both -commonPath and -variantPath are required\n\n";
|
||||
printUsage();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
Config config;
|
||||
config.dryRun = false;
|
||||
|
||||
if (!parseArguments(argc, argv, config)) {
|
||||
if (argc > 1 && std::string(argv[1]) != "-help") {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Handle dry run mode
|
||||
if (config.dryRun) {
|
||||
return dryRun(config.commonPath) ? 0 : 1;
|
||||
}
|
||||
|
||||
// Verify paths exist
|
||||
if (!FileUtils::fileExists(config.commonPath)) {
|
||||
std::cerr << "Error: commonPath does not exist: " << config.commonPath << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!FileUtils::fileExists(config.variantPath)) {
|
||||
std::cerr << "Error: variantPath does not exist: " << config.variantPath << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Test Generator" << std::endl;
|
||||
std::cout << "Common path: " << config.commonPath << std::endl;
|
||||
std::cout << "Variant path: " << config.variantPath << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
// Create code generator and load configurations
|
||||
CodeGenerator generator(config);
|
||||
|
||||
std::cout << "Loading configurations..." << std::endl;
|
||||
if (!generator.loadConfigurations()) {
|
||||
std::cerr << "Error: Failed to load configurations" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Generating code..." << std::endl;
|
||||
if (!generator.generateCode()) {
|
||||
std::cerr << "Error: Failed to generate code" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "\nCode generation completed successfully!" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user