Skip to content

Commit bf201cd

Browse files
committed
Add comprehensive upptst unit testing framework
- Create test directories for all examples (CharGenTest, Regression1DTest, Classify2DTest, etc.) - Add .cpp and .upp files for each test package - Implement functionality tests matching each example's core features - Create build and run scripts for individual and bulk testing - Add documentation in README_TESTS.md
1 parent 4f2d1f6 commit bf201cd

37 files changed

+1560
-0
lines changed

ConvNetCppTests.upp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "ConvNetCppTests",
3+
"include": [
4+
"src/ConvNet",
5+
"src/ConvNetCtrl",
6+
"upptst"
7+
],
8+
"defs": [],
9+
"libs": [],
10+
"out": "bin/tests",
11+
"src": [
12+
"upptst/TestMain.cpp"
13+
],
14+
"precompile": false,
15+
"console": true,
16+
"upp": [
17+
"Core",
18+
"ConvNet"
19+
]
20+
}

README_TESTS.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# ConvNetCpp Unit Tests
2+
3+
This directory contains headless unit tests for ConvNetCpp examples following the U++ upptst pattern.
4+
5+
## Directory Structure
6+
7+
Located in `upptst/` directory, each test follows the U++ convention:
8+
- `upptst/CharGenTest/` - Tests for the CharGen example functionality
9+
- `upptst/Regression1DTest/` - Tests for the Regression1D example functionality
10+
- `upptst/Classify2DTest/` - Tests for the Classify2D example functionality
11+
- `upptst/SimpleGANTest/` - Tests for the SimpleGAN example functionality
12+
- `upptst/GANTest/` - Tests for GAN-related functionality
13+
- `upptst/RegressionPainterTest/` - Tests for regression painter functionality
14+
- `upptst/ClassifyImagesTest/` - Tests for image classification functionality
15+
- `upptst/GridWorldTest/` - Tests for grid world reinforcement learning
16+
- `upptst/HeteroscedasticUncertaintyTest/` - Tests for uncertainty estimation
17+
- `upptst/MartingaleTest/` - Tests for financial prediction
18+
- `upptst/NetworkOptimizationTest/` - Tests for network optimization algorithms
19+
- `upptst/PuckWorldTest/` - Tests for continuous control environments
20+
- `upptst/ReinforcedLearningTest/` - Tests for reinforcement learning concepts
21+
- `upptst/TemporalDifferenceTest/` - Tests for temporal difference learning
22+
- `upptst/TrainerBenchmarkTest/` - Tests for different training algorithms
23+
- `upptst/WaterWorldTest/` - Tests for multi-agent reinforcement learning
24+
25+
Each test directory contains:
26+
- `TestName.cpp` - The test implementation
27+
- `TestName.upp` - The U++ project file
28+
29+
## Running Tests
30+
31+
### Build tests:
32+
33+
1. Build all tests:
34+
```bash
35+
./build-tests.sh
36+
```
37+
38+
2. Build a single test:
39+
```bash
40+
./build-tests.sh Classify2DTest
41+
```
42+
43+
3. Build multiple specific tests:
44+
```bash
45+
./build-tests.sh Classify2DTest Regression1DTest
46+
```
47+
48+
4. Build with clean option (force rebuild):
49+
```bash
50+
./build-tests.sh --clean
51+
```
52+
53+
Or for a single test:
54+
```bash
55+
./build-tests.sh --clean Classify2DTest
56+
```
57+
58+
### Run tests:
59+
60+
1. Run all tests:
61+
```bash
62+
./run-tests.sh
63+
```
64+
65+
2. Run a single test:
66+
```bash
67+
./run-test.sh Classify2DTest
68+
```
69+
70+
### Available Test Names:
71+
`CharGenTest`, `Regression1DTest`, `Classify2DTest`, `SimpleGANTest`, `GANTest`, `RegressionPainterTest`, `ClassifyImagesTest`, `GridWorldTest`, `HeteroscedasticUncertaintyTest`, `MartingaleTest`, `NetworkOptimizationTest`, `PuckWorldTest`, `ReinforcedLearningTest`, `TemporalDifferenceTest`, `TrainerBenchmarkTest`, `WaterWorldTest`
72+
73+
## Framework
74+
75+
Tests use the U++ Core library and ConvNetCpp functionality, with assertions to verify correct behavior. Each test is a standalone console application that follows the `CONSOLE_APP_MAIN` pattern from U++.
76+
77+
All tests are headless (no GUI components) and focus on the core neural network functionality demonstrated in the examples.

