Merge lp:~compiz-team/compiz/compiz.fix_1085591 into lp:compiz/0.9.9

Proposed by Sam Spilsbury
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
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 SubstructureRedirectMask.

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 SubstructureRedirectMask
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 SubstructureRedirectMask and attempting to take it results in an X error. It wasn't doing that before, and instead checked for X errors much earlier on (in error, because an X error up there wouldn't indicate that another WM was running at all).

In order to make this work properly, some changes had to be made to compiz. This adds a new runtime switch --send-startup-message which simply posts a message to all clients saying that the startup procedure has finished (and either succeeded or failed). Its needed because the location of when we take the server grab was changed to a much smaller critical section of the code, and that doesn't include the property change on the selection window. This is a far more accurate way of knowing that we've started, because we send it /after/ the startup procedure is done (rather than relying on the server to do the right thing.

Also fixed race condition caused by destroying the WMSnSelectionWindow before shutdown.

 Because we haven't changed our active event mask
 to remove SubstructureRedirectMask, other ICCCM
 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 SubstructureRedirectMask. That will cause them
 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 SubstructureRedirectMask.

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 SubstructureRedirectMask
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 SubstructureRedirectMask and attempting to take it results in an X error. It wasn't doing that before, and instead checked for X errors much earlier on (in error, because an X error up there wouldn't indicate that another WM was running at all).

In order to make this work properly, some changes had to be made to compiz. This adds a new runtime switch --send-startup-message which simply posts a message to all clients saying that the startup procedure has finished (and either succeeded or failed). Its needed because the location of when we take the server grab was changed to a much smaller critical section of the code, and that doesn't include the property change on the selection window. This is a far more accurate way of knowing that we've started, because we send it /after/ the startup procedure is done (rather than relying on the server to do the right thing.

(LP: #1085591)

Also fixed race condition caused by destroying the WMSnSelectionWindow before shutdown.

 Because we haven't changed our active event mask
 to remove SubstructureRedirectMask, other ICCCM
 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 SubstructureRedirectMask. That will cause them
 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.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
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

review: Needs Fixing
Revision history for this message
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 SubstructureRedirectMask, and we
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://code.launchpad.net/~compiz-team/compiz/compiz.fix_1085591/+merge/137524
> Your team Compiz Maintainers is subscribed to branch lp:compiz.
>

Revision history for this message
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 SubstructureRedirectMask, and we
> 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://code.launchpad.net/~compiz-team/compiz/compiz.fix_1085591/+merge/137524
>> Your team Compiz Maintainers is subscribed to branch lp:compiz.
>>
>

Revision history for this message
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/.compiz-1/plugins/libcore.so
./bin/compiz (core) - Debug: dlopen failed: /home/dan/.compiz-1/plugins/libcore.so: cannot open shared object file: No such file or directory
./bin/compiz (core) - Debug: Trying to load core from: /home/dan/tmp.591/lib/compiz/libcore.so
./bin/compiz (core) - Debug: dlopen failed: /home/dan/tmp.591/lib/compiz/libcore.so: cannot open shared object file: No such file or directory
./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_ChangeWindowAttributes)
  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_ChangeWindowAttributes)
  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

Revision history for this message
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)

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Seems to work now. Bug fixed.

Just a note on pthread creation in AsyncTask::AsyncTask... It's usually a bad 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.

review: Approve
Revision history for this message
Sam Spilsbury (smspillaz) wrote :

> Seems to work now. Bug fixed.
>
> Just a note on pthread creation in AsyncTask::AsyncTask... It's usually a bad
> 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)

Revision history for this message
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.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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);

Subscribers

People subscribed via source and target branches