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