Merge lp:~compiz-team/compiz/compiz.fix_1085591 into lp:compiz/0.9.9
- compiz.fix_1085591
- Merge into 0.9.9
Status: | Merged |
---|---|
Approved by: | Daniel van Vugt |
Approved revision: | 3513 |
Merged at revision: | 3504 |
Proposed branch: | lp:~compiz-team/compiz/compiz.fix_1085591 |
Merge into: | lp:compiz/0.9.9 |
Diff against target: |
1794 lines (+1206/-185) 21 files modified
compizconfig/libcompizconfig/tests/compizconfig_test_ccs_util.cpp (+1/-29) include/core/screen.h (+1/-0) src/global.cpp (+1/-0) src/main.cpp (+4/-0) src/screen.cpp (+165/-98) tests/CMakeLists.txt (+2/-0) tests/acceptance-tests/CMakeLists.txt (+1/-0) tests/acceptance-tests/xorg-gtest/CMakeLists.txt (+11/-0) tests/acceptance-tests/xorg-gtest/tests/CMakeLists.txt (+21/-0) tests/acceptance-tests/xorg-gtest/tests/README (+3/-0) tests/acceptance-tests/xorg-gtest/tests/compiz_acceptance_replace_current_wm.cpp (+255/-0) tests/shared/CMakeLists.txt (+2/-0) tests/shared/gtest_shared_asynctask.h (+53/-0) tests/shared/gtest_shared_tmpenv.h (+56/-0) tests/shared/src/CMakeLists.txt (+7/-0) tests/shared/src/gtest_shared_asynctask.cpp (+138/-0) tests/system/xorg-gtest/include/compiz-xorg-gtest.h (+40/-3) tests/system/xorg-gtest/src/compiz-xorg-gtest.cpp (+199/-20) tests/system/xorg-gtest/tests/CMakeLists.txt (+23/-9) tests/system/xorg-gtest/tests/compiz_xorg_gtest_icccm.cpp (+136/-0) tests/system/xorg-gtest/tests/compiz_xorg_gtest_test_window_stacking.cpp (+87/-26) |
To merge this branch: | bzr merge lp:~compiz-team/compiz/compiz.fix_1085591 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Daniel van Vugt | Approve | ||
Review via email: mp+138057@code.launchpad.net |
This proposal supersedes a proposal from 2012-12-03.
Commit message
Fix race condition causing --replace to fail occasionally.
Clear XErrors right before taking SubstructureRed
This changes the startup order a fair bit. The new order is now:
1. Do non-critical initialization that does not require a server grab
2. Do the ICCCM check to shut down the other window manager
3. Take server grab
4. Create edge windows
5. Clear error buffer
6. Attempt to take SubstructureRed
7. Clear error buffer -> if fail, exit
8. Create handles based on XQueryTree
9. Release server grab
(LP: #1085591)
System test added to demonstrate that compiz should exit when another client has SubstructureRed
In order to make this work properly, some changes had to be made to compiz. This adds a new runtime switch --send-
Also fixed race condition caused by destroying the WMSnSelectionWindow before shutdown.
Because we haven't changed our active event mask
to remove SubstructureRed
compliant window managers may receive a DestroyNotify
(eg, because we're blocked on XSync) before we get
a chance to close our display connection and remove
our SubstructureRed
to fail to start.
The selection window is destroyed anyways when we
close our connection, and that is a very accurate
indicator to other WM's that we are well and truly
gone because the protocol requires the implementation
to remove all client event masks before destroying
windows.
Added acceptance-tests for testing that behaviour. They are not added to the default test target for obvious reasons.
Description of the change
Fix race condition causing --replace to fail occasionally.
Clear XErrors right before taking SubstructureRed
This changes the startup order a fair bit. The new order is now:
1. Do non-critical initialization that does not require a server grab
2. Do the ICCCM check to shut down the other window manager
3. Take server grab
4. Create edge windows
5. Clear error buffer
6. Attempt to take SubstructureRed
7. Clear error buffer -> if fail, exit
8. Create handles based on XQueryTree
9. Release server grab
System test added to demonstrate that compiz should exit when another client has SubstructureRed
In order to make this work properly, some changes had to be made to compiz. This adds a new runtime switch --send-
(LP: #1085591)
Also fixed race condition caused by destroying the WMSnSelectionWindow before shutdown.
Because we haven't changed our active event mask
to remove SubstructureRed
compliant window managers may receive a DestroyNotify
(eg, because we're blocked on XSync) before we get
a chance to close our display connection and remove
our SubstructureRed
to fail to start.
The selection window is destroyed anyways when we
close our connection, and that is a very accurate
indicator to other WM's that we are well and truly
gone because the protocol requires the implementation
to remove all client event masks before destroying
windows.
Added acceptance-tests for testing that behaviour. They are not added to the default test target for obvious reasons.
Please throw this against the wall to see if any oddities have been introduced - they sometimes happen when you change the startup order as there is coupling between side effects.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:3502
http://
Executed test runs:
SUCCESS: http://
Click here to trigger a rebuild:
http://
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal | # |
I still get bug 1085591 even with this branch. Seems like nothing is fixed.
1. Start compiz
2. Start another compiz --replace
Expected: Compiz is replaced
Observed: The old compiz is killed but the new one fails to start with:
compiz (core) - Error: Another window manager is already running on screen: 0
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal | # |
Hmm.
Well in any case this does fix the problematic nature of the code. We are
meant to check for errors upon taking SubstructureRed
don't do that properly.
I'm suprised its still not working for you, it definitely fixed the problem
here.
On Dec 4, 2012 11:44 AM, "Daniel van Vugt" <email address hidden>
wrote:
> Review: Needs Fixing
>
> I still get bug 1085591 even with this branch. Seems like nothing is fixed.
>
> 1. Start compiz
> 2. Start another compiz --replace
> Expected: Compiz is replaced
> Observed: The old compiz is killed but the new one fails to start with:
> compiz (core) - Error: Another window manager is already running on
> screen: 0
> --
>
> https:/
> Your team Compiz Maintainers is subscribed to branch lp:compiz.
>
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal | # |
Oh, could you get some more output for me?
Change the DEBUG #define to be 1 and run compiz --debug.
That will print out X errors as they occurr.
On Dec 4, 2012 12:32 PM, "Sam Spilsbury" <email address hidden> wrote:
> Hmm.
>
> Well in any case this does fix the problematic nature of the code. We are
> meant to check for errors upon taking SubstructureRed
> don't do that properly.
>
> I'm suprised its still not working for you, it definitely fixed the
> problem here.
> On Dec 4, 2012 11:44 AM, "Daniel van Vugt" <email address hidden>
> wrote:
>
>> Review: Needs Fixing
>>
>> I still get bug 1085591 even with this branch. Seems like nothing is
>> fixed.
>>
>> 1. Start compiz
>> 2. Start another compiz --replace
>> Expected: Compiz is replaced
>> Observed: The old compiz is killed but the new one fails to start with:
>> compiz (core) - Error: Another window manager is already running on
>> screen: 0
>> --
>>
>> https:/
>> Your team Compiz Maintainers is subscribed to branch lp:compiz.
>>
>
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal | # |
Debug mode says...
./bin/compiz (core) - Info: Loading plugin: core
./bin/compiz (core) - Debug: Trying to load core from: /home/dan/
./bin/compiz (core) - Debug: dlopen failed: /home/dan/
./bin/compiz (core) - Debug: Trying to load core from: /home/dan/
./bin/compiz (core) - Debug: dlopen failed: /home/dan/
./bin/compiz (core) - Info: Starting plugin: core
./bin/compiz (core) - Debug: Started plugin: core
X Error of failed request: BadWindow (invalid Window parameter)
Major opcode of failed request: 34 (X_UngrabKey)
Minor opcode of failed request: 0
Resource id in failed request: 0x0
X Error of failed request: BadAccess (attempt to access private resource denied)
Major opcode of failed request: 2 (X_ChangeWindow
Minor opcode of failed request: 0
Resource id in failed request: 0xe1
X Error of failed request: BadAccess (attempt to access private resource denied)
Major opcode of failed request: 33 (X_GrabKey)
Minor opcode of failed request: 0
Resource id in failed request: 0xe1
... above error repeats several times ...
X Error of failed request: BadAccess (attempt to access private resource denied)
Major opcode of failed request: 2 (X_ChangeWindow
Minor opcode of failed request: 0
Resource id in failed request: 0xe1
./bin/compiz (core) - Error: Another window manager is already running on screen: 0
./bin/compiz (core) - Info: Stopping plugin: core
./bin/compiz (core) - Debug: Stopped plugin: core
./bin/compiz (core) - Info: Unloading plugin: core
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal | # |
OK, thanks for the output.
I've fixed another race condition and added some acceptance tests to verify it (they are quite flakey due to being time-based so they aren't added to the test target by default)
Daniel van Vugt (vanvugt) wrote : | # |
Seems to work now. Bug fixed.
Just a note on pthread creation in AsyncTask:
It's maybe not a realistic example but there are other similar reasons why touching things outside your class should be left till after construction.
Sam Spilsbury (smspillaz) wrote : | # |
> Seems to work now. Bug fixed.
>
> Just a note on pthread creation in AsyncTask:
> idea to create or touch anything outside of your own class in a constructor.
> Say for example, someone uses your class and decides they need a vector<> of
> them. Every time the vector reallocates, every element gets re-copy-
> constructed and destroyed. In this case, that would mean frequent and
> unexpected thread creates and joins (and hangs if the thread is not ready to
> be joined). So it's dangerous touching external things in a destructor or
> constructor.
>
> It's maybe not a realistic example but there are other similar reasons why
> touching things outside your class should be left till after construction.
You are correct.
A good solution in this case would be to make AsyncTask noncopyable.
Generally speaking I don't mess with external state in constructors and destructors unless its the purpose of the class to do that, and I only do it through well defined interfaces (eg, the class takes an interface to another class which is used to set up and tear down state, and those functions are guarunteed to be called through exceptions)
Daniel van Vugt (vanvugt) wrote : | # |
Still, side-effects are bad. If constructing a variable (object) does more than allocate memory then you can cause all sorts of bugs. This is where procedural languages excel in long-term maintainability, because you are forced to explicitly state, and make it obvious, what function is being called and when. Nothing is ever hidden in construction and destruction.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:3513
http://
Executed test runs:
SUCCESS: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'compizconfig/libcompizconfig/tests/compizconfig_test_ccs_util.cpp' |
2 | --- compizconfig/libcompizconfig/tests/compizconfig_test_ccs_util.cpp 2012-11-20 14:51:27 +0000 |
3 | +++ compizconfig/libcompizconfig/tests/compizconfig_test_ccs_util.cpp 2012-12-05 05:49:21 +0000 |
4 | @@ -25,6 +25,7 @@ |
5 | |
6 | #include <gtest/gtest.h> |
7 | #include <gtest_shared_characterwrapper.h> |
8 | +#include <gtest_shared_tmpenv.h> |
9 | |
10 | #include <boost/noncopyable.hpp> |
11 | |
12 | @@ -257,35 +258,6 @@ |
13 | static const std::string four ("4"); |
14 | static const std::string trueStr ("true"); |
15 | |
16 | -class TmpEnv : |
17 | - boost::noncopyable |
18 | -{ |
19 | - public: |
20 | - |
21 | - explicit TmpEnv (const char *env, const char *val) : |
22 | - envRestore (env), |
23 | - envRestoreVal (getenv (env)) |
24 | - { |
25 | - if (!val) |
26 | - unsetenv (env); |
27 | - else |
28 | - setenv (env, val, TRUE); |
29 | - } |
30 | - |
31 | - ~TmpEnv () |
32 | - { |
33 | - if (envRestoreVal) |
34 | - setenv (envRestore, envRestoreVal, TRUE); |
35 | - else |
36 | - unsetenv (envRestore); |
37 | - } |
38 | - |
39 | - private: |
40 | - |
41 | - const char *envRestore; |
42 | - const char *envRestoreVal; |
43 | -}; |
44 | - |
45 | const char * getConstCharFromCharacterWrapper (const CharacterWrapper &wrap) |
46 | { |
47 | return wrap; |
48 | |
49 | === modified file 'include/core/screen.h' |
50 | --- include/core/screen.h 2012-12-02 17:37:35 +0000 |
51 | +++ include/core/screen.h 2012-12-05 05:49:21 +0000 |
52 | @@ -52,6 +52,7 @@ |
53 | |
54 | extern bool replaceCurrentWm; |
55 | extern bool debugOutput; |
56 | +extern bool sendStartupMessage; |
57 | |
58 | extern CompScreen *screen; |
59 | |
60 | |
61 | === modified file 'src/global.cpp' |
62 | --- src/global.cpp 2012-04-30 16:10:35 +0000 |
63 | +++ src/global.cpp 2012-12-05 05:49:21 +0000 |
64 | @@ -36,6 +36,7 @@ |
65 | bool restartSignal = false; |
66 | |
67 | bool replaceCurrentWm = false; |
68 | +bool sendStartupMessage = false; |
69 | bool useDesktopHints = false; |
70 | bool debugOutput = false; |
71 | bool synchronousX = false; |
72 | |
73 | === modified file 'src/main.cpp' |
74 | --- src/main.cpp 2012-06-29 03:39:30 +0000 |
75 | +++ src/main.cpp 2012-12-05 05:49:21 +0000 |
76 | @@ -103,6 +103,10 @@ |
77 | { |
78 | replaceCurrentWm = true; |
79 | } |
80 | + else if (!strcmp (argv[i], "--send-startup-message")) |
81 | + { |
82 | + sendStartupMessage = true; |
83 | + } |
84 | else if (!strcmp (argv[i], "--sm-disable")) |
85 | { |
86 | disableSm = true; |
87 | |
88 | === modified file 'src/screen.cpp' |
89 | --- src/screen.cpp 2012-12-04 16:09:08 +0000 |
90 | +++ src/screen.cpp 2012-12-05 05:49:21 +0000 |
91 | @@ -4675,6 +4675,40 @@ |
92 | return privateScreen.orphanData.nextActiveWindow; |
93 | } |
94 | |
95 | +namespace |
96 | +{ |
97 | +void sendStartupMessageToClients (Display *dpy, bool success) |
98 | +{ |
99 | + /* Send a client message indicating that our startup is complete if |
100 | + * we were asked to do so */ |
101 | + if (sendStartupMessage) |
102 | + { |
103 | + Atom startupMessageAtom = XInternAtom (dpy, |
104 | + "_COMPIZ_TESTING_STARTUP", |
105 | + FALSE); |
106 | + XEvent startupMessageEvent; |
107 | + Window root = DefaultRootWindow (dpy); |
108 | + |
109 | + startupMessageEvent.xclient.type = ClientMessage; |
110 | + startupMessageEvent.xclient.window = root; |
111 | + startupMessageEvent.xclient.message_type = startupMessageAtom; |
112 | + startupMessageEvent.xclient.format = 32; |
113 | + startupMessageEvent.xclient.data.l[0] = success ? 1 : 0; |
114 | + startupMessageEvent.xclient.data.l[1] = 0; |
115 | + startupMessageEvent.xclient.data.l[2] = 0; |
116 | + startupMessageEvent.xclient.data.l[3] = 0; |
117 | + startupMessageEvent.xclient.data.l[4] = 0; |
118 | + |
119 | + XSendEvent (dpy, |
120 | + root, |
121 | + FALSE, |
122 | + StructureNotifyMask, |
123 | + &startupMessageEvent); |
124 | + XFlush (dpy); |
125 | + } |
126 | +} |
127 | +} |
128 | + |
129 | |
130 | bool |
131 | PrivateScreen::initDisplay (const char *name, cps::History& history, unsigned int showingDesktopMask) |
132 | @@ -4706,6 +4740,7 @@ |
133 | { |
134 | compLogMessage ("core", CompLogLevelFatal, |
135 | "No sync extension"); |
136 | + sendStartupMessageToClients (dpy, false); |
137 | return false; |
138 | } |
139 | |
140 | @@ -4736,91 +4771,10 @@ |
141 | escapeKeyCode = XKeysymToKeycode (dpy, XStringToKeysym ("Escape")); |
142 | returnKeyCode = XKeysymToKeycode (dpy, XStringToKeysym ("Return")); |
143 | |
144 | - char buf[128]; |
145 | - sprintf (buf, "WM_S%d", DefaultScreen (dpy)); |
146 | - wmSnAtom = XInternAtom (dpy, buf, 0); |
147 | - |
148 | - Window currentWmSnOwner = XGetSelectionOwner (dpy, wmSnAtom); |
149 | - |
150 | - if (currentWmSnOwner != None) |
151 | - { |
152 | - if (!replaceCurrentWm) |
153 | - { |
154 | - compLogMessage ("core", CompLogLevelError, |
155 | - "Screen %d on display \"%s\" already " |
156 | - "has a window manager; try using the " |
157 | - "--replace option to replace the current " |
158 | - "window manager.", |
159 | - DefaultScreen (dpy), DisplayString (dpy)); |
160 | - |
161 | - return false; |
162 | - } |
163 | - |
164 | - XSelectInput (dpy, currentWmSnOwner, StructureNotifyMask); |
165 | - } |
166 | + modHandler->updateModifierMappings (); |
167 | |
168 | Window root_tmp = XRootWindow (dpy, DefaultScreen (dpy)); |
169 | |
170 | - XSetWindowAttributes attr; |
171 | - attr.override_redirect = true; |
172 | - attr.event_mask = PropertyChangeMask; |
173 | - |
174 | - Window newWmSnOwner = |
175 | - XCreateWindow (dpy, root_tmp, -100, -100, 1, 1, 0, |
176 | - CopyFromParent, CopyFromParent, |
177 | - CopyFromParent, |
178 | - CWOverrideRedirect | CWEventMask, |
179 | - &attr); |
180 | - |
181 | - XChangeProperty (dpy, newWmSnOwner, Atoms::wmName, Atoms::utf8String, 8, |
182 | - PropModeReplace, (unsigned char *) PACKAGE, |
183 | - strlen (PACKAGE)); |
184 | - |
185 | - XEvent event; |
186 | - XWindowEvent (dpy, newWmSnOwner, PropertyChangeMask, &event); |
187 | - |
188 | - Time wmSnTimestamp = event.xproperty.time; |
189 | - |
190 | - XSetSelectionOwner (dpy, wmSnAtom, newWmSnOwner, wmSnTimestamp); |
191 | - |
192 | - if (XGetSelectionOwner (dpy, wmSnAtom) != newWmSnOwner) |
193 | - { |
194 | - compLogMessage ("core", CompLogLevelError, |
195 | - "Could not acquire window manager " |
196 | - "selection on screen %d display \"%s\"", |
197 | - DefaultScreen (dpy), DisplayString (dpy)); |
198 | - |
199 | - XDestroyWindow (dpy, newWmSnOwner); |
200 | - |
201 | - return false; |
202 | - } |
203 | - |
204 | - /* Send client message indicating that we are now the window manager */ |
205 | - event.xclient.type = ClientMessage; |
206 | - event.xclient.window = root_tmp; |
207 | - event.xclient.message_type = Atoms::manager; |
208 | - event.xclient.format = 32; |
209 | - event.xclient.data.l[0] = wmSnTimestamp; |
210 | - event.xclient.data.l[1] = wmSnAtom; |
211 | - event.xclient.data.l[2] = 0; |
212 | - event.xclient.data.l[3] = 0; |
213 | - event.xclient.data.l[4] = 0; |
214 | - |
215 | - XSendEvent (dpy, root_tmp, FALSE, StructureNotifyMask, &event); |
216 | - |
217 | - /* Wait for old window manager to go away */ |
218 | - if (currentWmSnOwner != None) |
219 | - { |
220 | - do { |
221 | - XWindowEvent (dpy, currentWmSnOwner, StructureNotifyMask, &event); |
222 | - } while (event.type != DestroyNotify); |
223 | - } |
224 | - |
225 | - modHandler->updateModifierMappings (); |
226 | - |
227 | - CompScreenImpl::checkForError (dpy); |
228 | - |
229 | - XGrabServer (dpy); |
230 | |
231 | /* Don't select for SubstructureRedirectMask or |
232 | * SubstructureNotifyMask yet since we need to |
233 | @@ -4859,17 +4813,6 @@ |
234 | SubstructureNotifyMask); |
235 | } |
236 | |
237 | - if (CompScreenImpl::checkForError (dpy)) |
238 | - { |
239 | - compLogMessage ("core", CompLogLevelError, |
240 | - "Another window manager is " |
241 | - "already running on screen: %d", DefaultScreen (dpy)); |
242 | - |
243 | - XUngrabServer (dpy); |
244 | - XSync (dpy, false); |
245 | - return false; |
246 | - } |
247 | - |
248 | for (int i = 0; i < SCREEN_EDGE_NUM; i++) |
249 | { |
250 | screenEdge[i].id = None; |
251 | @@ -4883,9 +4826,6 @@ |
252 | snContext = sn_monitor_context_new (snDisplay, screenNum, |
253 | compScreenSnEvent, this, NULL); |
254 | |
255 | - wmSnSelectionWindow = newWmSnOwner; |
256 | - this->wmSnTimestamp = wmSnTimestamp; |
257 | - |
258 | if (!XGetWindowAttributes (dpy, rootWindow(), &attrib)) |
259 | return false; |
260 | |
261 | @@ -4901,6 +4841,7 @@ |
262 | { |
263 | compLogMessage ("core", CompLogLevelFatal, |
264 | "Couldn't get visual info for default visual"); |
265 | + sendStartupMessageToClients (dpy, false); |
266 | return false; |
267 | } |
268 | |
269 | @@ -4911,6 +4852,7 @@ |
270 | { |
271 | compLogMessage ("core", CompLogLevelFatal, |
272 | "Couldn't allocate color"); |
273 | + sendStartupMessageToClients (dpy, false); |
274 | XFree (visinfo); |
275 | return false; |
276 | } |
277 | @@ -4921,6 +4863,7 @@ |
278 | { |
279 | compLogMessage ("core", CompLogLevelFatal, |
280 | "Couldn't create bitmap"); |
281 | + sendStartupMessageToClients (dpy, false); |
282 | XFree (visinfo); |
283 | return false; |
284 | } |
285 | @@ -4931,6 +4874,7 @@ |
286 | { |
287 | compLogMessage ("core", CompLogLevelFatal, |
288 | "Couldn't create invisible cursor"); |
289 | + sendStartupMessageToClients (dpy, false); |
290 | XFree (visinfo); |
291 | return false; |
292 | } |
293 | @@ -4949,6 +4893,93 @@ |
294 | |
295 | getDesktopHints (showingDesktopMask); |
296 | |
297 | + /* Check for other window managers */ |
298 | + |
299 | + char buf[128]; |
300 | + sprintf (buf, "WM_S%d", DefaultScreen (dpy)); |
301 | + wmSnAtom = XInternAtom (dpy, buf, 0); |
302 | + |
303 | + Window currentWmSnOwner = XGetSelectionOwner (dpy, wmSnAtom); |
304 | + |
305 | + if (currentWmSnOwner != None) |
306 | + { |
307 | + if (!replaceCurrentWm) |
308 | + { |
309 | + compLogMessage ("core", CompLogLevelError, |
310 | + "Screen %d on display \"%s\" already " |
311 | + "has a window manager; try using the " |
312 | + "--replace option to replace the current " |
313 | + "window manager.", |
314 | + DefaultScreen (dpy), DisplayString (dpy)); |
315 | + sendStartupMessageToClients (dpy, false); |
316 | + |
317 | + return false; |
318 | + } |
319 | + |
320 | + XSelectInput (dpy, currentWmSnOwner, StructureNotifyMask); |
321 | + } |
322 | + |
323 | + XSetWindowAttributes attr; |
324 | + attr.override_redirect = true; |
325 | + attr.event_mask = PropertyChangeMask; |
326 | + |
327 | + Window newWmSnOwner = |
328 | + XCreateWindow (dpy, root_tmp, -100, -100, 1, 1, 0, |
329 | + CopyFromParent, CopyFromParent, |
330 | + CopyFromParent, |
331 | + CWOverrideRedirect | CWEventMask, |
332 | + &attr); |
333 | + |
334 | + XChangeProperty (dpy, newWmSnOwner, Atoms::wmName, Atoms::utf8String, 8, |
335 | + PropModeReplace, (unsigned char *) PACKAGE, |
336 | + strlen (PACKAGE)); |
337 | + |
338 | + XEvent event; |
339 | + XWindowEvent (dpy, newWmSnOwner, PropertyChangeMask, &event); |
340 | + |
341 | + Time wmSnTimestamp = event.xproperty.time; |
342 | + |
343 | + XSetSelectionOwner (dpy, wmSnAtom, newWmSnOwner, wmSnTimestamp); |
344 | + |
345 | + if (XGetSelectionOwner (dpy, wmSnAtom) != newWmSnOwner) |
346 | + { |
347 | + compLogMessage ("core", CompLogLevelError, |
348 | + "Could not acquire window manager " |
349 | + "selection on screen %d display \"%s\"", |
350 | + DefaultScreen (dpy), DisplayString (dpy)); |
351 | + |
352 | + XDestroyWindow (dpy, newWmSnOwner); |
353 | + sendStartupMessageToClients (dpy, false); |
354 | + |
355 | + return false; |
356 | + } |
357 | + |
358 | + /* Send client message indicating that we are now the window manager */ |
359 | + event.xclient.type = ClientMessage; |
360 | + event.xclient.window = root_tmp; |
361 | + event.xclient.message_type = Atoms::manager; |
362 | + event.xclient.format = 32; |
363 | + event.xclient.data.l[0] = wmSnTimestamp; |
364 | + event.xclient.data.l[1] = wmSnAtom; |
365 | + event.xclient.data.l[2] = 0; |
366 | + event.xclient.data.l[3] = 0; |
367 | + event.xclient.data.l[4] = 0; |
368 | + |
369 | + XSendEvent (dpy, root_tmp, FALSE, StructureNotifyMask, &event); |
370 | + |
371 | + /* Wait for old window manager to go away */ |
372 | + if (currentWmSnOwner != None) |
373 | + { |
374 | + do { |
375 | + XWindowEvent (dpy, currentWmSnOwner, StructureNotifyMask, &event); |
376 | + } while (event.type != DestroyNotify); |
377 | + } |
378 | + |
379 | + |
380 | + /* Server grab from here, we are creating windows */ |
381 | + |
382 | + XGrabServer (dpy); |
383 | + |
384 | { |
385 | XSetWindowAttributes attrib; |
386 | attrib.override_redirect = 1; |
387 | @@ -4994,11 +5025,30 @@ |
388 | |
389 | XDefineCursor (dpy, rootWindow(), normalCursor); |
390 | |
391 | + /* Attempt to gain SubstructureRedirectMask */ |
392 | + CompScreenImpl::checkForError (dpy); |
393 | + |
394 | /* We should get DestroyNotify events for any windows that were |
395 | * destroyed while initializing windows here now */ |
396 | XSelectInput (dpy, rootWindow(), attrib.your_event_mask | |
397 | SubstructureRedirectMask | SubstructureNotifyMask); |
398 | |
399 | + if (CompScreenImpl::checkForError (dpy)) |
400 | + { |
401 | + compLogMessage ("core", CompLogLevelError, |
402 | + "Another window manager is " |
403 | + "already running on screen: %d", DefaultScreen (dpy)); |
404 | + |
405 | + XUngrabServer (dpy); |
406 | + XSync (dpy, FALSE); |
407 | + |
408 | + sendStartupMessageToClients (dpy, false); |
409 | + return false; |
410 | + } |
411 | + |
412 | + wmSnSelectionWindow = newWmSnOwner; |
413 | + this->wmSnTimestamp = wmSnTimestamp; |
414 | + |
415 | Window rootReturn, parentReturn; |
416 | Window *children; |
417 | unsigned int nchildren; |
418 | @@ -5096,6 +5146,8 @@ |
419 | |
420 | pingTimer.start (); |
421 | |
422 | + sendStartupMessageToClients (dpy, true); |
423 | + |
424 | return true; |
425 | } |
426 | |
427 | @@ -5278,8 +5330,23 @@ |
428 | if (invisibleCursor != None) |
429 | XFreeCursor (dpy, invisibleCursor); |
430 | |
431 | - if (wmSnSelectionWindow != None) |
432 | - XDestroyWindow (dpy, wmSnSelectionWindow); |
433 | + /* Do not destroy wmSnSelectionWindow here. |
434 | + * |
435 | + * Because we haven't changed our active event mask |
436 | + * to remove SubstructureRedirectMask, other ICCCM |
437 | + * compliant window managers may receive a DestroyNotify |
438 | + * (eg, because we're blocked on XSync) before we get |
439 | + * a chance to close our display connection and remove |
440 | + * our SubstructureRedirectMask. That will cause them |
441 | + * to fail to start. |
442 | + * |
443 | + * The selection window is destroyed anyways when we |
444 | + * close our connection, and that is a very accurate |
445 | + * indicator to other WM's that we are well and truly |
446 | + * gone because the protocol requires the implementation |
447 | + * to remove all client event masks before destroying |
448 | + * windows |
449 | + */ |
450 | |
451 | XSync (dpy, False); // Redundant? |
452 | XCloseDisplay (dpy); |
453 | |
454 | === modified file 'tests/CMakeLists.txt' |
455 | --- tests/CMakeLists.txt 2012-09-18 10:45:43 +0000 |
456 | +++ tests/CMakeLists.txt 2012-12-05 05:49:21 +0000 |
457 | @@ -1,4 +1,6 @@ |
458 | if (COMPIZ_BUILD_TESTING) |
459 | add_subdirectory (integration) |
460 | add_subdirectory (system) |
461 | + add_subdirectory (shared) |
462 | + add_subdirectory (acceptance-tests) |
463 | endif (COMPIZ_BUILD_TESTING) |
464 | |
465 | === added directory 'tests/acceptance-tests' |
466 | === added file 'tests/acceptance-tests/CMakeLists.txt' |
467 | --- tests/acceptance-tests/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
468 | +++ tests/acceptance-tests/CMakeLists.txt 2012-12-05 05:49:21 +0000 |
469 | @@ -0,0 +1,1 @@ |
470 | +add_subdirectory (xorg-gtest) |
471 | |
472 | === added directory 'tests/acceptance-tests/xorg-gtest' |
473 | === added file 'tests/acceptance-tests/xorg-gtest/CMakeLists.txt' |
474 | --- tests/acceptance-tests/xorg-gtest/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
475 | +++ tests/acceptance-tests/xorg-gtest/CMakeLists.txt 2012-12-05 05:49:21 +0000 |
476 | @@ -0,0 +1,11 @@ |
477 | +if (BUILD_XORG_GTEST) |
478 | + |
479 | + include_directories (${compiz_SOURCE_DIR}/tests/shared |
480 | + ${COMPIZ_XORG_SYSTEM_TEST_INCLUDE_DIR} |
481 | + ${X11_INCLUDE_DIRS} |
482 | + ${XORG_SERVER_INCLUDE_XORG_GTEST} |
483 | + ${XORG_SERVER_GTEST_SRC} |
484 | + ${GTEST_INCLUDE_DIRS}) |
485 | + add_subdirectory (tests) |
486 | + |
487 | +endif (BUILD_XORG_GTEST) |
488 | |
489 | === added directory 'tests/acceptance-tests/xorg-gtest/tests' |
490 | === added file 'tests/acceptance-tests/xorg-gtest/tests/CMakeLists.txt' |
491 | --- tests/acceptance-tests/xorg-gtest/tests/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
492 | +++ tests/acceptance-tests/xorg-gtest/tests/CMakeLists.txt 2012-12-05 05:49:21 +0000 |
493 | @@ -0,0 +1,21 @@ |
494 | +link_directories (${X11_XI_LIBRARY_DIRS} |
495 | + ${compiz_BINARY_DIR}/tests/shared/src) |
496 | + |
497 | +add_executable (compiz_acceptance_test_xorg_replace_current_wm |
498 | + ${CMAKE_CURRENT_SOURCE_DIR}/compiz_acceptance_replace_current_wm.cpp) |
499 | + |
500 | +set (COMPIZ_XORG_ACCEPTANCE_TEST_LIBRARIES |
501 | + compiz_xorg_gtest_system_test |
502 | + xorg_gtest_all |
503 | + xorg_gtest_main |
504 | + ${GTEST_BOTH_LIBRARIES} |
505 | + ${CMAKE_THREAD_LIBS_INIT} |
506 | + ${XORG_SERVER_LIBRARIES} |
507 | + ${X11_XI_LIBRARIES}) |
508 | + |
509 | +target_link_libraries (compiz_acceptance_test_xorg_replace_current_wm |
510 | + ${COMPIZ_XORG_ACCEPTANCE_TEST_LIBRARIES} |
511 | + compiz_gtest_shared_async_task) |
512 | + |
513 | +# Not autodiscovering tests here, these tests are |
514 | +# inherently flakey. See README |
515 | |
516 | === added file 'tests/acceptance-tests/xorg-gtest/tests/README' |
517 | --- tests/acceptance-tests/xorg-gtest/tests/README 1970-01-01 00:00:00 +0000 |
518 | +++ tests/acceptance-tests/xorg-gtest/tests/README 2012-12-05 05:49:21 +0000 |
519 | @@ -0,0 +1,3 @@ |
520 | +These tests are acceptance tests and may take a while to run. As such |
521 | +please do not add them to the compiz_discover_tests list. Use them to |
522 | +check that startup and shutdown work correctly |
523 | |
524 | === added file 'tests/acceptance-tests/xorg-gtest/tests/compiz_acceptance_replace_current_wm.cpp' |
525 | --- tests/acceptance-tests/xorg-gtest/tests/compiz_acceptance_replace_current_wm.cpp 1970-01-01 00:00:00 +0000 |
526 | +++ tests/acceptance-tests/xorg-gtest/tests/compiz_acceptance_replace_current_wm.cpp 2012-12-05 05:49:21 +0000 |
527 | @@ -0,0 +1,255 @@ |
528 | +/* |
529 | + * Compiz XOrg GTest Acceptance Tests |
530 | + * |
531 | + * Copyright (C) 2012 Canonical Ltd. |
532 | + * |
533 | + * This library is free software; you can redistribute it and/or |
534 | + * modify it under the terms of the GNU Lesser General Public |
535 | + * License as published by the Free Software Foundation; either |
536 | + * version 2.1 of the License, or (at your option) any later version. |
537 | + |
538 | + * This library is distributed in the hope that it will be useful, |
539 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
540 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
541 | + * Lesser General Public License for more details. |
542 | + |
543 | + * You should have received a copy of the GNU Lesser General Public |
544 | + * License along with this library; if not, write to the Free Software |
545 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
546 | + * |
547 | + * Authored By: |
548 | + * Sam Spilsbury <smspillaz@gmail.com> |
549 | + */ |
550 | +#include <sys/types.h> |
551 | +#include <signal.h> |
552 | +#include <gtest/gtest.h> |
553 | +#include <gmock/gmock.h> |
554 | + |
555 | +#include <boost/function.hpp> |
556 | +#include <boost/bind.hpp> |
557 | + |
558 | +#include <boost/shared_ptr.hpp> |
559 | +#include <boost/make_shared.hpp> |
560 | + |
561 | +#include <xorg/gtest/xorg-gtest.h> |
562 | +#include <compiz-xorg-gtest.h> |
563 | + |
564 | +#include <gtest_shared_tmpenv.h> |
565 | +#include <gtest_shared_characterwrapper.h> |
566 | +#include <gtest_shared_asynctask.h> |
567 | + |
568 | +#include <X11/Xlib.h> |
569 | +#include <X11/Xatom.h> |
570 | + |
571 | +namespace ct = compiz::testing; |
572 | + |
573 | +class XorgAcceptance : |
574 | + public xorg::testing::Test |
575 | +{ |
576 | + public: |
577 | + |
578 | + typedef boost::shared_ptr <ct::CompizProcess> ProcessPtr; |
579 | +}; |
580 | + |
581 | +namespace |
582 | +{ |
583 | + |
584 | +char TEST_FAILED_MSG = 's'; |
585 | +char PROCESS_EXITED_MSG = 'd'; |
586 | + |
587 | +} |
588 | + |
589 | +class WaitForSuccessDeathTask : |
590 | + public AsyncTask |
591 | +{ |
592 | + public: |
593 | + |
594 | + typedef boost::function <xorg::testing::Process::State ()> GetProcessState; |
595 | + |
596 | + WaitForSuccessDeathTask (const GetProcessState &procState) : |
597 | + mProcessState (procState) |
598 | + { |
599 | + } |
600 | + |
601 | + private: |
602 | + |
603 | + GetProcessState mProcessState; |
604 | + |
605 | + void Task (); |
606 | +}; |
607 | + |
608 | +void |
609 | +WaitForSuccessDeathTask::Task () |
610 | +{ |
611 | + do |
612 | + { |
613 | + if (ReadMsgFromTest (TEST_FAILED_MSG, 1)) |
614 | + return; |
615 | + } while (mProcessState () != xorg::testing::Process::FINISHED_SUCCESS); |
616 | + |
617 | + /* The process died, send a message back saying that it did */ |
618 | + SendMsgToTest (PROCESS_EXITED_MSG); |
619 | +} |
620 | + |
621 | +TEST_F (XorgAcceptance, SIGINTClosesDown) |
622 | +{ |
623 | + /* XXX: This is a bit stupid, but we have to do it. |
624 | + * It seems as though closing the child stdout or |
625 | + * stderr will cause the client to hang indefinitely |
626 | + * when the child calls XSync (and that can happen |
627 | + * implicitly, eg XCloseDisplay) */ |
628 | + TmpEnv env ("XORG_GTEST_CHILD_STDOUT", "1"); |
629 | + ProcessPtr compiz (boost::make_shared <ct::CompizProcess> (Display (), |
630 | + ct::CompizProcess::WaitForStartupMessage, |
631 | + 3000)); |
632 | + |
633 | + pid_t firstProcessPid = compiz->Pid (); |
634 | + |
635 | + WaitForSuccessDeathTask::GetProcessState procState (boost::bind (&ct::CompizProcess::State, |
636 | + compiz.get ())); |
637 | + AsyncTask::Ptr task (boost::make_shared <WaitForSuccessDeathTask> (procState)); |
638 | + |
639 | + kill (firstProcessPid, SIGINT); |
640 | + |
641 | + const unsigned int maximumWaitTime = 10 * 1000; // Ten seconds |
642 | + |
643 | + if (!task->ReadMsgFromTask (PROCESS_EXITED_MSG, maximumWaitTime)) |
644 | + { |
645 | + task->SendMsgToTask (TEST_FAILED_MSG); |
646 | + throw std::runtime_error ("compiz process did not exit with success status"); |
647 | + } |
648 | +} |
649 | + |
650 | +TEST_F (XorgAcceptance, ReplaceOtherWMFast) |
651 | +{ |
652 | + /* XXX: This is a bit stupid, but we have to do it. |
653 | + * It seems as though closing the child stdout or |
654 | + * stderr will cause the client to hang indefinitely |
655 | + * when the child calls XSync (and that can happen |
656 | + * implicitly, eg XCloseDisplay) */ |
657 | + TmpEnv env ("XORG_GTEST_CHILD_STDOUT", "1"); |
658 | + ProcessPtr firstCompiz (boost::make_shared <ct::CompizProcess> (Display (), |
659 | + ct::CompizProcess::WaitForStartupMessage, |
660 | + 3000)); |
661 | + |
662 | + /* Expect it to exit */ |
663 | + WaitForSuccessDeathTask::GetProcessState procState (boost::bind (&ct::CompizProcess::State, |
664 | + firstCompiz.get ())); |
665 | + AsyncTask::Ptr task (boost::make_shared <WaitForSuccessDeathTask> (procState)); |
666 | + const unsigned int maximumWaitTime = 10 * 1000; // Ten seconds |
667 | + |
668 | + ProcessPtr secondCompiz (boost::make_shared <ct::CompizProcess> (Display (), |
669 | + static_cast <ct::CompizProcess::StartupFlags> ( |
670 | + ct::CompizProcess::WaitForStartupMessage | |
671 | + ct::CompizProcess::ReplaceCurrentWM), |
672 | + maximumWaitTime)); |
673 | + |
674 | + if (!task->ReadMsgFromTask (PROCESS_EXITED_MSG, maximumWaitTime)) |
675 | + { |
676 | + task->SendMsgToTask (TEST_FAILED_MSG); |
677 | + throw std::runtime_error ("compiz process did not exit with success status"); |
678 | + } |
679 | +} |
680 | + |
681 | +namespace |
682 | +{ |
683 | +char TEST_FINISHED_MSG = 's'; |
684 | +} |
685 | + |
686 | +class SlowDownTask : |
687 | + public AsyncTask |
688 | +{ |
689 | + public: |
690 | + |
691 | + typedef boost::function <xorg::testing::Process::State ()> GetProcessState; |
692 | + typedef boost::function <pid_t ()> GetPid; |
693 | + |
694 | + SlowDownTask (const GetProcessState &procState, |
695 | + const GetPid &pid) : |
696 | + mProcessState (procState), |
697 | + mPid (pid), |
698 | + mIsRunning (true) |
699 | + { |
700 | + } |
701 | + |
702 | + private: |
703 | + |
704 | + GetProcessState mProcessState; |
705 | + GetPid mPid; |
706 | + bool mIsRunning; |
707 | + |
708 | + void Task (); |
709 | +}; |
710 | + |
711 | +void |
712 | +SlowDownTask::Task () |
713 | +{ |
714 | + do |
715 | + { |
716 | + if (ReadMsgFromTest (TEST_FINISHED_MSG, mIsRunning ? 1 : 400)) |
717 | + return; |
718 | + |
719 | + pid_t pid = mPid (); |
720 | + |
721 | + if (pid) |
722 | + { |
723 | + if (mIsRunning) |
724 | + { |
725 | + kill (pid, SIGSTOP); |
726 | + mIsRunning = false; |
727 | + } |
728 | + else |
729 | + { |
730 | + kill (pid, SIGCONT); |
731 | + mIsRunning = true; |
732 | + } |
733 | + } |
734 | + |
735 | + } while (mProcessState () != xorg::testing::Process::FINISHED_SUCCESS); |
736 | + |
737 | + /* The process died, send a message back saying that it did */ |
738 | + SendMsgToTest (PROCESS_EXITED_MSG); |
739 | +} |
740 | + |
741 | +TEST_F (XorgAcceptance, ReplaceOtherWMSlow) |
742 | +{ |
743 | + ::Display *dpy = Display (); |
744 | + |
745 | + /* XXX: This is a bit stupid, but we have to do it. |
746 | + * It seems as though closing the child stdout or |
747 | + * stderr will cause the client to hang indefinitely |
748 | + * when the child calls XSync (and that can happen |
749 | + * implicitly, eg XCloseDisplay) */ |
750 | + TmpEnv env ("XORG_GTEST_CHILD_STDOUT", "1"); |
751 | + ProcessPtr firstCompiz (boost::make_shared <ct::CompizProcess> (dpy, |
752 | + ct::CompizProcess::WaitForStartupMessage, |
753 | + 3000)); |
754 | + |
755 | + SlowDownTask::GetProcessState procState (boost::bind (&ct::CompizProcess::State, |
756 | + firstCompiz.get ())); |
757 | + SlowDownTask::GetPid getPid (boost::bind (&ct::CompizProcess::Pid, |
758 | + firstCompiz.get ())); |
759 | + |
760 | + /* Slow down the first compiz */ |
761 | + AsyncTask::Ptr task (boost::make_shared <SlowDownTask> (procState, getPid)); |
762 | + |
763 | + /* Select for StructureNotifyMask */ |
764 | + XSelectInput (dpy, |
765 | + DefaultRootWindow (dpy), |
766 | + StructureNotifyMask); |
767 | + |
768 | + const unsigned int maximumWaitTime = 20 * 1000; // Twenty seconds |
769 | + |
770 | + ProcessPtr secondCompiz (boost::make_shared <ct::CompizProcess> (Display (), |
771 | + static_cast <ct::CompizProcess::StartupFlags> ( |
772 | + ct::CompizProcess::ReplaceCurrentWM | |
773 | + ct::CompizProcess::WaitForStartupMessage), |
774 | + maximumWaitTime)); |
775 | + |
776 | + /* Wait until the first one goes away */ |
777 | + if (!task->ReadMsgFromTask (PROCESS_EXITED_MSG, maximumWaitTime)) |
778 | + { |
779 | + task->SendMsgToTask (TEST_FINISHED_MSG); |
780 | + throw std::runtime_error ("compiz process did not exit with success status"); |
781 | + } |
782 | +} |
783 | |
784 | === added file 'tests/shared/CMakeLists.txt' |
785 | --- tests/shared/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
786 | +++ tests/shared/CMakeLists.txt 2012-12-05 05:49:21 +0000 |
787 | @@ -0,0 +1,2 @@ |
788 | +set (GTEST_SHARED_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) |
789 | +add_subdirectory (src) |
790 | |
791 | === added file 'tests/shared/gtest_shared_asynctask.h' |
792 | --- tests/shared/gtest_shared_asynctask.h 1970-01-01 00:00:00 +0000 |
793 | +++ tests/shared/gtest_shared_asynctask.h 2012-12-05 05:49:21 +0000 |
794 | @@ -0,0 +1,53 @@ |
795 | +/* |
796 | + * Copyright (C) 2012 Canonical Ltd. |
797 | + * |
798 | + * This library is free software; you can redistribute it and/or |
799 | + * modify it under the terms of the GNU Lesser General Public |
800 | + * License as published by the Free Software Foundation; either |
801 | + * version 2.1 of the License, or (at your option) any later version. |
802 | + |
803 | + * This library is distributed in the hope that it will be useful, |
804 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
805 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
806 | + * Lesser General Public License for more details. |
807 | + |
808 | + * You should have received a copy of the GNU Lesser General Public |
809 | + * License along with this library; if not, write to the Free Software |
810 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
811 | + * |
812 | + * Authored By: |
813 | + * Sam Spilsbury <sam.spilsbury@canonical.com> |
814 | + */ |
815 | +#ifndef _COMPIZ_GTEST_SHARED_ASYNCTASK_H |
816 | +#define _COMPIZ_GTEST_SHARED_ASYNCTASK_H |
817 | + |
818 | +#include <memory> |
819 | +#include <boost/shared_ptr.hpp> |
820 | + |
821 | +class PrivateAsyncTask; |
822 | +class AsyncTask |
823 | +{ |
824 | + public: |
825 | + |
826 | + typedef boost::shared_ptr <AsyncTask> Ptr; |
827 | + |
828 | + AsyncTask (); |
829 | + ~AsyncTask (); |
830 | + |
831 | + void SendMsgToTask (char msg); |
832 | + bool ReadMsgFromTask (char msg, int maximumWait); |
833 | + |
834 | + protected: |
835 | + |
836 | + void SendMsgToTest (char msg); |
837 | + bool ReadMsgFromTest (char msg, int maximumWait); |
838 | + |
839 | + private: |
840 | + |
841 | + static void * ThreadEntry (void *data); |
842 | + virtual void Task () = 0; |
843 | + |
844 | + std::auto_ptr <PrivateAsyncTask> priv; |
845 | +}; |
846 | + |
847 | +#endif |
848 | |
849 | === added file 'tests/shared/gtest_shared_tmpenv.h' |
850 | --- tests/shared/gtest_shared_tmpenv.h 1970-01-01 00:00:00 +0000 |
851 | +++ tests/shared/gtest_shared_tmpenv.h 2012-12-05 05:49:21 +0000 |
852 | @@ -0,0 +1,56 @@ |
853 | +/* |
854 | + * Copyright (C) 2012 Canonical Ltd. |
855 | + * |
856 | + * This library is free software; you can redistribute it and/or |
857 | + * modify it under the terms of the GNU Lesser General Public |
858 | + * License as published by the Free Software Foundation; either |
859 | + * version 2.1 of the License, or (at your option) any later version. |
860 | + |
861 | + * This library is distributed in the hope that it will be useful, |
862 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
863 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
864 | + * Lesser General Public License for more details. |
865 | + |
866 | + * You should have received a copy of the GNU Lesser General Public |
867 | + * License along with this library; if not, write to the Free Software |
868 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
869 | + * |
870 | + * Authored By: |
871 | + * Sam Spilsbury <sam.spilsbury@canonical.com> |
872 | + */ |
873 | +#ifndef _COMPIZ_GTEST_SHARED_TMPENV |
874 | +#define _COMPIZ_GTEST_SHARED_TMPENV |
875 | + |
876 | +#include <cstdlib> |
877 | +#include <boost/noncopyable.hpp> |
878 | + |
879 | +class TmpEnv : |
880 | + boost::noncopyable |
881 | +{ |
882 | + public: |
883 | + |
884 | + explicit TmpEnv (const char *env, const char *val) : |
885 | + envRestore (env), |
886 | + envRestoreVal (getenv (env)) |
887 | + { |
888 | + if (!val) |
889 | + unsetenv (env); |
890 | + else |
891 | + setenv (env, val, 1); |
892 | + } |
893 | + |
894 | + ~TmpEnv () |
895 | + { |
896 | + if (envRestoreVal) |
897 | + setenv (envRestore, envRestoreVal, 1); |
898 | + else |
899 | + unsetenv (envRestore); |
900 | + } |
901 | + |
902 | + private: |
903 | + |
904 | + const char *envRestore; |
905 | + const char *envRestoreVal; |
906 | +}; |
907 | + |
908 | +#endif |
909 | |
910 | === added directory 'tests/shared/src' |
911 | === added file 'tests/shared/src/CMakeLists.txt' |
912 | --- tests/shared/src/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
913 | +++ tests/shared/src/CMakeLists.txt 2012-12-05 05:49:21 +0000 |
914 | @@ -0,0 +1,7 @@ |
915 | +include_directories (${GTEST_SHARED_INCLUDE_DIR}) |
916 | + |
917 | +add_library (compiz_gtest_shared_async_task STATIC |
918 | + gtest_shared_asynctask.cpp) |
919 | + |
920 | +target_link_libraries (compiz_gtest_shared_async_task |
921 | + ${CMAKE_THREAD_LIBS_INIT}) |
922 | |
923 | === added file 'tests/shared/src/gtest_shared_asynctask.cpp' |
924 | --- tests/shared/src/gtest_shared_asynctask.cpp 1970-01-01 00:00:00 +0000 |
925 | +++ tests/shared/src/gtest_shared_asynctask.cpp 2012-12-05 05:49:21 +0000 |
926 | @@ -0,0 +1,138 @@ |
927 | +/* |
928 | + * Copyright (C) 2012 Canonical Ltd. |
929 | + * |
930 | + * This library is free software; you can redistribute it and/or |
931 | + * modify it under the terms of the GNU Lesser General Public |
932 | + * License as published by the Free Software Foundation; either |
933 | + * version 2.1 of the License, or (at your option) any later version. |
934 | + |
935 | + * This library is distributed in the hope that it will be useful, |
936 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
937 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
938 | + * Lesser General Public License for more details. |
939 | + |
940 | + * You should have received a copy of the GNU Lesser General Public |
941 | + * License along with this library; if not, write to the Free Software |
942 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
943 | + * |
944 | + * Authored By: |
945 | + * Sam Spilsbury <sam.spilsbury@canonical.com> |
946 | + */ |
947 | +#include <stdexcept> |
948 | +#include <string.h> |
949 | +#include <errno.h> |
950 | +#include <poll.h> |
951 | +#include <fcntl.h> |
952 | +#include <unistd.h> |
953 | +#include <pthread.h> |
954 | +#include <stdlib.h> |
955 | +#include "gtest_shared_asynctask.h" |
956 | +#include "gtest_shared_characterwrapper.h" |
957 | + |
958 | +namespace |
959 | +{ |
960 | +void |
961 | +runtimeErrorWithErrno () |
962 | +{ |
963 | + CharacterWrapper allocatedError (strerror (errno)); |
964 | + throw std::runtime_error (std::string (allocatedError)); |
965 | +} |
966 | + |
967 | +bool |
968 | +checkForMessageOnFd (int fd, int timeout, char expected) |
969 | +{ |
970 | + struct pollfd pfd; |
971 | + pfd.fd = fd; |
972 | + pfd.events = POLLIN; |
973 | + pfd.revents = 0; |
974 | + |
975 | + /* Wait 1ms */ |
976 | + poll (&pfd, 1, timeout); |
977 | + |
978 | + if (pfd.revents == POLLIN) |
979 | + { |
980 | + char msg[1]; |
981 | + size_t sz = read (pfd.fd, reinterpret_cast <void *> (msg), 1); |
982 | + if (sz == 1 && |
983 | + msg[0] == expected) |
984 | + return true; |
985 | + } |
986 | + |
987 | + return false; |
988 | +} |
989 | +} |
990 | + |
991 | +class PrivateAsyncTask |
992 | +{ |
993 | + public: |
994 | + |
995 | + int testToTaskFd[2]; |
996 | + int taskToTestFd[2]; |
997 | + pthread_t mThread; |
998 | +}; |
999 | + |
1000 | +AsyncTask::AsyncTask () : |
1001 | + priv (new PrivateAsyncTask) |
1002 | +{ |
1003 | + if (pipe2 (priv->testToTaskFd, O_NONBLOCK | O_CLOEXEC) == -1) |
1004 | + runtimeErrorWithErrno (); |
1005 | + |
1006 | + if (pipe2 (priv->taskToTestFd, O_NONBLOCK | O_CLOEXEC) == -1) |
1007 | + runtimeErrorWithErrno (); |
1008 | + |
1009 | + if (pthread_create (&priv->mThread, |
1010 | + NULL, |
1011 | + AsyncTask::ThreadEntry, |
1012 | + reinterpret_cast<void *> (this)) != 0) |
1013 | + runtimeErrorWithErrno (); |
1014 | +} |
1015 | + |
1016 | +AsyncTask::~AsyncTask () |
1017 | +{ |
1018 | + void *ret; |
1019 | + if (pthread_join (priv->mThread, &ret) != 0) |
1020 | + { |
1021 | + runtimeErrorWithErrno (); |
1022 | + } |
1023 | + |
1024 | + close (priv->testToTaskFd[0]); |
1025 | + close (priv->testToTaskFd[1]); |
1026 | + close (priv->taskToTestFd[0]); |
1027 | + close (priv->taskToTestFd[0]); |
1028 | +} |
1029 | + |
1030 | +bool |
1031 | +AsyncTask::ReadMsgFromTest (char msg, int maximumWait) |
1032 | +{ |
1033 | + return checkForMessageOnFd (priv->testToTaskFd[0], maximumWait, msg); |
1034 | +} |
1035 | + |
1036 | +bool |
1037 | +AsyncTask::ReadMsgFromTask (char msg, int maximumWait) |
1038 | +{ |
1039 | + return checkForMessageOnFd (priv->taskToTestFd[0], maximumWait, msg); |
1040 | +} |
1041 | + |
1042 | +void |
1043 | +AsyncTask::SendMsgToTest (char msg) |
1044 | +{ |
1045 | + char buf[1] = { msg }; |
1046 | + if (write (priv->taskToTestFd[1], reinterpret_cast <void *> (buf), 1) == -1) |
1047 | + runtimeErrorWithErrno (); |
1048 | +} |
1049 | + |
1050 | +void |
1051 | +AsyncTask::SendMsgToTask (char msg) |
1052 | +{ |
1053 | + char buf[1] = { msg }; |
1054 | + if (write (priv->testToTaskFd[1], reinterpret_cast <void *> (buf), 1) == -1) |
1055 | + runtimeErrorWithErrno (); |
1056 | +} |
1057 | + |
1058 | +void * |
1059 | +AsyncTask::ThreadEntry (void *data) |
1060 | +{ |
1061 | + AsyncTask *task = reinterpret_cast <AsyncTask *> (data); |
1062 | + task->Task (); |
1063 | + return NULL; |
1064 | +} |
1065 | |
1066 | === modified file 'tests/system/xorg-gtest/include/compiz-xorg-gtest.h' |
1067 | --- tests/system/xorg-gtest/include/compiz-xorg-gtest.h 2012-09-18 15:48:30 +0000 |
1068 | +++ tests/system/xorg-gtest/include/compiz-xorg-gtest.h 2012-12-05 05:49:21 +0000 |
1069 | @@ -22,6 +22,8 @@ |
1070 | */ |
1071 | #ifndef _COMPIZ_XORG_GTEST_H |
1072 | #define _COMPIZ_XORG_GTEST_H |
1073 | +#include <memory> |
1074 | +#include <list> |
1075 | #include <gtest/gtest.h> |
1076 | #include <gmock/gmock.h> |
1077 | #include <xorg/gtest/xorg-gtest.h> |
1078 | @@ -35,6 +37,8 @@ |
1079 | typedef ::testing::MatcherInterface <const XEvent &> XEventMatcher; |
1080 | |
1081 | std::list <Window> NET_CLIENT_LIST_STACKING (Display *); |
1082 | + bool AdvanceToNextEventOnSuccess (Display *dpy, |
1083 | + bool waitResult); |
1084 | bool WaitForEventOfTypeOnWindow (Display *dpy, |
1085 | Window w, |
1086 | int type, |
1087 | @@ -49,17 +53,50 @@ |
1088 | const XEventMatcher &matcher, |
1089 | int timeout = 1000); |
1090 | |
1091 | - class XorgSystemTest : |
1092 | + class PrivateCompizProcess; |
1093 | + class CompizProcess |
1094 | + { |
1095 | + public: |
1096 | + typedef enum _StartupFlags |
1097 | + { |
1098 | + ReplaceCurrentWM = (1 << 0), |
1099 | + WaitForStartupMessage = (1 << 1), |
1100 | + ExpectStartupFailure = (1 << 2) |
1101 | + } StartupFlags; |
1102 | + |
1103 | + CompizProcess (Display *dpy, StartupFlags, unsigned int waitTimeout); |
1104 | + ~CompizProcess (); |
1105 | + xorg::testing::Process::State State (); |
1106 | + pid_t Pid (); |
1107 | + |
1108 | + private: |
1109 | + std::auto_ptr <PrivateCompizProcess> priv; |
1110 | + }; |
1111 | + |
1112 | + class PrivateCompizXorgSystemTest; |
1113 | + class CompizXorgSystemTest : |
1114 | public xorg::testing::Test |
1115 | { |
1116 | public: |
1117 | |
1118 | + CompizXorgSystemTest (); |
1119 | + |
1120 | virtual void SetUp (); |
1121 | virtual void TearDown (); |
1122 | |
1123 | + xorg::testing::Process::State CompizProcessState (); |
1124 | + void StartCompiz (CompizProcess::StartupFlags flags); |
1125 | + |
1126 | private: |
1127 | - |
1128 | - xorg::testing::Process mCompizProcess; |
1129 | + std::auto_ptr <PrivateCompizXorgSystemTest> priv; |
1130 | + }; |
1131 | + |
1132 | + class AutostartCompizXorgSystemTest : |
1133 | + public CompizXorgSystemTest |
1134 | + { |
1135 | + public: |
1136 | + |
1137 | + virtual void SetUp (); |
1138 | }; |
1139 | } |
1140 | } |
1141 | |
1142 | === modified file 'tests/system/xorg-gtest/src/compiz-xorg-gtest.cpp' |
1143 | --- tests/system/xorg-gtest/src/compiz-xorg-gtest.cpp 2012-09-18 16:14:34 +0000 |
1144 | +++ tests/system/xorg-gtest/src/compiz-xorg-gtest.cpp 2012-12-05 05:49:21 +0000 |
1145 | @@ -24,6 +24,7 @@ |
1146 | #include <stdexcept> |
1147 | #include <gtest/gtest.h> |
1148 | #include <gmock/gmock.h> |
1149 | +#include <boost/shared_ptr.hpp> |
1150 | #include <xorg/gtest/xorg-gtest.h> |
1151 | #include <compiz-xorg-gtest.h> |
1152 | #include <X11/Xlib.h> |
1153 | @@ -31,6 +32,9 @@ |
1154 | |
1155 | #include "compiz-xorg-gtest-config.h" |
1156 | |
1157 | +using ::testing::MatchResultListener; |
1158 | +using ::testing::MatcherInterface; |
1159 | + |
1160 | namespace ct = compiz::testing; |
1161 | namespace |
1162 | { |
1163 | @@ -44,6 +48,16 @@ |
1164 | } |
1165 | |
1166 | bool |
1167 | +ct::AdvanceToNextEventOnSuccess (Display *dpy, |
1168 | + bool waitResult) |
1169 | +{ |
1170 | + if (waitResult) |
1171 | + RemoveEventFromQueue (dpy); |
1172 | + |
1173 | + return waitResult; |
1174 | +} |
1175 | + |
1176 | +bool |
1177 | ct::WaitForEventOfTypeOnWindow (Display *dpy, |
1178 | Window w, |
1179 | int type, |
1180 | @@ -57,10 +71,11 @@ |
1181 | if (!XPeekEvent (dpy, &event)) |
1182 | throw std::runtime_error ("Failed to peek event"); |
1183 | |
1184 | - RemoveEventFromQueue (dpy); |
1185 | - |
1186 | if (event.xany.window != w) |
1187 | + { |
1188 | + RemoveEventFromQueue (dpy); |
1189 | continue; |
1190 | + } |
1191 | |
1192 | return true; |
1193 | } |
1194 | @@ -84,9 +99,10 @@ |
1195 | throw std::runtime_error ("Failed to peek event"); |
1196 | |
1197 | if (!matcher.MatchAndExplain (event, NULL)) |
1198 | + { |
1199 | + RemoveEventFromQueue (dpy); |
1200 | continue; |
1201 | - |
1202 | - RemoveEventFromQueue (dpy); |
1203 | + } |
1204 | |
1205 | return true; |
1206 | } |
1207 | @@ -124,27 +140,190 @@ |
1208 | return stackingOrder; |
1209 | } |
1210 | |
1211 | -void |
1212 | -ct::XorgSystemTest::SetUp () |
1213 | +Display *d; |
1214 | + |
1215 | +namespace |
1216 | +{ |
1217 | +class StartupClientMessageMatcher : |
1218 | + public ct::XEventMatcher |
1219 | +{ |
1220 | + public: |
1221 | + |
1222 | + StartupClientMessageMatcher (Atom startup, |
1223 | + Window root, |
1224 | + ct::CompizProcess::StartupFlags state) : |
1225 | + mStartup (startup), |
1226 | + mRoot (root), |
1227 | + mFlags (state) |
1228 | + { |
1229 | + } |
1230 | + |
1231 | + virtual bool MatchAndExplain (const XEvent &event, MatchResultListener *listener) const |
1232 | + { |
1233 | + int state = mFlags & ct::CompizProcess::ExpectStartupFailure ? 0 : 1; |
1234 | + |
1235 | + if (event.xclient.window == mRoot && |
1236 | + event.xclient.message_type == mStartup && |
1237 | + event.xclient.data.l[0] == state) |
1238 | + return true; |
1239 | + |
1240 | + return false; |
1241 | + } |
1242 | + |
1243 | + virtual void DescribeTo (std::ostream *os) const |
1244 | + { |
1245 | + *os << "is startup message"; |
1246 | + } |
1247 | + |
1248 | + virtual void DescribeNegationTo (std::ostream *os) const |
1249 | + { |
1250 | + *os << "is not startup message"; |
1251 | + } |
1252 | + |
1253 | + private: |
1254 | + |
1255 | + Atom mStartup; |
1256 | + Window mRoot; |
1257 | + ct::CompizProcess::StartupFlags mFlags; |
1258 | +}; |
1259 | +} |
1260 | + |
1261 | +class ct::PrivateCompizProcess |
1262 | +{ |
1263 | + public: |
1264 | + PrivateCompizProcess (ct::CompizProcess::StartupFlags flags) : |
1265 | + mFlags (flags), |
1266 | + mIsRunning (true) |
1267 | + { |
1268 | + } |
1269 | + |
1270 | + void WaitForStartupMessage (Display *dpy, |
1271 | + ct::CompizProcess::StartupFlags flags, |
1272 | + unsigned int waitTimeout); |
1273 | + |
1274 | + typedef boost::shared_ptr <xorg::testing::Process> ProcessPtr; |
1275 | + |
1276 | + ct::CompizProcess::StartupFlags mFlags; |
1277 | + bool mIsRunning; |
1278 | + xorg::testing::Process mProcess; |
1279 | +}; |
1280 | + |
1281 | +void |
1282 | +ct::PrivateCompizProcess::WaitForStartupMessage (Display *dpy, |
1283 | + ct::CompizProcess::StartupFlags flags, |
1284 | + unsigned int waitTimeout) |
1285 | +{ |
1286 | + XWindowAttributes attrib; |
1287 | + Window root = DefaultRootWindow (dpy); |
1288 | + |
1289 | + Atom startup = XInternAtom (dpy, |
1290 | + "_COMPIZ_TESTING_STARTUP", |
1291 | + false); |
1292 | + |
1293 | + StartupClientMessageMatcher matcher (startup, root, flags); |
1294 | + |
1295 | + d = dpy; |
1296 | + /* Save the current event mask and subscribe to StructureNotifyMask only */ |
1297 | + ASSERT_TRUE (XGetWindowAttributes (dpy, root, &attrib)); |
1298 | + XSelectInput (dpy, root, StructureNotifyMask | |
1299 | + attrib.your_event_mask); |
1300 | + |
1301 | + ASSERT_TRUE (ct::AdvanceToNextEventOnSuccess ( |
1302 | + dpy, |
1303 | + ct::WaitForEventOfTypeOnWindowMatching (dpy, |
1304 | + root, |
1305 | + ClientMessage, |
1306 | + -1, |
1307 | + -1, |
1308 | + matcher, |
1309 | + waitTimeout))); |
1310 | + |
1311 | + XSelectInput (dpy, root, attrib.your_event_mask); |
1312 | +} |
1313 | + |
1314 | +ct::CompizProcess::CompizProcess (::Display *dpy, |
1315 | + ct::CompizProcess::StartupFlags flags, |
1316 | + unsigned int waitTimeout) : |
1317 | + priv (new PrivateCompizProcess (flags)) |
1318 | +{ |
1319 | + xorg::testing::Process::SetEnv ("LD_LIBRARY_PATH", compizLDLibraryPath, true); |
1320 | + |
1321 | + std::vector <std::string> args; |
1322 | + |
1323 | + if (flags & ct::CompizProcess::ReplaceCurrentWM) |
1324 | + args.push_back ("--replace"); |
1325 | + |
1326 | + args.push_back ("--send-startup-message"); |
1327 | + |
1328 | + priv->mProcess.Start (compizBinaryPath, args); |
1329 | + EXPECT_EQ (priv->mProcess.GetState (), xorg::testing::Process::RUNNING); |
1330 | + |
1331 | + if (flags & ct::CompizProcess::WaitForStartupMessage) |
1332 | + priv->WaitForStartupMessage (dpy, flags, waitTimeout); |
1333 | +} |
1334 | + |
1335 | +ct::CompizProcess::~CompizProcess () |
1336 | +{ |
1337 | + if (priv->mProcess.GetState () == xorg::testing::Process::RUNNING) |
1338 | + priv->mProcess.Kill (); |
1339 | +} |
1340 | + |
1341 | +xorg::testing::Process::State |
1342 | +ct::CompizProcess::State () |
1343 | +{ |
1344 | + return priv->mProcess.GetState (); |
1345 | +} |
1346 | + |
1347 | +pid_t |
1348 | +ct::CompizProcess::Pid () |
1349 | +{ |
1350 | + return priv->mProcess.Pid (); |
1351 | +} |
1352 | + |
1353 | +class ct::PrivateCompizXorgSystemTest |
1354 | +{ |
1355 | + public: |
1356 | + |
1357 | + boost::shared_ptr <ct::CompizProcess> mProcess; |
1358 | +}; |
1359 | + |
1360 | +ct::CompizXorgSystemTest::CompizXorgSystemTest () : |
1361 | + priv (new PrivateCompizXorgSystemTest) |
1362 | +{ |
1363 | +} |
1364 | + |
1365 | +void |
1366 | +ct::CompizXorgSystemTest::SetUp () |
1367 | { |
1368 | xorg::testing::Test::SetUp (); |
1369 | - |
1370 | - ::Display *dpy = Display (); |
1371 | - XSelectInput (dpy, DefaultRootWindow (dpy), SubstructureNotifyMask | PropertyChangeMask); |
1372 | - |
1373 | - xorg::testing::Process::SetEnv ("LD_LIBRARY_PATH", compizLDLibraryPath, true); |
1374 | - mCompizProcess.Start (compizBinaryPath, "--replace", NULL); |
1375 | - |
1376 | - /* Output buffer is flushed by this point, compiz has a server grab, |
1377 | - * so we will only get this once the relevant initialization is complete */ |
1378 | - ASSERT_TRUE (xorg::testing::XServer::WaitForEventOfType (Display (), PropertyNotify, -1, -1, 3000)); |
1379 | - |
1380 | - ASSERT_EQ (mCompizProcess.GetState (), xorg::testing::Process::RUNNING); |
1381 | } |
1382 | |
1383 | void |
1384 | -ct::XorgSystemTest::TearDown () |
1385 | +ct::CompizXorgSystemTest::TearDown () |
1386 | { |
1387 | - mCompizProcess.Kill (); |
1388 | + priv->mProcess.reset (); |
1389 | + |
1390 | xorg::testing::Test::TearDown (); |
1391 | } |
1392 | + |
1393 | +xorg::testing::Process::State |
1394 | +ct::CompizXorgSystemTest::CompizProcessState () |
1395 | +{ |
1396 | + if (priv->mProcess) |
1397 | + return priv->mProcess->State (); |
1398 | + return xorg::testing::Process::NONE; |
1399 | +} |
1400 | + |
1401 | +void |
1402 | +ct::CompizXorgSystemTest::StartCompiz (ct::CompizProcess::StartupFlags flags) |
1403 | +{ |
1404 | + priv->mProcess.reset (new ct::CompizProcess (Display (), flags, 3000)); |
1405 | +} |
1406 | + |
1407 | +void |
1408 | +ct::AutostartCompizXorgSystemTest::SetUp () |
1409 | +{ |
1410 | + StartCompiz (static_cast <ct::CompizProcess::StartupFlags> ( |
1411 | + ct::CompizProcess::ReplaceCurrentWM | |
1412 | + ct::CompizProcess::WaitForStartupMessage)); |
1413 | +} |
1414 | |
1415 | === modified file 'tests/system/xorg-gtest/tests/CMakeLists.txt' |
1416 | --- tests/system/xorg-gtest/tests/CMakeLists.txt 2012-09-18 13:17:09 +0000 |
1417 | +++ tests/system/xorg-gtest/tests/CMakeLists.txt 2012-12-05 05:49:21 +0000 |
1418 | @@ -2,26 +2,40 @@ |
1419 | |
1420 | if (BUILD_XORG_GTEST AND X11_XI_FOUND) |
1421 | |
1422 | - include_directories (${COMPIZ_XORG_SYSTEM_TEST_INCLUDE_DIR} |
1423 | + include_directories (${compiz_SOURCE_DIR}/tests/shared |
1424 | + ${COMPIZ_XORG_SYSTEM_TEST_INCLUDE_DIR} |
1425 | ${X11_INCLUDE_DIRS} |
1426 | ${XORG_SERVER_INCLUDE_XORG_GTEST} |
1427 | ${XORG_SERVER_GTEST_SRC} |
1428 | ${GTEST_INCLUDE_DIRS}) |
1429 | |
1430 | - link_directories (${X11_XI_LIBRARY_DIRS}) |
1431 | + link_directories (${X11_XI_LIBRARY_DIRS} |
1432 | + ${compiz_BINARY_DIR}/tests/shared/src) |
1433 | |
1434 | add_executable (compiz_xorg_gtest_test_window_stacking |
1435 | ${CMAKE_CURRENT_SOURCE_DIR}/compiz_xorg_gtest_test_window_stacking.cpp) |
1436 | |
1437 | + add_executable (compiz_xorg_gtest_test_icccm |
1438 | + ${CMAKE_CURRENT_SOURCE_DIR}/compiz_xorg_gtest_icccm.cpp) |
1439 | + |
1440 | + set (COMPIZ_XORG_GTEST_LIBRARIES |
1441 | + compiz_xorg_gtest_system_test |
1442 | + xorg_gtest_all |
1443 | + xorg_gtest_main |
1444 | + ${GTEST_BOTH_LIBRARIES} |
1445 | + ${XORG_SERVER_LIBRARIES} |
1446 | + ${X11_XI_LIBRARIES} |
1447 | + ${CMAKE_THREAD_LIBS_INIT}) |
1448 | + |
1449 | target_link_libraries (compiz_xorg_gtest_test_window_stacking |
1450 | - compiz_xorg_gtest_system_test |
1451 | - xorg_gtest_all |
1452 | - xorg_gtest_main |
1453 | - ${GTEST_BOTH_LIBRARIES} |
1454 | - ${CMAKE_THREAD_LIBS_INIT} |
1455 | - ${XORG_SERVER_LIBRARIES} |
1456 | - ${X11_XI_LIBRARIES}) |
1457 | + ${COMPIZ_XORG_GTEST_LIBRARIES}) |
1458 | |
1459 | compiz_discover_tests (compiz_xorg_gtest_test_window_stacking) |
1460 | |
1461 | + target_link_libraries (compiz_xorg_gtest_test_icccm |
1462 | + compiz_gtest_shared_async_task |
1463 | + ${COMPIZ_XORG_GTEST_LIBRARIES}) |
1464 | + |
1465 | + compiz_discover_tests (compiz_xorg_gtest_test_icccm) |
1466 | + |
1467 | endif (BUILD_XORG_GTEST AND X11_XI_FOUND) |
1468 | |
1469 | === added file 'tests/system/xorg-gtest/tests/compiz_xorg_gtest_icccm.cpp' |
1470 | --- tests/system/xorg-gtest/tests/compiz_xorg_gtest_icccm.cpp 1970-01-01 00:00:00 +0000 |
1471 | +++ tests/system/xorg-gtest/tests/compiz_xorg_gtest_icccm.cpp 2012-12-05 05:49:21 +0000 |
1472 | @@ -0,0 +1,136 @@ |
1473 | +/* |
1474 | + * Compiz XOrg GTest, ICCCM compliance |
1475 | + * |
1476 | + * Copyright (C) 2012 Canonical Ltd. |
1477 | + * |
1478 | + * This library is free software; you can redistribute it and/or |
1479 | + * modify it under the terms of the GNU Lesser General Public |
1480 | + * License as published by the Free Software Foundation; either |
1481 | + * version 2.1 of the License, or (at your option) any later version. |
1482 | + |
1483 | + * This library is distributed in the hope that it will be useful, |
1484 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1485 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1486 | + * Lesser General Public License for more details. |
1487 | + |
1488 | + * You should have received a copy of the GNU Lesser General Public |
1489 | + * License along with this library; if not, write to the Free Software |
1490 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
1491 | + * |
1492 | + * Authored By: |
1493 | + * Sam Spilsbury <smspillaz@gmail.com> |
1494 | + */ |
1495 | +#include <list> |
1496 | +#include <string> |
1497 | +#include <stdexcept> |
1498 | +#include <boost/function.hpp> |
1499 | +#include <boost/bind.hpp> |
1500 | +#include <boost/shared_ptr.hpp> |
1501 | +#include <boost/make_shared.hpp> |
1502 | +#include <gtest/gtest.h> |
1503 | +#include <gmock/gmock.h> |
1504 | +#include <xorg/gtest/xorg-gtest.h> |
1505 | +#include <compiz-xorg-gtest.h> |
1506 | + |
1507 | +#include <gtest_shared_tmpenv.h> |
1508 | +#include <gtest_shared_characterwrapper.h> |
1509 | +#include <gtest_shared_asynctask.h> |
1510 | + |
1511 | +#include <X11/Xlib.h> |
1512 | +#include <X11/Xatom.h> |
1513 | + |
1514 | +using ::testing::MatchResultListener; |
1515 | +using ::testing::MakeMatcher; |
1516 | +using ::testing::Matcher; |
1517 | + |
1518 | +namespace ct = compiz::testing; |
1519 | + |
1520 | +namespace |
1521 | +{ |
1522 | + |
1523 | +char TEST_FAILED_MSG = 's'; |
1524 | +char PROCESS_EXITED_MSG = 'd'; |
1525 | + |
1526 | +} |
1527 | + |
1528 | +class WaitForSuccessDeathTask : |
1529 | + public AsyncTask |
1530 | +{ |
1531 | + public: |
1532 | + |
1533 | + typedef boost::function <xorg::testing::Process::State ()> GetProcessState; |
1534 | + |
1535 | + WaitForSuccessDeathTask (const GetProcessState &procState) : |
1536 | + mProcessState (procState) |
1537 | + { |
1538 | + } |
1539 | + |
1540 | + private: |
1541 | + |
1542 | + GetProcessState mProcessState; |
1543 | + |
1544 | + void Task (); |
1545 | +}; |
1546 | + |
1547 | +void |
1548 | +WaitForSuccessDeathTask::Task () |
1549 | +{ |
1550 | + do |
1551 | + { |
1552 | + if (ReadMsgFromTest (TEST_FAILED_MSG, 1)) |
1553 | + return; |
1554 | + } while (mProcessState () != xorg::testing::Process::FINISHED_FAILURE); |
1555 | + |
1556 | + /* The process died, send a message back saying that it did */ |
1557 | + SendMsgToTest (PROCESS_EXITED_MSG); |
1558 | +} |
1559 | + |
1560 | +class CompizXorgSystemICCCM : |
1561 | + public ct::CompizXorgSystemTest |
1562 | +{ |
1563 | + public: |
1564 | + |
1565 | + virtual void SetUp () |
1566 | + { |
1567 | + ct::CompizXorgSystemTest::SetUp (); |
1568 | + |
1569 | + ::Display *dpy = Display (); |
1570 | + |
1571 | + XSelectInput (dpy, DefaultRootWindow (dpy), |
1572 | + StructureNotifyMask | |
1573 | + FocusChangeMask | |
1574 | + PropertyChangeMask | |
1575 | + SubstructureRedirectMask); |
1576 | + } |
1577 | + |
1578 | + private: |
1579 | +}; |
1580 | + |
1581 | +TEST_F (CompizXorgSystemICCCM, SomeoneElseHasSubstructureRedirectMask) |
1582 | +{ |
1583 | + /* XXX: This is a bit stupid, but we have to do it. |
1584 | + * It seems as though closing the child stdout or |
1585 | + * stderr will cause the client to hang indefinitely |
1586 | + * when the child calls XSync (and that can happen |
1587 | + * implicitly, eg XCloseDisplay) */ |
1588 | + TmpEnv env ("XORG_GTEST_CHILD_STDOUT", "1"); |
1589 | + |
1590 | + StartCompiz (static_cast <ct::CompizProcess::StartupFlags> ( |
1591 | + ct::CompizProcess::ExpectStartupFailure | |
1592 | + ct::CompizProcess::ReplaceCurrentWM | |
1593 | + ct::CompizProcess::WaitForStartupMessage)); |
1594 | + |
1595 | + WaitForSuccessDeathTask::GetProcessState processState (boost::bind (&CompizXorgSystemICCCM::CompizProcessState, |
1596 | + this)); |
1597 | + AsyncTask::Ptr task (boost::make_shared <WaitForSuccessDeathTask> (processState)); |
1598 | + |
1599 | + /* Now wait for the thread to tell us the news - |
1600 | + * this will block for up to ten seconds */ |
1601 | + const int maximumWaitTime = 1000 * 10; // 10 seconds |
1602 | + |
1603 | + if (!task->ReadMsgFromTask (PROCESS_EXITED_MSG, maximumWaitTime)) |
1604 | + { |
1605 | + task->SendMsgToTask (TEST_FAILED_MSG); |
1606 | + throw std::runtime_error ("compiz process did not exit with failure status"); |
1607 | + } |
1608 | +} |
1609 | |
1610 | === modified file 'tests/system/xorg-gtest/tests/compiz_xorg_gtest_test_window_stacking.cpp' |
1611 | --- tests/system/xorg-gtest/tests/compiz_xorg_gtest_test_window_stacking.cpp 2012-11-28 18:14:56 +0000 |
1612 | +++ tests/system/xorg-gtest/tests/compiz_xorg_gtest_test_window_stacking.cpp 2012-12-05 05:49:21 +0000 |
1613 | @@ -38,8 +38,17 @@ |
1614 | namespace ct = compiz::testing; |
1615 | |
1616 | class CompizXorgSystemStackingTest : |
1617 | - public compiz::testing::XorgSystemTest |
1618 | + public ct::AutostartCompizXorgSystemTest |
1619 | { |
1620 | + public: |
1621 | + |
1622 | + virtual void SetUp () |
1623 | + { |
1624 | + ct::CompizXorgSystemTest::SetUp (); |
1625 | + |
1626 | + ::Display *dpy = Display (); |
1627 | + XSelectInput (dpy, DefaultRootWindow (dpy), SubstructureNotifyMask | PropertyChangeMask); |
1628 | + } |
1629 | }; |
1630 | |
1631 | namespace |
1632 | @@ -56,6 +65,12 @@ |
1633 | |
1634 | const long WINDOW_ATTRIB_VALUE_MASK = 0; |
1635 | |
1636 | + bool Advance (Display *dpy, |
1637 | + bool waitResult) |
1638 | + { |
1639 | + return ct::AdvanceToNextEventOnSuccess (dpy, waitResult); |
1640 | + } |
1641 | + |
1642 | void MakeDock (Display *dpy, Window w) |
1643 | { |
1644 | Atom _NET_WM_WINDOW_TYPE = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE", false); |
1645 | @@ -166,16 +181,26 @@ |
1646 | XMapRaised (dpy, w2); |
1647 | |
1648 | /* Both reparented and both mapped */ |
1649 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindow (dpy, w1, ReparentNotify, -1, -1)); |
1650 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindow (dpy, w1, MapNotify, -1, -1)); |
1651 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindow (dpy, w2, ReparentNotify, -1, -1)); |
1652 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindow (dpy, w2, MapNotify, -1, -1)); |
1653 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindow (dpy,w1, ReparentNotify, -1, -1))); |
1654 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindow (dpy, w1, MapNotify, -1, -1))); |
1655 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindow (dpy, w2, ReparentNotify, -1, -1))); |
1656 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindow (dpy, w2, MapNotify, -1, -1))); |
1657 | |
1658 | PropertyNotifyXEventMatcher matcher (dpy, "_NET_CLIENT_LIST_STACKING"); |
1659 | |
1660 | /* Wait for property change notify on the root window to happen twice */ |
1661 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindowMatching (dpy, DefaultRootWindow (dpy), PropertyNotify, -1, -1, matcher)); |
1662 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindowMatching (dpy, DefaultRootWindow (dpy), PropertyNotify, -1, -1, matcher)); |
1663 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindowMatching (dpy, |
1664 | + DefaultRootWindow (dpy), |
1665 | + PropertyNotify, |
1666 | + -1, |
1667 | + -1, |
1668 | + matcher))); |
1669 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindowMatching (dpy, |
1670 | + DefaultRootWindow (dpy), |
1671 | + PropertyNotify, |
1672 | + -1, |
1673 | + -1, |
1674 | + matcher))); |
1675 | |
1676 | /* Check the client list to see that w2 > w1 */ |
1677 | std::list <Window> clientList = ct::NET_CLIENT_LIST_STACKING (dpy); |
1678 | @@ -198,12 +223,16 @@ |
1679 | /* Immediately map the dock window and clear the event queue for it */ |
1680 | XMapRaised (dpy, dock); |
1681 | |
1682 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindow (dpy, dock, ReparentNotify, -1, -1)); |
1683 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindow (dpy, dock, MapNotify, -1, -1)); |
1684 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindow (dpy, dock, ReparentNotify, -1, -1))); |
1685 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindow (dpy, dock, MapNotify, -1, -1))); |
1686 | |
1687 | /* Dock window needs to be in the client list */ |
1688 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindowMatching (dpy, DefaultRootWindow (dpy), PropertyNotify, -1, -1, matcher)); |
1689 | - |
1690 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindowMatching (dpy, |
1691 | + DefaultRootWindow (dpy), |
1692 | + PropertyNotify, |
1693 | + -1, |
1694 | + -1, |
1695 | + matcher))); |
1696 | std::list <Window> clientList = ct::NET_CLIENT_LIST_STACKING (dpy); |
1697 | ASSERT_EQ (clientList.size (), 1); |
1698 | |
1699 | @@ -216,15 +245,24 @@ |
1700 | XMapRaised (dpy, w2); |
1701 | |
1702 | /* Both reparented and both mapped */ |
1703 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindow (dpy, w1, ReparentNotify, -1, -1)); |
1704 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindow (dpy, w1, MapNotify, -1, -1)); |
1705 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindow (dpy, w2, ReparentNotify, -1, -1)); |
1706 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindow (dpy, w2, MapNotify, -1, -1)); |
1707 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindow (dpy, w1, ReparentNotify, -1, -1))); |
1708 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindow (dpy, w1, MapNotify, -1, -1))); |
1709 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindow (dpy, w2, ReparentNotify, -1, -1))); |
1710 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindow (dpy, w2, MapNotify, -1, -1))); |
1711 | |
1712 | /* Wait for property change notify on the root window to happen twice */ |
1713 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindowMatching (dpy, DefaultRootWindow (dpy), PropertyNotify, -1, -1, matcher)); |
1714 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindowMatching (dpy, DefaultRootWindow (dpy), PropertyNotify, -1, -1, matcher)); |
1715 | - |
1716 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindowMatching (dpy, |
1717 | + DefaultRootWindow (dpy), |
1718 | + PropertyNotify, |
1719 | + -1, |
1720 | + -1, |
1721 | + matcher))); |
1722 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindowMatching (dpy, |
1723 | + DefaultRootWindow (dpy), |
1724 | + PropertyNotify, |
1725 | + -1, |
1726 | + -1, |
1727 | + matcher))); |
1728 | clientList = ct::NET_CLIENT_LIST_STACKING (dpy); |
1729 | |
1730 | /* Check the client list to see that dock > w2 > w1 */ |
1731 | @@ -250,8 +288,18 @@ |
1732 | XMapRaised (dpy, w2); |
1733 | |
1734 | /* Wait for property change notify on the root window to happen twice */ |
1735 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindowMatching (dpy, DefaultRootWindow (dpy), PropertyNotify, -1, -1, matcher)); |
1736 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindowMatching (dpy, DefaultRootWindow (dpy), PropertyNotify, -1, -1, matcher)); |
1737 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindowMatching (dpy, |
1738 | + DefaultRootWindow (dpy), |
1739 | + PropertyNotify, |
1740 | + -1, |
1741 | + -1, |
1742 | + matcher))); |
1743 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindowMatching (dpy, |
1744 | + DefaultRootWindow (dpy), |
1745 | + PropertyNotify, |
1746 | + -1, |
1747 | + -1, |
1748 | + matcher))); |
1749 | |
1750 | SetUserTime (dpy, w2, 200); |
1751 | SetClientLeader (dpy, w2, w2); |
1752 | @@ -259,7 +307,7 @@ |
1753 | SetClientLeader (dpy, w3, w3); |
1754 | |
1755 | XMapRaised (dpy, w3); |
1756 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindowMatching (dpy, DefaultRootWindow (dpy), PropertyNotify, -1, -1, matcher)); |
1757 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindowMatching (dpy, DefaultRootWindow (dpy), PropertyNotify, -1, -1, matcher))); |
1758 | |
1759 | /* Check the client list to see that w2 > w3 > w1 */ |
1760 | std::list <Window> clientList = ct::NET_CLIENT_LIST_STACKING (dpy); |
1761 | @@ -284,15 +332,28 @@ |
1762 | XMapRaised (dpy, w2); |
1763 | |
1764 | /* Wait for property change notify on the root window to happen twice */ |
1765 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindowMatching (dpy, DefaultRootWindow (dpy), PropertyNotify, -1, -1, matcher)); |
1766 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindowMatching (dpy, DefaultRootWindow (dpy), PropertyNotify, -1, -1, matcher)); |
1767 | - |
1768 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindowMatching (dpy, |
1769 | + DefaultRootWindow (dpy), |
1770 | + PropertyNotify, |
1771 | + -1, |
1772 | + -1, |
1773 | + matcher))); |
1774 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindowMatching (dpy, |
1775 | + DefaultRootWindow (dpy), |
1776 | + PropertyNotify, |
1777 | + -1, |
1778 | + -1, |
1779 | + matcher))); |
1780 | SetUserTime (dpy, w3, 0); |
1781 | SetClientLeader (dpy, w3, w3); |
1782 | |
1783 | XMapRaised (dpy, w3); |
1784 | - ASSERT_TRUE (ct::WaitForEventOfTypeOnWindowMatching (dpy, DefaultRootWindow (dpy), PropertyNotify, -1, -1, matcher)); |
1785 | - |
1786 | + ASSERT_TRUE (Advance (dpy, ct::WaitForEventOfTypeOnWindowMatching (dpy, |
1787 | + DefaultRootWindow (dpy), |
1788 | + PropertyNotify, |
1789 | + -1, |
1790 | + -1, |
1791 | + matcher))); |
1792 | /* Check the client list to see that w2 > w3 > w1 */ |
1793 | std::list <Window> clientList = ct::NET_CLIENT_LIST_STACKING (dpy); |
1794 | ASSERT_EQ (clientList.size (), 3); |
PASSED: Continuous integration, rev:3501 jenkins. qa.ubuntu. com/job/ compiz- ci/287/ jenkins. qa.ubuntu. com/job/ compiz- ci/./build= pbuilder, distribution= quantal, flavor= amd64/287/ console
http://
Executed test runs:
SUCCESS: http://
Click here to trigger a rebuild: jenkins. qa.ubuntu. com/job/ compiz- ci/287/ /rebuild/?
http://