Skip to content

Commit 96a5d5b

Browse files
committed
add failing test for #14
1 parent 5863fc5 commit 96a5d5b

File tree

1 file changed

+84
-0
lines changed

1 file changed

+84
-0
lines changed

test/ChromeDevtoolsProtocol/DevtoolsClientTest.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
<?php
22
namespace ChromeDevtoolsProtocol;
33

4+
use ChromeDevtoolsProtocol\Exception\DeadlineException;
45
use ChromeDevtoolsProtocol\Exception\ErrorException;
56
use ChromeDevtoolsProtocol\Instance\Launcher;
67
use ChromeDevtoolsProtocol\Model\Network\EnableRequest;
8+
use ChromeDevtoolsProtocol\Model\Network\GetResponseBodyRequest;
9+
use ChromeDevtoolsProtocol\Model\Network\LoadingFinishedEvent;
710
use ChromeDevtoolsProtocol\Model\Page\NavigateRequest;
811
use ChromeDevtoolsProtocol\Model\Security\CertificateErrorActionEnum;
912
use ChromeDevtoolsProtocol\Model\Security\CertificateErrorEvent;
1013
use ChromeDevtoolsProtocol\Model\Security\HandleCertificateErrorRequest;
1114
use ChromeDevtoolsProtocol\Model\Security\SetOverrideCertificateErrorsRequest;
15+
use ChromeDevtoolsProtocol\WebSocket\WebSocketClient;
1216
use PHPUnit\Framework\TestCase;
17+
use Wrench\Payload\Payload;
1318

1419
class DevtoolsClientTest extends TestCase
1520
{
@@ -80,4 +85,83 @@ public function testErrorHandling()
8085
}
8186
}
8287

88+
/*
89+
* https://github.com/jakubkulhan/chrome-devtools-protocol/issues/14
90+
*/
91+
public function testConflictBetweenAwaitAndExecute()
92+
{
93+
// websocket messages
94+
$responses = [];
95+
$addResponse = function($payload) use(&$responses) {
96+
$payloadStub = $this->getMockBuilder(Payload::class)
97+
->disableOriginalConstructor()
98+
->getMock();
99+
$payloadStub->method('getPayload')->willReturn($payload);
100+
$responses[] = $payloadStub;
101+
};
102+
103+
// create a stub websocket client, which returns predefined messages
104+
$wsClient = $this->getMockBuilder(WebSocketClient::class)
105+
->disableOriginalConstructor()
106+
->getMock();
107+
$wsClient->method('receive')->willReturnCallback(function() use(&$responses) {
108+
if (!empty($responses)) {
109+
return [array_shift($responses)];
110+
}
111+
return null;
112+
});
113+
$wsClient->method('setDeadline')->willReturnCallback(function($deadline) {
114+
$timeout = floatval($deadline->format("U.u")) - microtime(true);
115+
if ($timeout < 0.0) {
116+
throw new DeadlineException("Socket deadline reached.");
117+
}
118+
});
119+
120+
// create the failing scenario
121+
$ctx = Context::withTimeout(Context::background(), 3);
122+
123+
$client = new DevtoolsClient($wsClient);
124+
register_shutdown_function(function () use ($client) { $client->close(); });
125+
126+
$addResponse('{"id":1,"result":{}}');
127+
$client->page()->enable($ctx);
128+
129+
$addResponse('{"id":2,"result":{}}');
130+
$client->network()->enable($ctx, EnableRequest::make());
131+
132+
$client->network()->addLoadingFinishedListener(function (LoadingFinishedEvent $event) use($ctx, $client, $addResponse) { // <- 2
133+
134+
// The order of these responses matters, if the loadEventFired comes first, awaitLoadEventFired (1) would wait forever
135+
$addResponse('{"method":"Page.loadEventFired","params":{"timestamp":6758.846787}}');
136+
$addResponse('{"id":4,"result":{"body":"..."}}');
137+
138+
$client->network()->getResponseBody($ctx, GetResponseBodyRequest::builder() // <- 3
139+
->setRequestId($event->requestId)
140+
->build()
141+
);
142+
});
143+
144+
$url = 'https://www.google.com';
145+
146+
$addResponse('{"id":3,"result":{"frameId":"1E56ACDD9B3B7F678F972C0EF0782649","loaderId":"AAF889CAE5B10663CA8D383A6125AC1B"}}');
147+
$client->page()->navigate($ctx, NavigateRequest::builder()->setUrl($url)->build());
148+
149+
$addResponse('{"method":"Network.loadingFinished","params":{"requestId":"AAF889CAE5B10663CA8D383A6125AC1B","timestamp":6758.623335,"encodedDataLength":67174,"shouldReportCorbBlocking":false}}');
150+
$client->page()->awaitLoadEventFired($ctx); // <- 1
151+
152+
$this->assertTrue((bool)'No conflict');
153+
154+
/*
155+
156+
1) we are waiting for the LoadEvent, but in the meanwhile the LoadingFinishedEvent arrives ->
157+
2) so the listener is called
158+
3) getResponseBody command is executed
159+
- if the response is received before LoadEvent, everything is fine
160+
- but if the LoadEvent is happening before the response is received,
161+
the LoadEvent is dropped (not buffered) and awaitLoadEventFired could never return
162+
163+
*/
164+
}
165+
166+
83167
}

0 commit comments

Comments
 (0)