Skip to content

Commit c7100a1

Browse files
new test infrasturcture added
1 parent 474c32b commit c7100a1

15 files changed

Lines changed: 573 additions & 137 deletions

.github/workflows/tests.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,15 @@ jobs:
3131
php tests/index_test.php
3232
php tests/widgets_test/AdditionalWidgetsTest.php
3333
php tests/widgets_test/ButtonTest.php
34+
php tests/widgets_test/CanvasTest.php
35+
php tests/widgets_test/CheckbuttonTest.php
3436
php tests/widgets_test/GeometryTest.php
3537
php tests/widgets_test/InputTest.php
3638
php tests/widgets_test/LabelTest.php
3739
php tests/widgets_test/MenuTest.php
3840
php tests/widgets_test/MenubuttonTest.php
3941
php tests/widgets_test/MessageTest.php
42+
php tests/widgets_test/TopLevelWidgetTest.php
4043
php tests/widgets_test/WindowTest.php
4144
4245
test-macos:
@@ -59,12 +62,15 @@ jobs:
5962
php tests/index_test.php
6063
php tests/widgets_test/AdditionalWidgetsTest.php
6164
php tests/widgets_test/ButtonTest.php
65+
php tests/widgets_test/CanvasTest.php
66+
php tests/widgets_test/CheckbuttonTest.php
6267
php tests/widgets_test/GeometryTest.php
6368
php tests/widgets_test/InputTest.php
6469
php tests/widgets_test/LabelTest.php
6570
php tests/widgets_test/MenuTest.php
6671
php tests/widgets_test/MenubuttonTest.php
6772
php tests/widgets_test/MessageTest.php
73+
php tests/widgets_test/TopLevelWidgetTest.php
6874
php tests/widgets_test/WindowTest.php
6975
7076
test-windows:
@@ -87,10 +93,13 @@ jobs:
8793
php tests/index_test.php
8894
php tests/widgets_test/AdditionalWidgetsTest.php
8995
php tests/widgets_test/ButtonTest.php
96+
php tests/widgets_test/CanvasTest.php
97+
php tests/widgets_test/CheckbuttonTest.php
9098
php tests/widgets_test/GeometryTest.php
9199
php tests/widgets_test/InputTest.php
92100
php tests/widgets_test/LabelTest.php
93101
php tests/widgets_test/MenuTest.php
94102
php tests/widgets_test/MenubuttonTest.php
95103
php tests/widgets_test/MessageTest.php
104+
php tests/widgets_test/TopLevelWidgetTest.php
96105
php tests/widgets_test/WindowTest.php

