1 /*
2  * Test winmm timer
3  *
4  * Copyright (c) 2005 Robert Reif
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include <stdarg.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <math.h>
25 
26 #include "wine/test.h"
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winnls.h"
30 #include "mmsystem.h"
31 #define NOBITMAP
32 #include "mmreg.h"
33 
34 #include "winmm_test.h"
35 
36 static TIMECAPS tc;
37 
38 static void test_timeGetDevCaps(void)
39 {
40    MMRESULT rc;
41 
42     rc = timeGetDevCaps(&tc, 0);
43     ok(rc == TIMERR_NOCANDO || rc == MMSYSERR_INVALPARAM,
44        "timeGetDevCaps() returned %s, should have returned TIMERR_NOCANDO "
45        "or MMSYSERR_INVALPARAM\n", mmsys_error(rc));
46 
47     rc = timeGetDevCaps(0, sizeof(tc));
48     ok(rc == TIMERR_NOCANDO || rc == TIMERR_STRUCT,
49        "timeGetDevCaps() returned %s, should have returned TIMERR_NOCANDO "
50        "or TIMERR_STRUCT\n", mmsys_error(rc));
51 
52     rc = timeGetDevCaps(0, 0);
53     ok(rc == TIMERR_NOCANDO || rc == MMSYSERR_INVALPARAM,
54        "timeGetDevCaps() returned %s, should have returned TIMERR_NOCANDO "
55        "or MMSYSERR_INVALPARAM\n", mmsys_error(rc));
56 
57     rc = timeGetDevCaps(&tc, sizeof(tc));
58     ok(rc == TIMERR_NOERROR, "timeGetDevCaps() returned %s, "
59        "should have returned TIMERR_NOERROR\n", mmsys_error(rc));
60 
61     if (rc == TIMERR_NOERROR)
62         trace("wPeriodMin = %u, wPeriodMax = %u\n",
63               tc.wPeriodMin, tc.wPeriodMax);
64 }
65 
66 #define NUM_SAMPLES    100
67 
68 static DWORD count = 0;
69 static DWORD times[NUM_SAMPLES];
70 
71 static void CALLBACK testTimeProc(UINT uID, UINT uMsg, DWORD_PTR dwUser,
72                                   DWORD_PTR dw1, DWORD_PTR dw2)
73 {
74     if (count < NUM_SAMPLES)
75         times[count++] = timeGetTime();
76 }
77 
78 static void test_timer(UINT period, UINT resolution)
79 {
80     MMRESULT rc;
81     UINT i, id, delta;
82     DWORD dwMin = 0xffffffff, dwMax = 0;
83     double sum = 0.0;
84     double deviation = 0.0;
85 
86     count = 0;
87 
88     for (i = 0; i < NUM_SAMPLES; i++)
89         times[i] = 0;
90 
91     rc = timeBeginPeriod(period);
92     ok(rc == TIMERR_NOERROR, "timeBeginPeriod(%u) returned %s, "
93        "should have returned TIMERR_NOERROR\n", period, mmsys_error(rc));
94     if (rc != TIMERR_NOERROR)
95         return;
96 
97     id = timeSetEvent(period, resolution, testTimeProc, 0, TIME_PERIODIC);
98     ok(id != 0, "timeSetEvent(%u, %u, %p, 0, TIME_PERIODIC) returned %d, "
99        "should have returned id > 0\n", period, resolution, testTimeProc, id);
100     if (id == 0)
101         return;
102 
103     Sleep((NUM_SAMPLES * period) + (2 * period));
104 
105     rc = timeEndPeriod(period);
106     ok(rc == TIMERR_NOERROR, "timeEndPeriod(%u) returned %s, "
107        "should have returned TIMERR_NOERROR\n", period, mmsys_error(rc));
108     if (rc != TIMERR_NOERROR)
109         return;
110 
111     rc = timeKillEvent(id);
112     ok(rc == TIMERR_NOERROR, "timeKillEvent(%u) returned %s, "
113        "should have returned TIMERR_NOERROR\n", id, mmsys_error(rc));
114 
115     trace("period = %u, resolution = %u\n", period, resolution);
116 
117     for (i = 0; i < count; i++)
118     {
119         if (i == 0)
120         {
121             if (winetest_debug > 1)
122                 trace("time[%d] = %u\n", i, times[i]);
123         }
124         else
125         {
126             delta = times[i] - times[i - 1];
127 
128             if (winetest_debug > 1)
129                 trace("time[%d] = %u delta = %d\n", i, times[i], delta);
130 
131             sum += delta;
132             deviation += ((delta - period) * (delta - period));
133 
134             if (delta < dwMin)
135                 dwMin = delta;
136 
137             if (delta > dwMax)
138                 dwMax = delta;
139         }
140     }
141 
142     trace("min = %u, max = %u, average = %f, standard deviation = %f\n",
143           dwMin, dwMax, sum / (count - 1), sqrt(deviation / (count - 2)));
144 }
145 
146 static const char * get_priority(int priority)
147 {
148     static char     tmp[32];
149 #define STR(x) case x: return #x
150     switch(priority) {
151     STR(THREAD_PRIORITY_LOWEST);
152     STR(THREAD_PRIORITY_BELOW_NORMAL);
153     STR(THREAD_PRIORITY_NORMAL);
154     STR(THREAD_PRIORITY_HIGHEST);
155     STR(THREAD_PRIORITY_ABOVE_NORMAL);
156     STR(THREAD_PRIORITY_TIME_CRITICAL);
157     STR(THREAD_PRIORITY_IDLE);
158     }
159     sprintf(tmp, "UNKNOWN(%d)", priority);
160     return tmp;
161 }
162 
163 static int priority = 0;
164 static BOOL fired = FALSE;
165 
166 static void CALLBACK priorityTimeProc(UINT uID, UINT uMsg, DWORD_PTR dwUser,
167                                       DWORD_PTR dw1, DWORD_PTR dw2)
168 {
169     priority = GetThreadPriority(GetCurrentThread());
170     ok(priority!=THREAD_PRIORITY_ERROR_RETURN, "GetThreadPriority() failed, GetLastError() = %u\n", GetLastError());
171     fired = TRUE;
172 }
173 
174 static void test_priority(void)
175 {
176     UINT id;
177 
178     id = timeSetEvent(100, 100, priorityTimeProc, 0, TIME_ONESHOT);
179     ok(id != 0, "timeSetEvent(100, 100, %p, 0, TIME_ONESHOT) returned %d, "
180        "should have returned id > 0\n", priorityTimeProc, id);
181     if (id == 0)
182         return;
183 
184     Sleep(200);
185 
186     ok(fired == TRUE, "Callback not called\n");
187     if (fired)
188     {
189         ok(priority == THREAD_PRIORITY_TIME_CRITICAL,
190            "thread priority is %s, should be THREAD_PRIORITY_TIME_CRITICAL\n",
191            get_priority(priority));
192     }
193     timeKillEvent(id);
194 }
195 
196 START_TEST(timer)
197 {
198     test_timeGetDevCaps();
199 
200     if (tc.wPeriodMin <= 1) {
201         test_timer(1, 0);
202         test_timer(1, 1);
203     }
204 
205     if (tc.wPeriodMin <= 10) {
206         test_timer(10, 0);
207         test_timer(10, 1);
208         test_timer(10, 10);
209     }
210 
211     if (tc.wPeriodMin <= 20) {
212         test_timer(20, 0);
213         test_timer(20, 1);
214         test_timer(20, 10);
215         test_timer(20, 20);
216     }
217 
218     test_priority();
219 }
220