-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtesting-asynchronous-qt-code-part-1.html
244 lines (218 loc) · 26.5 KB
/
testing-asynchronous-qt-code-part-1.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
<!DOCTYPE html>
<html lang="en">
<head>
<link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,700,400italic' rel='stylesheet' type='text/css'>
<link rel="stylesheet" type="text/css" href="http://projects.curiousllc.com/theme/css/style.min.css">
<link rel="stylesheet" type="text/css" href="http://projects.curiousllc.com/theme/css/pygments.min.css">
<link rel="stylesheet" type="text/css" href="http://projects.curiousllc.com/theme/css/font-awesome.min.css">
<link href="http://projects.curiousllc.com/feeds/all.atom.xml" type="application/atom+xml" rel="alternate" title="Working with hardware Atom">
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="robots" content="" />
<meta name="author" content="Ryan Day" />
<meta name="description" content="Test your asynchronous code" />
<meta name="keywords" content="python, qt, testing">
<meta property="og:site_name" content="Working with hardware"/>
<meta property="og:title" content="Testing Asynchronous Qt Code (part 1)"/>
<meta property="og:description" content="Test your asynchronous code"/>
<meta property="og:locale" content="en_US"/>
<meta property="og:url" content="http://projects.curiousllc.com/testing-asynchronous-qt-code-part-1.html"/>
<meta property="og:type" content="article"/>
<meta property="article:published_time" content="2015-06-29 05:45:00-04:00"/>
<meta property="article:modified_time" content=""/>
<meta property="article:author" content="http://projects.curiousllc.com/author/ryan-day.html">
<meta property="article:section" content="programming"/>
<meta property="article:tag" content="python"/>
<meta property="article:tag" content="qt"/>
<meta property="article:tag" content="testing"/>
<meta property="og:image" content="https://avatars3.githubusercontent.com/u/6845642?v=3&s=200"> <title>Working with hardware – Testing Asynchronous Qt Code (part 1)</title>
</head>
<body>
<aside>
<div>
<a href="http://projects.curiousllc.com">
<img src="https://avatars3.githubusercontent.com/u/6845642?v=3&s=200" alt="" title="">
</a>
<h1><a href="http://projects.curiousllc.com"></a></h1>
<p></p>
<nav>
<div style="text-align: left; font-size: 0.9em">At Curious, we connect Things to the Cloud. <br/><br/>
If you would like to launch a product, or connect your product to the Cloud, drop us a line!</div>
<ul class="list">
<li><a href="mailto:[email protected]" target="_blank">[email protected]</a></li>
<li><a href="http://curiousllc.com/" target="_blank">Curious LLC</a></li>
<li><a href="http://www.meetup.com/NOVA-Python" target="_blank">Nova Python</a></li>
<li><a href="http://www.meetup.com/Golang-DC/" target="_blank">Golang DC</a></li>
</ul>
</nav>
<ul class="social">
<li><a class="sc-You can add links in your config file" href="#" target="_blank"><i class="fa fa-You can add links in your config file"></i></a></li>
<li><a class="sc-Another social link" href="#" target="_blank"><i class="fa fa-Another social link"></i></a></li>
</ul>
</div>
</aside>
<main>
<article>
<header>
<h1 id="testing-asynchronous-qt-code-part-1">Testing Asynchronous Qt Code (part 1)</h1>
<p>
Posted on Mon 29 June 2015 in <a href="http://projects.curiousllc.com/category/programming.html">programming</a> by <a href="http://projects.curiousllc.com/author/ryan-day.html">Ryan Day</a>
• Tagged with
<a href="http://projects.curiousllc.com/tag/python.html">python</a>, <a href="http://projects.curiousllc.com/tag/qt.html">qt</a>, <a href="http://projects.curiousllc.com/tag/testing.html">testing</a>
</p>
</header>
<div>
<p><a class="reference external" href="http://projects.curiousllc.com/testing-asynchronous-qt-code-part-2.html">Continued in part 2</a></p>
<p>Asynchronous code is a little different to test than a normal code. If you are testing operations that happen in an asynchronous manner, then the results of those operations may simply not be present when your test case tries to verify them. You must have a better understanding of what is happening behind the scenes in order to test out your asynchronous code.</p>
<p>I've put together a <a class="reference external" href="https://github.com/CuriousLLC/QtTestMethods">quick repo on Github</a> with examples of a multi threaded GUI written with <a class="reference external" href="https://pypi.python.org/pypi/PySide/1.2.2">PySide</a> (a Python Qt library). This GUI will ask for your name. Once you enter it, it will say hello "Name", and add your name to a list. The list is cached using a Mock external service. This service operates outside of Qt on it's own thread.</p>
<p><strong>Qt Connection Types</strong></p>
<p>The first tricky situation is with the Submit Name functionality. You can follow along by executing the program, adding your name to the box, and clicking Submit. Your name pops up in the names list and is added to the NameManager. Here is the code path we want to test:</p>
<div class="highlight"><pre><span class="n">add_name_signal</span> <span class="o">=</span> <span class="n">Signal</span><span class="p">(</span><span class="nb">unicode</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="c"># ...</span>
<span class="bp">self</span><span class="o">.</span><span class="n">name_manager</span> <span class="o">=</span> <span class="n">NameManager</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">name_manager_thread</span> <span class="o">=</span> <span class="n">QThread</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">name_manager</span><span class="o">.</span><span class="n">moveToThread</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name_manager_thread</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">name_manager_thread</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">submit_button</span><span class="o">.</span><span class="n">clicked</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">say_hello</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_name_signal</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name_manager</span><span class="o">.</span><span class="n">store_name</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_name_signal</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">cache_name</span><span class="p">)</span>
<span class="nd">@Slot</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">say_hello</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_name_signal</span><span class="o">.</span><span class="n">emit</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name_text</span><span class="o">.</span><span class="n">text</span><span class="p">())</span>
<span class="bp">self</span><span class="o">.</span><span class="n">hello_label</span><span class="o">.</span><span class="n">setText</span><span class="p">(</span><span class="s">u"{} {}"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">GREETING</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">name_text</span><span class="o">.</span><span class="n">text</span><span class="p">()))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">name_text</span><span class="o">.</span><span class="n">setText</span><span class="p">(</span><span class="s">''</span><span class="p">)</span>
<span class="nd">@Slot</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">cache_name</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">names_list</span><span class="o">.</span><span class="n">addItem</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
</pre></div>
<p>Take a look at the testNamesList test in the testNames.py file. Run it, it will pass!</p>
<div class="highlight"><pre><span class="k">def</span> <span class="nf">testNamesList</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">name_text</span><span class="o">.</span><span class="n">setText</span><span class="p">(</span><span class="s">'Ryan'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">submit_button</span><span class="o">.</span><span class="n">click</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">name_text</span><span class="o">.</span><span class="n">setText</span><span class="p">(</span><span class="s">'Meg'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">submit_button</span><span class="o">.</span><span class="n">click</span><span class="p">()</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">names_list</span><span class="o">.</span><span class="n">count</span><span class="p">()</span> <span class="o">==</span> <span class="mi">2</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">names_list</span><span class="o">.</span><span class="n">item</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="p">()</span> <span class="o">==</span> <span class="s">'Ryan'</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">names_list</span><span class="o">.</span><span class="n">item</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="p">()</span> <span class="o">==</span> <span class="s">'Meg'</span>
</pre></div>
<p>This test doesn't look tricky, but it is. We aren't testing one condition. The name_manager.store_name connection. Let's add that to the test:</p>
<div class="highlight"><pre><span class="k">def</span> <span class="nf">testNamesList</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">name_text</span><span class="o">.</span><span class="n">setText</span><span class="p">(</span><span class="s">'Ryan'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">submit_button</span><span class="o">.</span><span class="n">click</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">name_text</span><span class="o">.</span><span class="n">setText</span><span class="p">(</span><span class="s">'Meg'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">submit_button</span><span class="o">.</span><span class="n">click</span><span class="p">()</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">names_list</span><span class="o">.</span><span class="n">count</span><span class="p">()</span> <span class="o">==</span> <span class="mi">2</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">names_list</span><span class="o">.</span><span class="n">item</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="p">()</span> <span class="o">==</span> <span class="s">'Ryan'</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">names_list</span><span class="o">.</span><span class="n">item</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="p">()</span> <span class="o">==</span> <span class="s">'Meg'</span>
<span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">name_manager</span><span class="o">.</span><span class="n">names</span><span class="p">)</span> <span class="o">==</span> <span class="mi">2</span>
</pre></div>
<p>We can see that name_manager.store_name is called just the same as the cache_name method is called. So the two should always be equal. Let's run the test now.</p>
<div class="highlight"><pre><span class="nv">$ </span>nosetests tests.testNames:QtTest.testNamesList
.
----------------------------------------------------------------------
Ran 1 <span class="nb">test </span>in 0.075s
OK
</pre></div>
<p>Great! But wait... what if we try some more times:</p>
<div class="highlight"><pre><span class="nv">$ </span>nosetests tests.testNames:QtTest.testNamesList
.
----------------------------------------------------------------------
Ran 1 <span class="nb">test </span>in 0.075s
OK
nosetests tests.testNames:QtTest.testNamesList
<span class="nv">F</span>
<span class="o">======================================================================</span>
FAIL: testNamesList <span class="o">(</span>tests.testNames.QtTest<span class="o">)</span>
----------------------------------------------------------------------
Traceback <span class="o">(</span>most recent call last<span class="o">)</span>:
File <span class="s2">"/home/curious/Development/QtTestMethods/tests/testNames.py"</span>, line 38, in testNamesList
assert len<span class="o">(</span>self.main_window.name_manager.names<span class="o">)</span> <span class="o">==</span> 2
AssertionError
----------------------------------------------------------------------
Ran 1 <span class="nb">test </span>in 0.075s
FAILED <span class="o">(</span><span class="nv">failures</span><span class="o">=</span>1<span class="o">)</span>
</pre></div>
<p>Ok, so this fails <em>sometimes</em> for the name_manager.names length check, but never for the names_list.count check. What is actually happening here?</p>
<p>The problem lies with connecting the add_name_signal to the handlers.</p>
<div class="highlight"><pre><span class="c"># This is what the code says</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_name_signal</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name_manager</span><span class="o">.</span><span class="n">store_name</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_name_signal</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">cache_name</span><span class="p">)</span>
<span class="c"># This is what the code is actually doing</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_name_signal</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name_manager</span><span class="o">.</span><span class="n">store_name</span><span class="p">,</span> <span class="n">Qt</span><span class="o">.</span><span class="n">AutoConnection</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_name_signal</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">cache_name</span><span class="p">,</span> <span class="n">Qt</span><span class="o">.</span><span class="n">AutoConnection</span><span class="p">)</span>
<span class="c"># Which really means that the code is doing</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_name_signal</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">name_manager</span><span class="o">.</span><span class="n">store_name</span><span class="p">,</span> <span class="n">Qt</span><span class="o">.</span><span class="n">QueuedConnection</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">add_name_signal</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">cache_name</span><span class="p">,</span> <span class="n">Qt</span><span class="o">.</span><span class="n">DirectConnection</span><span class="p">)</span>
</pre></div>
<p>By not specifying the type of connection, we are using a <strong>Qt.DirectConnection</strong> for the <em>cache_name</em> method, and a <strong>Qt.QueuedConnection</strong> for the <em>name_manager.store_name</em> method.</p>
<p>To understand this, we turn to the Qt manual for <a class="reference external" href="http://doc.qt.io/qt-4.8/qt.html#ConnectionType-enum">Signal Connection Types</a>. Since our code doesn't specify a connection type, we are using the default <strong>Qt.AutoConnection</strong>. Since the name_manager runs in its own QThread, that connection becomes a <strong>Qt.QueuedConnection</strong>. Since cache_name runs in the current thread, it becomes a <strong>Qt.DirectConnection</strong>.</p>
<p><em>Qt.DirectConnections*</em> are run synchronously. You can try this out yourself. In Pycharm try stepping through the code. <strong>Qt.QueuedConnections</strong> are run when the target Qt thread's event loop gets a chance to run them.</p>
<p>The reason our test fails intermittently is because the events for the <strong>name_manager</strong> thread are <em>sometimes</em> processed before our test checks the result. The rest of the time they are not processed. To avoid having race conditions in our tests, we need to wait for the appropriate result to become available.</p>
<div class="highlight"><pre><span class="k">def</span> <span class="nf">wait_for</span><span class="p">(</span><span class="n">cond</span><span class="p">,</span> <span class="n">to</span><span class="p">):</span>
<span class="sd">"""</span>
<span class="sd"> Wait for the condition function to return True until the timeout is met.</span>
<span class="sd"> The timeout is broken into pieces to make sure the application can continue</span>
<span class="sd"> processing without blocking functionality</span>
<span class="sd"> :param cond: A function that returns a boolean</span>
<span class="sd"> :param to: Timeout in seconds</span>
<span class="sd"> :return: The result of the condition function</span>
<span class="sd"> """</span>
<span class="n">watchdog</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">msecs</span> <span class="o">=</span> <span class="p">(</span><span class="n">to</span> <span class="o">/</span> <span class="mf">8.</span><span class="p">)</span> <span class="o">*</span> <span class="mi">1000</span>
<span class="k">while</span> <span class="n">cond</span><span class="p">()</span> <span class="ow">is</span> <span class="bp">False</span> <span class="ow">and</span> <span class="n">watchdog</span> <span class="o"><</span> <span class="mi">8</span><span class="p">:</span>
<span class="n">QThread</span><span class="o">.</span><span class="n">msleep</span><span class="p">(</span><span class="n">msecs</span><span class="p">)</span>
<span class="n">watchdog</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">return</span> <span class="n">cond</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">testNamesList</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">done</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">name_manager_thread</span><span class="o">.</span><span class="n">quit</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">name_text</span><span class="o">.</span><span class="n">setText</span><span class="p">(</span><span class="s">'Ryan'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">submit_button</span><span class="o">.</span><span class="n">click</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">name_text</span><span class="o">.</span><span class="n">setText</span><span class="p">(</span><span class="s">'Meg'</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">submit_button</span><span class="o">.</span><span class="n">click</span><span class="p">()</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">names_list</span><span class="o">.</span><span class="n">count</span><span class="p">()</span> <span class="o">==</span> <span class="mi">2</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">names_list</span><span class="o">.</span><span class="n">item</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="p">()</span> <span class="o">==</span> <span class="s">'Ryan'</span>
<span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">names_list</span><span class="o">.</span><span class="n">item</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">text</span><span class="p">()</span> <span class="o">==</span> <span class="s">'Meg'</span>
<span class="k">assert</span> <span class="n">wait_for</span><span class="p">(</span><span class="k">lambda</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">main_window</span><span class="o">.</span><span class="n">name_manager</span><span class="o">.</span><span class="n">names</span><span class="p">)</span> <span class="o">==</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="ow">is</span> <span class="bp">True</span>
</pre></div>
<p>This <em>wait_for</em> function will allow us to wait for a condition to become True. The function will timeout after a period of time of course. This lets us update our test. If we run this new test over and over, it will always return True.</p>
</div>
<div class="tag-cloud">
<p>
<a href="http://projects.curiousllc.com/tag/python.html">python</a>
<a href="http://projects.curiousllc.com/tag/qt.html">qt</a>
<a href="http://projects.curiousllc.com/tag/testing.html">testing</a>
</p>
</div>
</article>
<footer>
<p>© Ryan Day </p>
<p>Built using <a href="http://getpelican.com" target="_blank">Pelican</a> - <a href="https://github.com/alexandrevicenzi/flex" target="_blank">Flex</a> theme by <a href="http://alexandrevicenzi.com" target="_blank">Alexandre Vicenzi</a></p> </footer>
</main>
<script type="text/javascript">
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-2393416-13', 'auto');
ga('send', 'pageview');
</script>
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "BlogPosting",
"name": "Testing Asynchronous Qt Code (part 1)",
"headline": "Testing Asynchronous Qt Code (part 1)",
"datePublished": "2015-06-29 05:45:00-04:00",
"dateModified": "",
"author": {
"@type": "Person",
"name": "Ryan Day",
"url": "http://projects.curiousllc.com/author/ryan-day.html"
},
"image": "https://avatars3.githubusercontent.com/u/6845642?v=3&s=200",
"url": "http://projects.curiousllc.com/testing-asynchronous-qt-code-part-1.html",
"description": "Test your asynchronous code"
}
</script></body>
</html>