tests/TestRunner.php

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
<?php
2+
3+
namespace PhpGuiTest;
4+
5+
use PhpGui\ProcessTCL;
6+
7+
/**
8+
* Minimal test runner for php-gui.
9+
* Each test file calls TestRunner::summary() at the end, which exits with
10+
* code 1 if any assertion failed — making CI failures visible.
11+
*/
12+
class TestRunner
13+
{
14+
private static int $passed = 0;
15+
private static int $failed = 0;
16+
private static string $suite = '';
17+
18+
public static function suite(string $name): void
19+
{
20+
self::$suite = $name;
21+
echo "\n[SUITE] {$name}\n";
22+
}
23+
24+
public static function assert(bool $condition, string $message): void
25+
{
26+
if ($condition) {
27+
self::$passed++;
28+
echo " [PASS] {$message}\n";
29+
} else {
30+
self::$failed++;
31+
echo " [FAIL] {$message}\n";
32+
}
33+
}
34+
35+
/** @param mixed $expected */
36+
/** @param mixed $actual */
37+
public static function assertEqual($expected, $actual, string $message): void
38+
{
39+
$ok = $expected === $actual;
40+
if (!$ok) {
41+
$message .= sprintf(
42+
' (expected %s, got %s)',
43+
var_export($expected, true),
44+
var_export($actual, true)
45+
);
46+
}
47+
self::assert($ok, $message);
48+
}
49+
50+
/** @param mixed $value */
51+
public static function assertNotEmpty($value, string $message): void
52+
{
53+
self::assert(!empty($value), $message);
54+
}
55+
56+
/**
57+
* Assert that the Tcl widget at $path exists (winfo exists returns 1).
58+
*/
59+
public static function assertWidgetExists(string $path, string $message): void
60+
{
61+
$result = trim(ProcessTCL::getInstance()->evalTcl("winfo exists {$path}"));
62+
self::assert($result === '1', $message . " (winfo exists {$path} = {$result})");
63+
}
64+
65+
/**
66+
* Assert that the Tcl widget at $path no longer exists.
67+
*/
68+
public static function assertWidgetGone(string $path, string $message): void
69+
{
70+
$result = trim(ProcessTCL::getInstance()->evalTcl("winfo exists {$path}"));
71+
self::assert($result === '0', $message . " (winfo exists {$path} = {$result})");
72+
}
73+
74+
/**
75+
* Assert that $fn throws an instance of $exceptionClass.
76+
*/
77+
public static function assertThrows(callable $fn, string $exceptionClass, string $message): void
78+
{
79+
try {
80+
$fn();
81+
self::$failed++;
82+
echo " [FAIL] {$message} (no exception thrown)\n";
83+
} catch (\Throwable $e) {
84+
if ($e instanceof $exceptionClass) {
85+
self::$passed++;
86+
echo " [PASS] {$message}\n";
87+
} else {
88+
self::$failed++;
89+
echo " [FAIL] {$message} (got " . get_class($e) . ': ' . $e->getMessage() . ")\n";
90+
}
91+
}
92+
}
93+
94+
/**
95+
* Assert that $fn does NOT throw.
96+
*/
97+
public static function assertNoThrow(callable $fn, string $message): void
98+
{
99+
try {
100+
$fn();
101+
self::$passed++;
102+
echo " [PASS] {$message}\n";
103+
} catch (\Throwable $e) {
104+
self::$failed++;
105+
echo " [FAIL] {$message} (threw " . get_class($e) . ': ' . $e->getMessage() . ")\n";
106+
}
107+
}
108+
109+
/**
110+
* Print results and exit with code 1 if any test failed.
111+
*/
112+
public static function summary(): void
113+
{
114+
$total = self::$passed + self::$failed;
115+
echo "\n--- Results: " . self::$passed . "/{$total} passed";
116+
if (self::$failed > 0) {
117+
echo ', ' . self::$failed . " FAILED";
118+
echo " ---\n";
119+
exit(1);
120+
}
121+
echo " ---\n";
122+
}
123+
}

tests/index_test.php

Lines changed: 39 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,60 @@
11
<?php
22
require_once __DIR__ . '/../vendor/autoload.php';
3+
require_once __DIR__ . '/TestRunner.php';
34

45
use PhpGui\Application;
56
use PhpGui\Widget\Window;
67
use PhpGui\Widget\Label;
78
use PhpGui\Widget\Button;
89
use PhpGui\Widget\Input;
10+
use PhpGuiTest\TestRunner;
911

1012
$app = new Application();
13+
TestRunner::suite('IntegrationTest');
1114

12-
// Create main window
13-
$window = new Window([
14-
'title' => 'Integration Test',
15-
'width' => 500,
16-
'height' => 300
17-
]);
15+
// Window
16+
$window = new Window(['title' => 'Integration Test', 'width' => 500, 'height' => 300]);
17+
$wid = $window->getId();
18+
TestRunner::assertWidgetExists(".{$wid}", 'Main window created');
1819

