Skip to content

Commit 13a933b

Browse files
committed
Do not inline current time retrieval to be mockable
1 parent dc55bf1 commit 13a933b

File tree

5 files changed

+91
-78
lines changed

5 files changed

+91
-78
lines changed

Zend/zend_time.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| Copyright (c) The PHP Group |
4+
+----------------------------------------------------------------------+
5+
| This source file is subject to version 3.01 of the PHP license, |
6+
| that is bundled with this package in the file LICENSE, and is |
7+
| available through the world-wide-web at the following url: |
8+
| https://www.php.net/license/3_01.txt |
9+
| If you did not receive a copy of the PHP license and are unable to |
10+
| obtain it through the world-wide-web, please send a note to |
11+
| license@php.net so we can mail you a copy immediately. |
12+
+----------------------------------------------------------------------+
13+
| Author: Marc Bennewitz <marc@mabe.berlin> |
14+
+----------------------------------------------------------------------+
15+
*/
16+
17+
#include "zend_time.h"
18+
19+
/* Current real/wall-time in seconds */
20+
ZEND_API time_t zend_time_real_get(void) {
21+
return time(NULL);
22+
}
23+
24+
ZEND_API void zend_time_real_spec(struct timespec *ts) {
25+
#if defined(HAVE_CLOCK_GETTIME)
26+
27+
(void) clock_gettime(CLOCK_REALTIME, ts);
28+
29+
#elif defined(HAVE_TIMESPEC_GET)
30+
31+
(void) timespec_get(ts, TIME_UTC);
32+
33+
#elif defined(HAVE_GETTIMEOFDAY)
34+
35+
struct timeval tv;
36+
(void) gettimeofday(&tv, NULL);
37+
zend_time_val2spec(tv, ts);
38+
39+
#else
40+
41+
ts->tv_sec = zend_time_real_get();
42+
ts->tv_nsec = 0;
43+
44+
#endif
45+
}
46+
47+
ZEND_API uint64_t zend_time_mono_fallback(void) {
48+
#if ZEND_HRTIME_AVAILABLE
49+
return (uint64_t)zend_hrtime();
50+
#else
51+
struct timespec ts;
52+
zend_time_real_spec(&ts);
53+
return ((uint64_t) ts.tv_sec * ZEND_NANO_IN_SEC) + ts.tv_nsec;
54+
#endif
55+
}

Zend/zend_time.h

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -80,46 +80,15 @@ static zend_always_inline void zend_time_val2spec(struct timeval tv, struct time
8080
ts->tv_nsec = (long) (tv.tv_usec * 1000);
8181
}
8282

83-
/* Get current timestamp in seconds */
84-
static zend_always_inline time_t zend_time_real_get(void) {
85-
return time(NULL);
86-
}
87-
88-
/* wrapper around clock_gettime/timestamp_get/gettimeofday/time */
89-
static zend_always_inline void zend_time_real_spec(struct timespec *ts) {
90-
#if defined(HAVE_CLOCK_GETTIME)
91-
92-
(void) clock_gettime(CLOCK_REALTIME, ts);
93-
94-
#elif defined(HAVE_TIMESPEC_GET)
95-
96-
(void) timespec_get(ts, TIME_UTC);
97-
98-
#elif defined(HAVE_GETTIMEOFDAY)
83+
/* Current real/wall-time in seconds */
84+
ZEND_API time_t zend_time_real_get(void);
9985

100-
struct timeval tv;
101-
(void) gettimeofday(&tv, NULL);
102-
zend_time_val2spec(tv, ts);
103-
104-
#else
105-
106-
ts->tv_sec = zend_time_real_get();
107-
ts->tv_nsec = 0;
108-
109-
#endif
110-
}
86+
/* Current real/wall-time in up-to nano seconds */
87+
ZEND_API void zend_time_real_spec(struct timespec *ts);
11188

11289
/* Monotonic time in nanoseconds with a fallback to real/wall-time
11390
if no monotonic timer is available */
114-
static zend_always_inline uint64_t zend_time_mono_fallback(void) {
115-
#if ZEND_HRTIME_AVAILABLE
116-
return (uint64_t)zend_hrtime();
117-
#else
118-
struct timespec ts;
119-
zend_time_real_spec(&ts);
120-
return ((uint64_t) ts.tv_sec * ZEND_NANO_IN_SEC) + ts.tv_nsec;
121-
#endif
122-
}
91+
ZEND_API uint64_t zend_time_mono_fallback(void);
12392