build-tests.sh

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/bin/bash
2+
3+
# Build script for ConvNetCpp unit tests following U++ upptst conventions
4+
5+
# Check for --clean argument
6+
CLEAN=""
7+
if [ "$1" = "--clean" ]; then
8+
CLEAN="a"
9+
shift
10+
fi
11+
12+
# Build single test if argument provided, otherwise build all
13+
if [ $# -gt 0 ]; then
14+
TESTS=("$@")
15+
echo "Building specific test(s): ${TESTS[*]} (clean=$CLEAN)"
16+
else
17+
TESTS=("CharGenTest" "Regression1DTest" "Classify2DTest" "SimpleGANTest" "GANTest" "RegressionPainterTest" "ClassifyImagesTest" "GridWorldTest" "HeteroscedasticUncertaintyTest" "MartingaleTest" "NetworkOptimizationTest" "PuckWorldTest" "ReinforcedLearningTest" "TemporalDifferenceTest" "TrainerBenchmarkTest" "WaterWorldTest")
18+
echo "Building all ConvNetCpp unit tests... (clean=$CLEAN)"
19+
fi
20+
21+
for test in "${TESTS[@]}"; do
22+
echo "Building $test..."
23+
if [ -d "upptst/$test" ]; then
24+
if command -v umk >/dev/null 2>&1; then
25+
# Use umk to build the test - call from project root with path to .upp file
26+
umk upptst,$HOME/upp/uppsrc,$HOME/upp/bazaar,src $test ~/.config/u++/theide/CLANG.bm -bds${CLEAN} +CONSOLE,DEBUG_FULL "bin/${test}"
27+
if [ $? -eq 0 ]; then
28+
echo "$test built successfully"
29+
else
30+
echo "$test build failed"
31+
fi
32+
else
33+
echo " ✗ U++ build system (umk) not found. Please ensure U++ is installed."
34+
fi
35+
else
36+
echo " ✗ Directory upptst/$test not found"
37+
fi
38+
done
39+
40+
echo "Build process completed!"

run-test.sh

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/bin/bash
2+
3+
# Script to run specific ConvNetCpp unit tests
4+
echo "Running specific ConvNetCpp unit tests..."
5+
6+
# Check if a test name was provided
7+
if [ $# -eq 0 ]; then
8+
echo "Usage: $0 <TestName>"
9+
echo "Example: $0 Classify2DTest"
10+
echo "Available tests: CharGenTest, Regression1DTest, Classify2DTest, SimpleGANTest, GANTest, RegressionPainterTest, ClassifyImagesTest, GridWorldTest, HeteroscedasticUncertaintyTest, MartingaleTest, NetworkOptimizationTest, PuckWorldTest, ReinforcedLearningTest, TemporalDifferenceTest, TrainerBenchmarkTest, WaterWorldTest"
11+
exit 1
12+
fi
13+
14+
TEST_NAME="$1"
15+
16+
# Create bin directory if it doesn't exist
17+
mkdir -p bin
18+
19+
echo "========================================"
20+
echo "Running $TEST_NAME..."
21+
echo "========================================"
22+
23+
if [ -f "bin/$TEST_NAME" ]; then
24+
"./bin/$TEST_NAME"
25+
result=$?
26+
if [ $result -eq 0 ]; then
27+
echo "$TEST_NAME PASSED"
28+
else
29+
echo "$TEST_NAME FAILED with exit code $result"
30+
fi
31+
else
32+
echo "$TEST_NAME executable not found in bin/"
33+
echo "Did you build the test first? Try: ./build-tests.sh $TEST_NAME"
34+
fi
35+
36+
echo "Test $TEST_NAME completed!"

run-tests.sh

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/bin/bash
2+
3+
# Script to run all ConvNetCpp unit tests
4+
echo "Running ALL ConvNetCpp unit tests..."
5+
6+
# Create bin directory if it doesn't exist
7+
mkdir -p bin
8+
9+
# Run each test executable
10+
TESTS=("CharGenTest" "Regression1DTest" "Classify2DTest" "SimpleGANTest" "GANTest" "RegressionPainterTest" "ClassifyImagesTest" "GridWorldTest" "HeteroscedasticUncertaintyTest" "MartingaleTest" "NetworkOptimizationTest" "PuckWorldTest" "ReinforcedLearningTest" "TemporalDifferenceTest" "TrainerBenchmarkTest" "WaterWorldTest")
11+
12+
for test in "${TESTS[@]}"; do
13+
echo "========================================"
14+
echo "Running $test..."
15+
echo "========================================"
16+
17+
if [ -f "bin/$test" ]; then
18+
"./bin/$test"
19+
result=$?
20+
if [ $result -eq 0 ]; then
21+
echo "$test PASSED"
22+
else
23+
echo "$test FAILED with exit code $result"
24+
fi
25+
else
26+
echo "$test executable not found in bin/"
27+
echo " Try: ./build-tests.sh $test"
28+
fi
29+
echo ""
30+
done
31+
32+
echo "All tests completed!"

upptst/CharGenTest/CharGenTest.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#include <Core/Core.h>
2+
#include <ConvNet/ConvNet.h>
3+
4+
using namespace Upp;
5+
using namespace ConvNet;
6+
7+
CONSOLE_APP_MAIN
8+
{
9+
SeedRandom();
10+
11+
// Test CharGen vocabulary setup functionality
12+
Vector<WString> sents;
13+
sents.Add(WString("hello"));
14+
sents.Add(WString("world"));
15+
16+
VectorMap<int, int> letterToIndex;
17+
VectorMap<int, int> indexToLetter;
18+
Vector<WString> vocab;
19+
int input_size, output_size, epoch_size;
20+
21+
// Simulate the InitVocab logic from CharGen
22+
WString txt = Join(sents, ""); // concat all
23+
VectorMap<int, int> d;
24+
for (int i = 0; i < txt.GetCount(); i++) {
25+
int txti = txt[i];
26+
d.GetAdd(txti, 0)++;
27+
}
28+
29+
// filter by count threshold and create pointers
30+
letterToIndex.Clear();
31+
indexToLetter.Clear();
32+
vocab.Clear();
33+
34+
int q = 1; // Start at 1 to reserve 0 for special tokens
35+
for (int i = 0; i < d.GetCount(); i++) {
36+
int ch = d.GetKey(i);
37+
if (d[i] >= 1) { // count threshold = 1
38+
letterToIndex.Add(ch, q);
39+
indexToLetter.Add(q, ch);
40+
vocab.Add().Cat(ch);
41+
q++;
42+
}
43+
}
44+
45+
input_size = vocab.GetCount() + 1;
46+
output_size = vocab.GetCount() + 1;
47+
epoch_size = sents.GetCount();
48+
49+
// Verify results
50+
LOG("CharGen vocabulary test:");
51+
LOG(" Vocabulary size: " << vocab.GetCount());
52+
LOG(" Input size: " << input_size);
53+
LOG(" Output size: " << output_size);
54+
LOG(" Epoch size: " << epoch_size);
55+
56+
ASSERT(vocab.GetCount() == 6); // h,e,l,o,w,r,d (duplicates removed)
57+
ASSERT(input_size == 7); // vocab size + 1 (for START token)
58+
ASSERT(output_size == 7); // vocab size + 1 (for END token)
59+
ASSERT(epoch_size == 2); // 2 sentences
60+
61+
// Test RecurrentSession with LSTM configuration
62+
RecurrentSession ses;
63+
64+
String model_str = "{\n"
65+
"\t\"generator\":\"lstm\",\n"
66+
"\t\"hidden_sizes\":[10,10],\n"
67+
"\t\"letter_size\":5,\n"
68+
"\t\"regc\":0.000001,\n"
69+
"\t\"learning_rate\":0.01,\n"
70+
"\t\"clipval\":5.0\n"
71+
"}";
72+
73+
ValueMap js = ParseJSON(model_str);
74+
ses.Load(js);
75+
ses.SetInputSize(20); // arbitrary size
76+
ses.SetOutputSize(20); // arbitrary size
77+
ses.Init();
78+
79+
LOG("CharGen RecurrentSession test:");
80+
LOG(" Learning rate: " << ses.GetLearningRate());
81+
ASSERT(ses.GetLearningRate() == 0.01);
82+
83+
LOG("CharGen tests completed successfully!");
84+
}

upptst/CharGenTest/CharGenTest.upp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
uses
2+
Core,
3+
ConvNet;
4+
5+
file
6+
CharGenTest.cpp;
7+
8+
mainconfig
9+
"" = "";
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#include <Core/Core.h>
2+
#include <ConvNet/ConvNet.h>
3+
4+
using namespace Upp;
5+
using namespace ConvNet;
6+
7+
CONSOLE_APP_MAIN
8+
{
9+
SeedRandom();
10+
11+
// Test the network structure from Classify2D example
12+
Session session;
13+
14+
String t = "[\n"
15+
"\t{\"type\":\"input\", \"input_width\":1, \"input_height\":1, \"input_depth\":2},\n"
16+
"\t{\"type\":\"fc\", \"neuron_count\":6, \"activation\": \"tanh\"},\n"
17+
"\t{\"type\":\"fc\", \"neuron_count\":2, \"activation\": \"tanh\"},\n"
18+
"\t{\"type\":\"softmax\", \"class_count\":2},\n"
19+
"\t{\"type\":\"sgd\", \"learning_rate\":0.01, \"momentum\":0.1, \"batch_size\":10, \"l2_decay\":0.001}\n"
20+
"]\n";
21+
22+
bool success = session.MakeLayers(t);
23+
24+
LOG("Classify2D network setup test:");
25+
LOG(" Network creation success: " << (success ? "true" : "false"));
26+
ASSERT(success);
27+
ASSERT(session.GetLayerCount() == 5); // Should have 5 layers
28+
29+
// Test with simple binary classification data
30+
SessionData& d = session.Data();
31+
d.BeginData(2, 6, 2); // 2 input features, 6 samples, 2 possible labels
32+
33+
// Class 0: points with x1 < 0
34+
d.SetData(0, 0, -1.0).SetData(0, 1, 0.5).SetLabel(0, 0);
35+
d.SetData(1, 0, -0.5).SetData(1, 1, -1.0).SetLabel(1, 0);
36+
d.SetData(2, 0, -2.0).SetData(2, 1, 1.5).SetLabel(2, 0);
37+
38+
// Class 1: points with x1 > 0
39+
d.SetData(3, 0, 1.0).SetData(3, 1, 0.5).SetLabel(3, 1);
40+
d.SetData(4, 0, 0.5).SetData(4, 1, -1.0).SetLabel(4, 1);
41+
d.SetData(5, 0, 2.0).SetData(5, 1, 1.5).SetLabel(5, 1);
42+
43+
d.EndData();
44+
45+
session.StartTraining();
46+
47+
// Train for a few iterations
48+
for (int i = 0; i < 50; i++) {
49+
session.Tick();
50+
}
51+
52+
session.StopTraining();
53+
54+
// Test that the network can process inputs and produce outputs
55+
Vector<double> input1 = {-1.0, 0.0}; // Should be class 0
56+
Vector<double> output1 = session.Predict(input1);
57+
58+
Vector<double> input2 = {1.0, 0.0}; // Should be class 1
59+
Vector<double> output2 = session.Predict(input2);
60+
61+
LOG("Classify2D prediction test:");
62+
LOG(" Input 1 [" << input1[0] << ", " << input1[1] << "] -> Output ["
63+
<< output1[0] << ", " << output1[1] << "]");
64+
LOG(" Input 2 [" << input2[0] << ", " << input2[1] << "] -> Output ["
65+
<< output2[0] << ", " << output2[1] << "]");
66+
67+
// Verify that the network produces valid probability outputs (sums to ~1)
68+
double sum1 = output1[0] + output1[1];
69+
double sum2 = output2[0] + output2[1];
70+
71+
ASSERT(abs(sum1 - 1.0) < 0.01); // Softmax outputs should sum to ~1
72+
ASSERT(abs(sum2 - 1.0) < 0.01); // Softmax outputs should sum to ~1
73+
74+
LOG("Classify2D tests completed successfully!");
75+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
uses
2+
Core,
3+
ConvNet;
4+
5+
file
6+
Classify2DTest.cpp;
7+
8+
mainconfig
9+
"" = "";

0 commit comments

Comments
 (0)