19-
// Label test
20-
$label = new Label($window->getId(), [
21-
'text' => 'Hello, PHP GUI World!'
22-
]);
20+
// Label
21+
$label = new Label($wid, ['text' => 'Hello, PHP GUI World!']);
22+
$lpath = ".{$wid}.{$label->getId()}";
23+
TestRunner::assertWidgetExists($lpath, 'Label created');
2324
$label->pack(['pady' => 20]);
2425

25-
// Button test
26-
$button = new Button($window->getId(), [
27-
'text' => 'Click Me',
28-
'command' => function() use ($label) {
29-
echo "Button clicked from integration test!\n";
26+
// Button — callback integration
27+
$clicked = false;
28+
$button = new Button($wid, [
29+
'text' => 'Click Me',
30+
'command' => function () use ($label, &$clicked) {
31+
$clicked = true;
3032
$label->setText('Button clicked!');
31-
}
33+
},
3234
]);
35+
$bpath = ".{$wid}.{$button->getId()}";
36+
TestRunner::assertWidgetExists($bpath, 'Button created');
3337
$button->pack(['pady' => 10]);
3438

35-
// Input test
36-
$input = new Input($window->getId(), [
37-
'text' => 'Type here...',
38-
'bg' => 'lightyellow',
39-
'fg' => 'black',
40-
'font' => 'Arial 14'
41-
]);
39+
// Simulate the click
40+
\PhpGui\ProcessTCL::getInstance()->executeCallback($button->getId());
41+
TestRunner::assert($clicked, 'Button callback fires on trigger');
42+
$labelText = trim(\PhpGui\ProcessTCL::getInstance()->evalTcl("{$lpath} cget -text"));
43+
TestRunner::assertEqual('Button clicked!', $labelText, 'Label text updated by button callback');
44+
45+
// Input — getValue/setValue
46+
$input = new Input($wid, ['text' => 'Type here...']);
47+
$ipath = ".{$wid}.{$input->getId()}";
48+
TestRunner::assertWidgetExists($ipath, 'Input created');
49+
TestRunner::assertEqual('Type here...', $input->getValue(), 'Input initial value correct');
50+
$input->setValue('Hello from test');
51+
TestRunner::assertEqual('Hello from test', $input->getValue(), 'Input setValue/getValue roundtrip');
4252
$input->pack(['pady' => 10]);
4353

44-
// Register Enter key event on input widget
45-
$input->onEnter(function() use ($input) {
46-
echo "Input Enter Pressed (Integration Test): " . $input->getValue() . "\n";
47-
});
48-
49-
// Button to show input box value
50-
$showButton = new Button($window->getId(), [
51-
'text' => 'Show Input',
52-
'command' => function() use ($input) {
53-
echo "Show Input Button clicked (Integration Test): " . $input->getValue() . "\n";
54-
}
55-
]);
56-
$showButton->pack(['pady' => 10]);
57-
58-
echo "Integration test setup complete.\n";
54+
// onEnter callback
55+
$entered = false;
56+
$input->onEnter(function () use (&$entered) { $entered = true; });
57+
\PhpGui\ProcessTCL::getInstance()->executeCallback($input->getId());
58+
TestRunner::assert($entered, 'Input onEnter callback fires on trigger');
5959

60-
// End the test without starting the main loop
61-
$app->quit();
60+
TestRunner::summary();
Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?php
22
require_once __DIR__ . '/../../vendor/autoload.php';
3+
require_once __DIR__ . '/../TestRunner.php';
34

45
use PhpGui\Application;
56
use PhpGui\Widget\Canvas;
@@ -8,33 +9,59 @@
89
use PhpGui\Widget\Entry;
910
use PhpGui\Widget\Frame;
1011
use PhpGui\Widget\Window;
12+
use PhpGuiTest\TestRunner;
1113

1214
$app = new Application();
15+
TestRunner::suite('AdditionalWidgetsTest');
16+
1317
$window = new Window(['title' => 'Additional Widgets Test']);
18+
$wid = $window->getId();
1419

15-
// Test Canvas widget
16-
$canvas = new Canvas($window->getId(), []);
17-
echo "AdditionalWidgetsTest: Canvas widget created\n";
20+
// --- Canvas ---
21+
$canvas = new Canvas($wid, ['width' => 200, 'height' => 200]);
22+
$cp = ".{$wid}.{$canvas->getId()}";
23+
TestRunner::assertWidgetExists($cp, 'Canvas Tcl widget exists');
1824
$canvas->destroy();
25+
TestRunner::assertWidgetGone($cp, 'Canvas Tcl widget gone after destroy()');
1926

20-
// Test Checkbutton widget
21-
$check = new Checkbutton($window->getId(), ['text' => 'Test Checkbutton']);
22-
echo "AdditionalWidgetsTest: Checkbutton widget created\n";
27+
// --- Checkbutton ---
28+
$check = new Checkbutton($wid, ['text' => 'My Check']);
29+
$chp = ".{$wid}.{$check->getId()}";
30+
TestRunner::assertWidgetExists($chp, 'Checkbutton Tcl widget exists');
31+
TestRunner::assert(!$check->isChecked(), 'Checkbutton starts unchecked');
32+
$check->setChecked(true);
33+
TestRunner::assert($check->isChecked(), 'setChecked(true) makes it checked');
34+
$check->setChecked(false);
35+
TestRunner::assert(!$check->isChecked(), 'setChecked(false) makes it unchecked');
36+
$check->toggle();
37+
TestRunner::assert($check->isChecked(), 'toggle() flips from false to true');
2338
$check->destroy();
39+
TestRunner::assertWidgetGone($chp, 'Checkbutton Tcl widget gone after destroy()');
2440

25-
// Test Combobox widget
26-
$combo = new Combobox($window->getId(), ['values' => 'Option1 Option2 Option3']);
27-
echo "AdditionalWidgetsTest: Combobox widget created\n";
41+
// --- Combobox ---
42+
$combo = new Combobox($wid, ['values' => 'Alpha Beta Gamma']);
43+
$cbp = ".{$wid}.{$combo->getId()}";
44+
TestRunner::assertWidgetExists($cbp, 'Combobox Tcl widget exists');
45+
$combo->setValue('Beta');
46+
TestRunner::assertEqual('Beta', $combo->getValue(), 'Combobox setValue/getValue roundtrip');
2847
$combo->destroy();
48+
TestRunner::assertWidgetGone($cbp, 'Combobox Tcl widget gone after destroy()');
2949

30-
// Test Entry widget
31-
$entry = new Entry($window->getId(), ['text' => 'Default Entry']);
32-
echo "AdditionalWidgetsTest: Entry widget created\n";
50+
// --- Entry ---
51+
$entry = new Entry($wid, ['text' => 'Default']);
52+
$ep = ".{$wid}.{$entry->getId()}";
53+
TestRunner::assertWidgetExists($ep, 'Entry Tcl widget exists');
54+
TestRunner::assertEqual('Default', $entry->getValue(), 'Entry initial text correct');
55+
$entry->setValue('Modified');
56+
TestRunner::assertEqual('Modified', $entry->getValue(), 'Entry setValue/getValue roundtrip');
3357
$entry->destroy();
58+
TestRunner::assertWidgetGone($ep, 'Entry Tcl widget gone after destroy()');
3459

35-
// Test Frame widget
36-
$frame = new Frame($window->getId(), []);
37-
echo "AdditionalWidgetsTest: Frame widget created\n";
60+
// --- Frame ---
61+
$frame = new Frame($wid, []);
62+
$fp = ".{$wid}.{$frame->getId()}";
63+
TestRunner::assertWidgetExists($fp, 'Frame Tcl widget exists');
3864
$frame->destroy();
65+
TestRunner::assertWidgetGone($fp, 'Frame Tcl widget gone after destroy()');
3966

40-
$app->quit();
67+
TestRunner::summary();

0 commit comments

Comments
 (0)