Skip to content

Commit 5a644b9

Browse files
authored
Merge pull request #35 from true-async/32-tests-for-mysql-pdo-native-mysql
32 tests for mysql pdo native mysql
2 parents 097f53e + 2719dc0 commit 5a644b9

32 files changed

+3719
-1
lines changed

run-tests.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ else
1616
TEST_PATH="$BASE_PATH/$1"
1717
fi
1818

19-
"$PHP_EXECUTABLE" "$RUN_TESTS_PATH" --show-diff -m -p "$PHP_EXECUTABLE" "$TEST_PATH"
19+
"$PHP_EXECUTABLE" "$RUN_TESTS_PATH" --show-diff -p "$PHP_EXECUTABLE" "$TEST_PATH"
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
--TEST--
2+
MySQLi: Basic async connection test
3+
--EXTENSIONS--
4+
mysqli
5+
--SKIPIF--
6+
<?php
7+
require_once __DIR__ . '/inc/async_mysqli_test.inc';
8+
AsyncMySQLiTest::skipIfNoAsync();
9+
AsyncMySQLiTest::skipIfNoMySQLi();
10+
AsyncMySQLiTest::skip();
11+
?>
12+
--FILE--
13+
<?php
14+
require_once __DIR__ . '/inc/async_mysqli_test.inc';
15+
16+
use function Async\spawn;
17+
use function Async\await;
18+
19+
echo "start\n";
20+
21+
$coroutine = spawn(function() {
22+
try {
23+
// Initialize database first
24+
$mysqli = AsyncMySQLiTest::initDatabase();
25+
echo "connected\n";
26+
27+
// Test simple query
28+
$result = $mysqli->query("SELECT 1 as test");
29+
if ($result) {
30+
$row = $result->fetch_assoc();
31+
echo "query result: " . $row['test'] . "\n";
32+
$result->free();
33+
}
34+
35+
$mysqli->close();
36+
echo "closed\n";
37+
38+
return "success";
39+
} catch (Exception $e) {
40+
echo "error: " . $e->getMessage() . "\n";
41+
return "failed";
42+
}
43+
});
44+
45+
$result = await($coroutine);
46+
echo "awaited: " . $result . "\n";
47+
echo "end\n";
48+
49+
?>
50+
--EXPECT--
51+
start
52+
connected
53+
query result: 1
54+
closed
55+
awaited: success
56+
end
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
--TEST--
2+
MySQLi: Async query execution
3+
--EXTENSIONS--
4+
mysqli
5+
--SKIPIF--
6+
<?php
7+
require_once __DIR__ . '/inc/async_mysqli_test.inc';
8+
AsyncMySQLiTest::skipIfNoAsync();
9+
AsyncMySQLiTest::skipIfNoMySQLi();
10+
AsyncMySQLiTest::skip();
11+
?>
12+
--FILE--
13+
<?php
14+
require_once __DIR__ . '/inc/async_mysqli_test.inc';
15+
16+
use function Async\spawn;
17+
use function Async\await;
18+
19+
echo "start\n";
20+
21+
$coroutine = spawn(function() {
22+
try {
23+
$mysqli = AsyncMySQLiTest::initDatabase();
24+
25+
// Create and populate test table
26+
$mysqli->query("DROP TEMPORARY TABLE IF EXISTS async_test");
27+
$result = $mysqli->query("CREATE TEMPORARY TABLE async_test (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50), value INT)");
28+
29+
if (!$result) {
30+
echo "create table failed: " . $mysqli->error . "\n";
31+
return "failed";
32+
}
33+
echo "table created\n";
34+
35+
// Insert test data
36+
$mysqli->query("INSERT INTO async_test (name, value) VALUES ('test1', 10)");
37+
$mysqli->query("INSERT INTO async_test (name, value) VALUES ('test2', 20)");
38+
$mysqli->query("INSERT INTO async_test (name, value) VALUES ('test3', 30)");
39+
echo "data inserted\n";
40+
41+
// Test SELECT query
42+
$result = $mysqli->query("SELECT * FROM async_test ORDER BY id");
43+
if ($result) {
44+
$count = 0;
45+
while ($row = $result->fetch_assoc()) {
46+
$count++;
47+
echo "row $count: {$row['name']} = {$row['value']}\n";
48+
}
49+
$result->free();
50+
}
51+
52+
// Test aggregate query
53+
$result = $mysqli->query("SELECT COUNT(*) as total, SUM(value) as sum_value FROM async_test");
54+
if ($result) {
55+
$row = $result->fetch_assoc();
56+
echo "total rows: {$row['total']}, sum: {$row['sum_value']}\n";
57+
$result->free();
58+
}
59+
60+
$mysqli->close();
61+
return "completed";
62+
} catch (Exception $e) {
63+
echo "error: " . $e->getMessage() . "\n";
64+
return "failed";
65+
}
66+
});
67+
68+
$result = await($coroutine);
69+
echo "result: " . $result . "\n";
70+
echo "end\n";
71+
72+
?>
73+
--EXPECT--
74+
start
75+
table created
76+
data inserted
77+
row 1: test1 = 10
78+
row 2: test2 = 20
79+
row 3: test3 = 30
80+
total rows: 3, sum: 60
81+
result: completed
82+
end
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
--TEST--
2+
MySQLi: Concurrent connections in separate coroutines
3+
--EXTENSIONS--
4+
mysqli
5+
--SKIPIF--
6+
<?php
7+
require_once __DIR__ . '/inc/async_mysqli_test.inc';
8+
AsyncMySQLiTest::skipIfNoAsync();
9+
AsyncMySQLiTest::skipIfNoMySQLi();
10+
AsyncMySQLiTest::skip();
11+
?>
12+
--FILE--
13+
<?php
14+
require_once __DIR__ . '/inc/async_mysqli_test.inc';
15+
16+
use function Async\spawn;
17+
use function Async\await;
18+
use function Async\awaitAllOrFail;
19+
20+
echo "start\n";
21+
22+
// Create multiple coroutines with separate MySQLi connections
23+
$coroutines = [
24+
spawn(function() {
25+
$mysqli = AsyncMySQLiTest::factory();
26+
$result = $mysqli->query("SELECT 'coroutine1' as source, CONNECTION_ID() as conn_id");
27+
$row = $result->fetch_assoc();
28+
$result->free();
29+
$mysqli->close();
30+
return ['source' => $row['source'], 'conn_id' => $row['conn_id']];
31+
}),
32+
33+
spawn(function() {
34+
$mysqli = AsyncMySQLiTest::factory();
35+
$result = $mysqli->query("SELECT 'coroutine2' as source, CONNECTION_ID() as conn_id");
36+
$row = $result->fetch_assoc();
37+
$result->free();
38+
$mysqli->close();
39+
return ['source' => $row['source'], 'conn_id' => $row['conn_id']];
40+
}),
41+
42+
spawn(function() {
43+
$mysqli = AsyncMySQLiTest::factory();
44+
$result = $mysqli->query("SELECT 'coroutine3' as source, CONNECTION_ID() as conn_id");
45+
$row = $result->fetch_assoc();
46+
$result->free();
47+
$mysqli->close();
48+
return ['source' => $row['source'], 'conn_id' => $row['conn_id']];
49+
}),
50+
51+
spawn(function() {
52+
$mysqli = AsyncMySQLiTest::factory();
53+
// Test with some workload
54+
$mysqli->query("CREATE TEMPORARY TABLE temp_work (id INT, data VARCHAR(100))");
55+
$mysqli->query("INSERT INTO temp_work VALUES (1, 'data1'), (2, 'data2')");
56+
$result = $mysqli->query("SELECT COUNT(*) as count, CONNECTION_ID() as conn_id FROM temp_work");
57+
$row = $result->fetch_assoc();
58+
$result->free();
59+
$mysqli->close();
60+
return ['source' => 'coroutine4', 'conn_id' => $row['conn_id'], 'count' => $row['count']];
61+
})
62+
];
63+
64+
$results = awaitAllOrFail($coroutines);
65+
66+
// Display results in deterministic order
67+
usort($results, function($a, $b) {
68+
return strcmp($a['source'], $b['source']);
69+
});
70+
71+
foreach ($results as $result) {
72+
if (isset($result['count'])) {
73+
echo "from {$result['source']} (with work) conn_id: {$result['conn_id']}, count: {$result['count']}\n";
74+
} else {
75+
echo "from {$result['source']} conn_id: {$result['conn_id']}\n";
76+
}
77+
}
78+
79+
// Verify all connections are different
80+
$connectionIds = array_map(function($r) { return $r['conn_id']; }, $results);
81+
$uniqueIds = array_unique($connectionIds);
82+
echo "unique connections: " . count($uniqueIds) . "\n";
83+
echo "total coroutines: " . count($connectionIds) . "\n";
84+
85+
if (count($uniqueIds) === count($connectionIds)) {
86+
echo "isolation: passed\n";
87+
} else {
88+
echo "isolation: failed\n";
89+
}
90+
91+
echo "end\n";
92+
93+
?>
94+
--EXPECTF--
95+
start
96+
from coroutine1 conn_id: %d
97+
from coroutine2 conn_id: %d
98+
from coroutine3 conn_id: %d
99+
from coroutine4 (with work) conn_id: %d, count: 2
100+
unique connections: 4
101+
total coroutines: 4
102+
isolation: passed
103+
end
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
--TEST--
2+
MySQLi: Async prepared statements
3+
--EXTENSIONS--
4+
mysqli
5+
--SKIPIF--
6+
<?php
7+
require_once __DIR__ . '/inc/async_mysqli_test.inc';
8+
AsyncMySQLiTest::skipIfNoAsync();
9+
AsyncMySQLiTest::skipIfNoMySQLi();
10+
AsyncMySQLiTest::skip();
11+
?>
12+
--FILE--
13+
<?php
14+
require_once __DIR__ . '/inc/async_mysqli_test.inc';
15+
16+
use function Async\spawn;
17+
use function Async\await;
18+
19+
echo "start\n";
20+
21+
$coroutine = spawn(function() {
22+
try {
23+
$mysqli = AsyncMySQLiTest::factory();
24+
25+
// Create test table
26+
$mysqli->query("DROP TEMPORARY TABLE IF EXISTS async_prepared_test");
27+
$mysqli->query("CREATE TEMPORARY TABLE async_prepared_test (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50), score INT, active BOOLEAN)");
28+
echo "table created\n";
29+
30+
// Test INSERT prepared statement
31+
$stmt = $mysqli->prepare("INSERT INTO async_prepared_test (name, score, active) VALUES (?, ?, ?)");
32+
if (!$stmt) {
33+
throw new Exception("Prepare failed: " . $mysqli->error);
34+
}
35+
36+
// Insert multiple records
37+
$name = "user1"; $score = 85; $active = true;
38+
$stmt->bind_param("sii", $name, $score, $active);
39+
$stmt->execute();
40+
41+
$name = "user2"; $score = 92; $active = false;
42+
$stmt->bind_param("sii", $name, $score, $active);
43+
$stmt->execute();
44+
45+
$name = "user3"; $score = 78; $active = true;
46+
$stmt->bind_param("sii", $name, $score, $active);
47+
$stmt->execute();
48+
49+
$stmt->close();
50+
echo "inserted records with prepared statement\n";
51+
52+
// Test SELECT prepared statement
53+
$stmt = $mysqli->prepare("SELECT id, name, score FROM async_prepared_test WHERE score > ? AND active = ? ORDER BY id");
54+
if (!$stmt) {
55+
throw new Exception("Prepare SELECT failed: " . $mysqli->error);
56+
}
57+
58+
$min_score = 80;
59+
$is_active = true;
60+
$stmt->bind_param("ii", $min_score, $is_active);
61+
$stmt->execute();
62+
63+
$result = $stmt->get_result();
64+
echo "records with score > $min_score and active = $is_active:\n";
65+
66+
while ($row = $result->fetch_assoc()) {
67+
echo " id: {$row['id']}, name: {$row['name']}, score: {$row['score']}\n";
68+
}
69+
70+
$stmt->close();
71+
72+
// Test UPDATE prepared statement
73+
$stmt = $mysqli->prepare("UPDATE async_prepared_test SET score = score + ? WHERE name = ?");
74+
if (!$stmt) {
75+
throw new Exception("Prepare UPDATE failed: " . $mysqli->error);
76+
}
77+
78+
$bonus = 5;
79+
$target_name = "user1";
80+
$stmt->bind_param("is", $bonus, $target_name);
81+
$stmt->execute();
82+
83+
echo "updated $target_name with bonus $bonus points\n";
84+
echo "affected rows: " . $stmt->affected_rows . "\n";
85+
86+
$stmt->close();
87+
88+
// Verify update
89+
$result = $mysqli->query("SELECT name, score FROM async_prepared_test WHERE name = 'user1'");
90+
$row = $result->fetch_assoc();
91+
echo "user1 new score: {$row['score']}\n";
92+
$result->free();
93+
94+
$mysqli->close();
95+
return "completed";
96+
} catch (Exception $e) {
97+
echo "error: " . $e->getMessage() . "\n";
98+
return "failed";
99+
}
100+
});
101+
102+
$result = await($coroutine);
103+
echo "result: " . $result . "\n";
104+
echo "end\n";
105+
106+
?>
107+
--EXPECT--
108+
start
109+
table created
110+
inserted records with prepared statement
111+
records with score > 80 and active = 1:
112+
id: 1, name: user1, score: 85
113+
updated user1 with bonus 5 points
114+
affected rows: 1
115+
user1 new score: 90
116+
result: completed
117+
end

0 commit comments

Comments
 (0)