12493
END_EXTERN_C()
12594

configure.ac

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1761,6 +1761,7 @@ PHP_ADD_SOURCES([Zend], m4_normalize([
17611761
zend_generators.c
17621762
zend_hash.c
17631763
zend_highlight.c
1764+
zend_time.c
17641765
zend_hrtime.c
17651766
zend_inheritance.c
17661767
zend_ini_parser.c

tests/unit/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ COMMON_LDFLAGS = ../../.libs/libphp.a -lcmocka -lpthread -lm -ldl -lresolv -luti
55
# Update paths in .github/workflows/unit-tests.yml when adding new test to make it run in PR when such file changes
66
TESTS = main/test_network
77
main/test_network_SRC = main/test_network.c
8-
main/test_network_LDFLAGS = $(COMMON_LDFLAGS) -Wl,--wrap=connect,--wrap=poll,--wrap=getsockopt,--wrap=gettimeofday
8+
main/test_network_LDFLAGS = $(COMMON_LDFLAGS) -Wl,--wrap=connect,--wrap=poll,--wrap=getsockopt,--wrap=zend_time_mono_fallback
99

1010

1111
# Build all tests

tests/unit/main/test_network.c

Lines changed: 29 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,11 @@ int __wrap_getsockopt(int fd, int level, int optname, void *optval, socklen_t *o
3636
return mock_type(int);
3737
}
3838

39-
// Mocked gettimeofday
40-
int __wrap_gettimeofday(struct timeval *time_Info, struct timezone *timezone_Info)
39+
// Mocked zend_time_mono_fallback
40+
uint64_t __wrap_zend_time_mono_fallback(void)
4141
{
4242
function_called();
43-
struct timeval *now = mock_ptr_type(struct timeval *);
44-
if (now) {
45-
time_Info->tv_sec = now->tv_sec;
46-
time_Info->tv_usec = now->tv_usec;
47-
}
48-
return mock_type(int);
43+
return mock_type(uint64_t);
4944
}
5045

5146
// Test successful connection
@@ -74,9 +69,8 @@ static void test_php_network_connect_socket_progress_success(void **state) {
7469
will_return(__wrap_connect, EINPROGRESS);
7570

7671
// Mock time setting - ignored
77-
expect_function_call(__wrap_gettimeofday);
78-
will_return(__wrap_gettimeofday, NULL);
79-
will_return(__wrap_gettimeofday, 0);
72+
expect_function_call(__wrap_zend_time_mono_fallback);
73+
will_return(__wrap_zend_time_mono_fallback, 0);
8074

8175
// Mock poll to return success
8276
expect_function_call(__wrap_poll);
@@ -96,29 +90,27 @@ static void test_php_network_connect_socket_progress_success(void **state) {
9690

9791
static void test_php_network_connect_socket_eintr_t1(void **state) {
9892
struct timeval timeout_tv = { .tv_sec = 2, .tv_usec = 500000 };
99-
struct timeval start_time = { .tv_sec = 1000, .tv_usec = 0 }; // Initial time
100-
struct timeval retry_time = { .tv_sec = 1001, .tv_usec = 200000 }; // Time after EINTR
93+
uint64_t start_time = 1000000000000; // Initial time
94+
uint64_t retry_time = 1001200000000; // Time after EINTR
10195
php_socket_t sockfd = 12;
10296
int error_code = 0;
10397

10498
// Mock connect to set EINPROGRESS
10599
expect_function_call(__wrap_connect);
106100
will_return(__wrap_connect, EINPROGRESS);
107101

108-
// Mock gettimeofday for initial call
109-
expect_function_call(__wrap_gettimeofday);
110-
will_return(__wrap_gettimeofday, &start_time);
111-
will_return(__wrap_gettimeofday, 0);
102+
// Mock zend_time_mono_fallback for initial call
103+
expect_function_call(__wrap_zend_time_mono_fallback);
104+
will_return(__wrap_zend_time_mono_fallback, start_time);
112105

113106
// Mock poll to return EINTR first
114107
expect_function_call(__wrap_poll);
115108
expect_value(__wrap_poll, timeout, 2500);
116109
will_return(__wrap_poll, -EINTR);
117110

118-
// Mock gettimeofday after EINTR
119-
expect_function_call(__wrap_gettimeofday);
120-
will_return(__wrap_gettimeofday, &retry_time);
121-
will_return(__wrap_gettimeofday, 0);
111+
// Mock zend_time_mono_fallback after EINTR
112+
expect_function_call(__wrap_zend_time_mono_fallback);
113+
will_return(__wrap_zend_time_mono_fallback, retry_time);
122114

123115
// Mock poll to succeed on retry
124116
expect_function_call(__wrap_poll);
@@ -139,29 +131,27 @@ static void test_php_network_connect_socket_eintr_t1(void **state) {
139131

140132
static void test_php_network_connect_socket_eintr_t2(void **state) {
141133
struct timeval timeout_tv = { .tv_sec = 2, .tv_usec = 1500000 };
142-
struct timeval start_time = { .tv_sec = 1000, .tv_usec = 300000 }; // Initial time
143-
struct timeval retry_time = { .tv_sec = 1001, .tv_usec = 200000 }; // Time after EINTR
134+
uint64_t start_time = 1000300000000; // Initial time
135+
uint64_t retry_time = 1001200000000; // Time after EINTR
144136
php_socket_t sockfd = 12;
145137
int error_code = 0;
146138

147139
// Mock connect to set EINPROGRESS
148140
expect_function_call(__wrap_connect);
149141
will_return(__wrap_connect, EINPROGRESS);
150142

151-
// Mock gettimeofday for initial call
152-
expect_function_call(__wrap_gettimeofday);
153-
will_return(__wrap_gettimeofday, &start_time);
154-
will_return(__wrap_gettimeofday, 0);
143+
// Mock zend_time_mono_fallback for initial call
144+
expect_function_call(__wrap_zend_time_mono_fallback);
145+
will_return(__wrap_zend_time_mono_fallback, start_time);
155146

156147
// Mock poll to return EINTR first
157148
expect_function_call(__wrap_poll);
158149
expect_value(__wrap_poll, timeout, 3500);
159150
will_return(__wrap_poll, -EINTR);
160151

161-
// Mock gettimeofday after EINTR
162-
expect_function_call(__wrap_gettimeofday);
163-
will_return(__wrap_gettimeofday, &retry_time);
164-
will_return(__wrap_gettimeofday, 0);
152+
// Mock zend_time_mono_fallback after EINTR
153+
expect_function_call(__wrap_zend_time_mono_fallback);
154+
will_return(__wrap_zend_time_mono_fallback, retry_time);
165155

166156
// Mock poll to succeed on retry
167157
expect_function_call(__wrap_poll);
@@ -182,29 +172,27 @@ static void test_php_network_connect_socket_eintr_t2(void **state) {
182172

183173
static void test_php_network_connect_socket_eintr_t3(void **state) {
184174
struct timeval timeout_tv = { .tv_sec = 2, .tv_usec = 500000 };
185-
struct timeval start_time = { .tv_sec = 1002, .tv_usec = 300000 }; // Initial time
186-
struct timeval retry_time = { .tv_sec = 1001, .tv_usec = 2200000 }; // Time after EINTR
175+
uint64_t start_time = 1002300000000; // Initial time
176+
uint64_t retry_time = 1003200000000; // Time after EINTR
187177
php_socket_t sockfd = 12;
188178
int error_code = 0;
189179

190180
// Mock connect to set EINPROGRESS
191181
expect_function_call(__wrap_connect);
192182
will_return(__wrap_connect, EINPROGRESS);
193183

194-
// Mock gettimeofday for initial call
195-
expect_function_call(__wrap_gettimeofday);
196-
will_return(__wrap_gettimeofday, &start_time);
197-
will_return(__wrap_gettimeofday, 0);
184+
// Mock zend_time_mono_fallback for initial call
185+
expect_function_call(__wrap_zend_time_mono_fallback);
186+
will_return(__wrap_zend_time_mono_fallback, start_time);
198187

199188
// Mock poll to return EINTR first
200189
expect_function_call(__wrap_poll);
201190
expect_value(__wrap_poll, timeout, 2500);
202191
will_return(__wrap_poll, -EINTR);
203192

204-
// Mock gettimeofday after EINTR
205-
expect_function_call(__wrap_gettimeofday);
206-
will_return(__wrap_gettimeofday, &retry_time);
207-
will_return(__wrap_gettimeofday, 0);
193+
// Mock zend_time_mono_fallback after EINTR
194+
expect_function_call(__wrap_zend_time_mono_fallback);
195+
will_return(__wrap_zend_time_mono_fallback, retry_time);
208196

209197
// Mock poll to succeed on retry
210198
expect_function_call(__wrap_poll);

0 commit comments

Comments
 (0)