Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
1 | <?php |
||
18 | class AppControllerTest extends RestTestCase |
||
19 | { |
||
20 | /** |
||
21 | * @const complete content type string expected on a resouce |
||
22 | */ |
||
23 | const CONTENT_TYPE = 'application/json; charset=UTF-8; profile=http://localhost/schema/core/app/item'; |
||
24 | |||
25 | /** |
||
26 | * @const corresponding vendorized schema mime type |
||
27 | */ |
||
28 | const COLLECTION_TYPE = 'application/json; charset=UTF-8; profile=http://localhost/schema/core/app/collection'; |
||
29 | |||
30 | /** |
||
31 | * setup client and load fixtures |
||
32 | * |
||
33 | * @return void |
||
34 | */ |
||
35 | public function setUp() |
||
49 | /** |
||
50 | * check if all fixtures are returned on GET |
||
51 | * |
||
52 | * @return void |
||
53 | */ |
||
54 | public function testFindAll() |
||
81 | |||
82 | /** |
||
83 | * test if we can get list of apps, paged and with filters.. |
||
84 | * |
||
85 | * @return void |
||
86 | */ |
||
87 | public function testGetAppWithFilteringAndPaging() |
||
88 | { |
||
89 | $client = static::createRestClient(); |
||
90 | $_SERVER['QUERY_STRING'] = 'eq(showInMenu,true)&limit(1)'; |
||
91 | $client->request('GET', '/core/app/?eq(showInMenu,true)&limit(1)'); |
||
92 | unset($_SERVER['QUERY_STRING']); |
||
93 | $response = $client->getResponse(); |
||
94 | |||
95 | $this->assertEquals(1, count($client->getResults())); |
||
96 | |||
97 | $this->assertContains( |
||
98 | '<http://localhost/core/app/?eq(showInMenu%2Ctrue)&limit(1)>; rel="self"', |
||
99 | $response->headers->get('Link') |
||
100 | ); |
||
101 | |||
102 | $this->assertContains( |
||
103 | '<http://localhost/core/app/?eq(showInMenu%2Ctrue)&limit(1%2C1)>; rel="next"', |
||
104 | $response->headers->get('Link') |
||
105 | ); |
||
106 | |||
107 | $this->assertContains( |
||
108 | '<http://localhost/core/app/?eq(showInMenu%2Ctrue)&limit(1%2C1)>; rel="last"', |
||
109 | $response->headers->get('Link') |
||
110 | ); |
||
111 | } |
||
112 | |||
113 | /** |
||
114 | * rql limit() should *never* be overwritten by default value |
||
115 | * |
||
116 | * @return void |
||
117 | */ |
||
118 | public function testGetAppPagingWithRql() |
||
119 | { |
||
120 | // does limit work? |
||
121 | $client = static::createRestClient(); |
||
122 | $client->request('GET', '/core/app/?limit(1)'); |
||
123 | $this->assertEquals(1, count($client->getResults())); |
||
124 | |||
125 | $response = $client->getResponse(); |
||
126 | |||
127 | $this->assertContains( |
||
128 | '<http://localhost/core/app/?limit(1)>; rel="self"', |
||
129 | $response->headers->get('Link') |
||
130 | ); |
||
131 | |||
132 | $this->assertContains( |
||
133 | '<http://localhost/core/app/?limit(1%2C1)>; rel="next"', |
||
134 | $response->headers->get('Link') |
||
135 | ); |
||
136 | |||
137 | $this->assertContains( |
||
138 | '<http://localhost/core/app/?limit(1%2C1)>; rel="last"', |
||
139 | $response->headers->get('Link') |
||
140 | ); |
||
141 | |||
142 | $this->assertSame('2', $response->headers->get('X-Total-Count')); |
||
143 | |||
144 | /*** pagination tests **/ |
||
145 | $client = static::createRestClient(); |
||
146 | $client->request('GET', '/core/app/?limit(1,1)'); |
||
147 | $this->assertEquals(1, count($client->getResults())); |
||
148 | |||
149 | $response = $client->getResponse(); |
||
150 | |||
151 | $this->assertContains( |
||
152 | '<http://localhost/core/app/?limit(1%2C1)>; rel="self"', |
||
153 | $response->headers->get('Link') |
||
154 | ); |
||
155 | |||
156 | $this->assertContains( |
||
157 | '<http://localhost/core/app/?limit(1%2C0)>; rel="prev"', |
||
158 | $response->headers->get('Link') |
||
159 | ); |
||
160 | |||
161 | // we're on the 'last' page - so 'last' should not be in in Link header |
||
162 | $this->assertNotContains( |
||
163 | 'rel="last"', |
||
164 | $response->headers->get('Link') |
||
165 | ); |
||
166 | |||
167 | $this->assertSame('2', $response->headers->get('X-Total-Count')); |
||
168 | } |
||
169 | |||
170 | /** |
||
171 | * check for a client error if invalid limit value is provided |
||
172 | * |
||
173 | * @dataProvider invalidPagingPageSizeProvider |
||
174 | * |
||
175 | * @param integer $limit limit value that should fail |
||
176 | * @return void |
||
177 | */ |
||
178 | public function testInvalidPagingPageSize($limit) |
||
179 | { |
||
180 | $client = static::createRestClient(); |
||
181 | $client->request('GET', sprintf('/core/app/?limit(%s)', $limit)); |
||
182 | |||
183 | $this->assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode()); |
||
184 | $this->assertContains('negative or null limit in rql', $client->getResults()->message); |
||
185 | } |
||
186 | |||
187 | /** |
||
188 | * page size test provides |
||
189 | * |
||
190 | * @return array[] |
||
|
|||
191 | */ |
||
192 | public function invalidPagingPageSizeProvider() |
||
193 | { |
||
194 | return [ |
||
195 | [0], |
||
196 | [-1], |
||
197 | ]; |
||
198 | } |
||
199 | |||
200 | /** |
||
201 | * RQL is parsed only when we get apps |
||
202 | * |
||
203 | * @return void |
||
204 | */ |
||
205 | public function testRqlIsParsedOnlyOnGetRequest() |
||
206 | { |
||
207 | $appData = [ |
||
208 | 'showInMenu' => false, |
||
209 | 'order' => 100, |
||
210 | 'name' => ['en' => 'Administration'], |
||
211 | ]; |
||
212 | |||
213 | $client = static::createRestClient(); |
||
214 | $client->request('GET', '/core/app/?invalidrqlquery'); |
||
215 | $this->assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode()); |
||
216 | $this->assertContains('syntax error in rql', $client->getResults()->message); |
||
217 | |||
218 | $client = static::createRestClient(); |
||
219 | $client->request('GET', '/core/app/admin?invalidrqlquery'); |
||
220 | $this->assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode()); |
||
221 | $this->assertContains('syntax error in rql', $client->getResults()->message); |
||
222 | |||
223 | $client = static::createRestClient(); |
||
224 | $client->request('OPTIONS', '/core/app/?invalidrqlquery'); |
||
225 | $this->assertEquals(Response::HTTP_NO_CONTENT, $client->getResponse()->getStatusCode()); |
||
226 | |||
227 | $client = static::createRestClient(); |
||
228 | $client->request('OPTIONS', '/schema/core/app/collection?invalidrqlquery'); |
||
229 | $this->assertEquals(Response::HTTP_NO_CONTENT, $client->getResponse()->getStatusCode()); |
||
230 | |||
231 | $client = static::createRestClient(); |
||
232 | $client->request('OPTIONS', '/schema/core/app/item?invalidrqlquery'); |
||
233 | $this->assertEquals(Response::HTTP_NO_CONTENT, $client->getResponse()->getStatusCode()); |
||
234 | |||
235 | $client = static::createRestClient(); |
||
236 | $client->request('GET', '/schema/core/app/collection?invalidrqlquery'); |
||
237 | $this->assertEquals(Response::HTTP_OK, $client->getResponse()->getStatusCode()); |
||
238 | |||
239 | $client = static::createRestClient(); |
||
240 | $client->request('GET', '/schema/core/app/item?invalidrqlquery'); |
||
241 | $this->assertEquals(Response::HTTP_OK, $client->getResponse()->getStatusCode()); |
||
242 | |||
243 | |||
244 | $client = static::createRestClient(); |
||
245 | $client->post('/core/app/?invalidrqlquery', $appData); |
||
246 | $this->assertEquals(Response::HTTP_CREATED, $client->getResponse()->getStatusCode()); |
||
247 | |||
248 | $client = static::createRestClient(); |
||
249 | $client->put('/core/app/admin?invalidrqlquery', $appData); |
||
250 | $this->assertEquals(Response::HTTP_NO_CONTENT, $client->getResponse()->getStatusCode()); |
||
251 | |||
252 | $client = static::createRestClient(); |
||
253 | $client->request('DELETE', '/core/app/admin?invalidrqlquery'); |
||
254 | $this->assertEquals(Response::HTTP_NO_CONTENT, $client->getResponse()->getStatusCode()); |
||
255 | } |
||
256 | |||
257 | /** |
||
258 | * Test only RQL select() operator is allowed for GET one |
||
259 | * |
||
260 | * @return void |
||
261 | * @group tmp |
||
262 | */ |
||
263 | public function testOnlyRqlSelectIsAllowedOnGetOne() |
||
264 | { |
||
265 | $client = static::createRestClient(); |
||
266 | $client->request('GET', '/core/app/?select(id)'); |
||
267 | $this->assertEquals(Response::HTTP_OK, $client->getResponse()->getStatusCode()); |
||
268 | |||
269 | $client = static::createRestClient(); |
||
270 | $client->request('GET', '/core/app/admin?select(id)'); |
||
271 | $this->assertEquals(Response::HTTP_OK, $client->getResponse()->getStatusCode()); |
||
272 | |||
273 | foreach ([ |
||
274 | 'limit' => 'limit(1)', |
||
275 | 'sort' => 'sort(+id)', |
||
276 | 'eq' => 'eq(id,a)', |
||
277 | ] as $extraRqlOperator => $extraRqlOperatorQuery) { |
||
278 | $client = static::createRestClient(); |
||
279 | $client->request('GET', '/core/app/?select(id)&'.$extraRqlOperatorQuery); |
||
280 | $this->assertEquals(Response::HTTP_OK, $client->getResponse()->getStatusCode()); |
||
281 | |||
282 | $client = static::createRestClient(); |
||
283 | $client->request('GET', '/core/app/admin?select(id)&'.$extraRqlOperatorQuery); |
||
284 | $this->assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode()); |
||
285 | $this->assertEquals( |
||
286 | sprintf('RQL operator "%s" is not allowed for this request', $extraRqlOperator), |
||
287 | $client->getResults()->message |
||
288 | ); |
||
289 | } |
||
290 | } |
||
291 | |||
292 | /** |
||
293 | * check for empty collections when no fixtures are loaded |
||
294 | * |
||
295 | * @return void |
||
296 | */ |
||
297 | View Code Duplication | public function testFindAllEmptyCollection() |
|
298 | { |
||
299 | // reset fixtures since we already have some from setUp |
||
300 | $this->loadFixtures(array(), null, 'doctrine_mongodb'); |
||
301 | $client = static::createRestClient(); |
||
302 | $client->request('GET', '/core/app/'); |
||
303 | |||
304 | $response = $client->getResponse(); |
||
305 | $results = $client->getResults(); |
||
306 | |||
307 | $this->assertResponseContentType(self::COLLECTION_TYPE, $response); |
||
308 | |||
309 | $this->assertEquals(array(), $results); |
||
310 | } |
||
311 | |||
312 | /** |
||
313 | * test if we can get an app by id |
||
314 | * |
||
315 | * @return void |
||
316 | */ |
||
317 | public function testGetApp() |
||
318 | { |
||
319 | $client = static::createRestClient(); |
||
320 | $client->request('GET', '/core/app/admin'); |
||
321 | $response = $client->getResponse(); |
||
322 | $results = $client->getResults(); |
||
323 | |||
324 | $this->assertResponseContentType(self::CONTENT_TYPE, $response); |
||
325 | |||
326 | $this->assertEquals('admin', $results->id); |
||
327 | $this->assertEquals('Administration', $results->name->en); |
||
328 | $this->assertEquals(true, $results->showInMenu); |
||
329 | |||
330 | $this->assertContains( |
||
331 | '<http://localhost/core/app/admin>; rel="self"', |
||
332 | $response->headers->get('Link') |
||
333 | ); |
||
334 | $this->assertEquals('*', $response->headers->get('Access-Control-Allow-Origin')); |
||
335 | } |
||
336 | |||
337 | /** |
||
338 | * test if we can create an app through POST |
||
339 | * |
||
340 | * @return void |
||
341 | */ |
||
342 | public function testPostApp() |
||
343 | { |
||
344 | $testApp = new \stdClass; |
||
345 | $testApp->name = new \stdClass; |
||
346 | $testApp->name->en = 'new Test App'; |
||
347 | $testApp->showInMenu = true; |
||
348 | |||
349 | $client = static::createRestClient(); |
||
350 | $client->post('/core/app/', $testApp); |
||
351 | $response = $client->getResponse(); |
||
352 | $results = $client->getResults(); |
||
353 | |||
354 | // we sent a location header so we don't want a body |
||
355 | $this->assertNull($results); |
||
356 | $this->assertContains('/core/app/', $response->headers->get('Location')); |
||
357 | |||
358 | $client = static::createRestClient(); |
||
359 | $client->request('GET', $response->headers->get('Location')); |
||
360 | $response = $client->getResponse(); |
||
361 | $results = $client->getResults(); |
||
362 | |||
363 | $this->assertResponseContentType(self::CONTENT_TYPE, $response); |
||
364 | $this->assertEquals('new Test App', $results->name->en); |
||
365 | $this->assertTrue($results->showInMenu); |
||
366 | $this->assertContains( |
||
367 | '<http://localhost/core/app/'.$results->id.'>; rel="self"', |
||
368 | explode(',', $response->headers->get('Link')) |
||
369 | ); |
||
370 | } |
||
371 | |||
372 | /** |
||
373 | * test if we get a correct return if we post empty. |
||
374 | * |
||
375 | * @return void |
||
376 | */ |
||
377 | public function testPostEmptyApp() |
||
378 | { |
||
379 | $client = static::createRestClient(); |
||
380 | |||
381 | // send nothing really.. |
||
382 | $client->post('/core/app/', "", array(), array(), array(), false); |
||
383 | |||
384 | $response = $client->getResponse(); |
||
385 | |||
386 | $this->assertContains( |
||
387 | 'No input data', |
||
388 | $response->getContent() |
||
389 | ); |
||
390 | |||
391 | $this->assertEquals(400, $response->getStatusCode()); |
||
392 | } |
||
393 | |||
394 | /** |
||
395 | * test if we get a correct return if we post empty. |
||
396 | * |
||
397 | * @return void |
||
398 | */ |
||
399 | public function testPostNonObjectApp() |
||
400 | { |
||
401 | $client = static::createRestClient(); |
||
402 | $client->post('/core/app/', "non-object value"); |
||
403 | |||
404 | $response = $client->getResponse(); |
||
405 | $this->assertContains('JSON request body must be an object', $response->getContent()); |
||
406 | $this->assertEquals(400, $response->getStatusCode()); |
||
407 | } |
||
408 | |||
409 | /** |
||
410 | * test if 500 error is reported when posting an malformed input |
||
411 | * |
||
412 | * @return void |
||
413 | */ |
||
414 | public function testPostMalformedApp() |
||
415 | { |
||
416 | $testApp = new \stdClass; |
||
417 | $testApp->name = new \stdClass; |
||
418 | $testApp->name->en = 'new Test App'; |
||
419 | $testApp->showInMenu = true; |
||
420 | |||
421 | // malform it ;-) |
||
422 | $input = str_replace(":", ";", json_encode($testApp)); |
||
423 | |||
424 | $client = static::createRestClient(); |
||
425 | |||
426 | // make sure this is sent as 'raw' input (not json_encoded again) |
||
427 | $client->post('/core/app/', $input, array(), array(), array(), false); |
||
428 | |||
429 | $response = $client->getResponse(); |
||
430 | |||
431 | // Check that error message contains detailed reason |
||
432 | json_decode($input); |
||
433 | $lastJsonError = function_exists('json_last_error_msg') |
||
434 | ? json_last_error_msg() |
||
435 | : 'Unable to decode JSON string'; |
||
436 | $this->assertContains( |
||
437 | $lastJsonError, |
||
438 | $client->getResults()->message |
||
439 | ); |
||
440 | |||
441 | $this->assertEquals(400, $response->getStatusCode()); |
||
442 | } |
||
443 | |||
444 | /** |
||
445 | * Tests if an error is returned when an id is send in a post |
||
446 | * |
||
447 | * @return void |
||
448 | */ |
||
449 | public function testPostWithId() |
||
450 | { |
||
451 | $helloApp = new \stdClass(); |
||
452 | $helloApp->id = 101; |
||
453 | $helloApp->name = "tubel"; |
||
454 | |||
455 | $client = static::createRestClient(); |
||
456 | $client->post('/person/customer', $helloApp); |
||
457 | |||
458 | $this->assertEquals( |
||
459 | 'Bad Request - "id" can not be given on a POST request. '. |
||
460 | 'Do a PUT request instead to update an existing record.', |
||
461 | $client->getResults()->message |
||
462 | ); |
||
463 | } |
||
464 | /** |
||
465 | * test updating apps |
||
466 | * |
||
467 | * @return void |
||
468 | */ |
||
469 | public function testPutApp() |
||
470 | { |
||
471 | $helloApp = new \stdClass(); |
||
472 | $helloApp->id = "tablet"; |
||
473 | $helloApp->name = new \stdClass(); |
||
474 | $helloApp->name->en = "Tablet"; |
||
475 | $helloApp->showInMenu = false; |
||
476 | |||
477 | $client = static::createRestClient(); |
||
478 | $client->put('/core/app/tablet', $helloApp); |
||
479 | |||
480 | $this->assertNull($client->getResults()); |
||
481 | $this->assertNull($client->getResponse()->headers->get('Location')); |
||
482 | |||
483 | $client = static::createRestClient(); |
||
484 | $client->request('GET', '/core/app/tablet'); |
||
485 | $response = $client->getResponse(); |
||
486 | $results = $client->getResults(); |
||
487 | |||
488 | $this->assertResponseContentType(self::CONTENT_TYPE, $response); |
||
489 | $this->assertEquals('Tablet', $results->name->en); |
||
490 | $this->assertFalse($results->showInMenu); |
||
491 | $this->assertContains( |
||
492 | '<http://localhost/core/app/tablet>; rel="self"', |
||
493 | explode(',', $response->headers->get('Link')) |
||
494 | ); |
||
495 | } |
||
496 | |||
497 | /** |
||
498 | * Test for PATCH Request |
||
499 | * |
||
500 | * @return void |
||
501 | */ |
||
502 | public function testPatchAppRequestApplyChanges() |
||
503 | { |
||
504 | $helloApp = new \stdClass(); |
||
505 | $helloApp->id = "testapp"; |
||
506 | $helloApp->name = new \stdClass(); |
||
507 | $helloApp->name->en = "Test App"; |
||
508 | $helloApp->showInMenu = false; |
||
509 | |||
510 | // 1. Create some App |
||
511 | $client = static::createRestClient(); |
||
512 | $client->put('/core/app/' . $helloApp->id, $helloApp); |
||
513 | |||
514 | // 2. PATCH request |
||
515 | $client = static::createRestClient(); |
||
516 | $patchJson = json_encode( |
||
517 | [ |
||
518 | [ |
||
519 | 'op' => 'replace', |
||
520 | 'path' => '/name/en', |
||
521 | 'value' => 'Test App Patched' |
||
522 | ] |
||
523 | ] |
||
524 | ); |
||
525 | $client->request('PATCH', '/core/app/' . $helloApp->id, array(), array(), array(), $patchJson); |
||
526 | $response = $client->getResponse(); |
||
527 | |||
528 | $this->assertEquals(200, $response->getStatusCode()); |
||
529 | |||
530 | // 3. Get changed App and check changed title |
||
531 | $client = static::createRestClient(); |
||
532 | $client->request('GET', '/core/app/' . $helloApp->id); |
||
533 | |||
534 | $response = $client->getResponse(); |
||
535 | $results = $client->getResults(); |
||
536 | |||
537 | $this->assertResponseContentType(self::CONTENT_TYPE, $response); |
||
538 | $this->assertEquals('Test App Patched', $results->name->en); |
||
539 | } |
||
540 | |||
541 | /** |
||
542 | * Test for Malformed PATCH Request |
||
543 | * |
||
544 | * @return void |
||
545 | */ |
||
546 | public function testMalformedPatchAppRequest() |
||
575 | |||
576 | /** |
||
577 | * Try to update an app with a non matching ID in GET and req body |
||
578 | * |
||
579 | * @return void |
||
580 | */ |
||
581 | public function testNonMatchingIdPutApp() |
||
601 | |||
602 | /** |
||
603 | * We had an issue when PUTing without ID would create a new record. |
||
604 | * This test ensures that we don't do that, instead we should apply the ID from the GET req. |
||
605 | * |
||
606 | * @return void |
||
607 | */ |
||
608 | public function testPutAppNoIdInPayload() |
||
629 | |||
630 | /** |
||
631 | * test updating an inexistant document (upsert) |
||
632 | * |
||
633 | * @return void |
||
634 | */ |
||
635 | public function testUpsertApp() |
||
648 | |||
649 | /** |
||
650 | * test deleting an app |
||
651 | * |
||
652 | * @return void |
||
653 | */ |
||
654 | public function testDeleteApp() |
||
674 | |||
675 | /** |
||
676 | * test failing validation on boolean field |
||
677 | * |
||
678 | * @return void |
||
679 | */ |
||
680 | public function testFailingBooleanValidationOnAppUpdate() |
||
698 | |||
699 | /** |
||
700 | * test getting schema information |
||
701 | * |
||
702 | * @return void |
||
703 | */ |
||
704 | View Code Duplication | public function testGetAppSchemaInformation() |
|
713 | |||
714 | /** |
||
715 | * requests on OPTIONS and HEAD shall not lead graviton to get any data from mongodb. |
||
716 | * if we page limit(1) this will lead to presence of the x-total-count header if |
||
717 | * data is generated (asserted by testGetAppPagingWithRql()). thus, if we don't |
||
718 | * have this header, we can safely assume that no data has been processed in RestController. |
||
719 | * |
||
720 | * @return void |
||
721 | */ |
||
722 | View Code Duplication | public function testNoRecordsAreGeneratedOnPreRequests() |
|
734 | |||
735 | /** |
||
736 | * test getting schema information from canonical url |
||
737 | * |
||
738 | * @return void |
||
739 | */ |
||
740 | View Code Duplication | public function testGetAppSchemaInformationCanonical() |
|
748 | |||
749 | /** |
||
750 | * test getting collection schema |
||
751 | * |
||
752 | * @return void |
||
753 | */ |
||
754 | public function testGetAppCollectionSchemaInformation() |
||
782 | |||
783 | /** |
||
784 | * Test for searchable translations |
||
785 | * |
||
786 | * @dataProvider searchableTranslationDataProvider |
||
787 | * |
||
788 | * @param string $expr expression |
||
789 | * @param int $expCount count |
||
790 | * |
||
791 | * @return void |
||
792 | */ |
||
793 | public function testSearchableTranslations($expr, $expCount) |
||
807 | |||
808 | /** |
||
809 | * data provider for searchable translations |
||
810 | * |
||
811 | * @return array data |
||
812 | */ |
||
813 | public function searchableTranslationDataProvider() |
||
824 | |||
825 | /** |
||
826 | * ensure we have nice parse error output in rql parse failure |
||
827 | * |
||
828 | * @return void |
||
829 | */ |
||
830 | View Code Duplication | public function testRqlSyntaxError() |
|
844 | |||
845 | /** |
||
846 | * check if response looks like schema |
||
847 | * |
||
848 | * @param object $response response |
||
849 | * |
||
850 | * @return void |
||
851 | */ |
||
852 | private function assertIsSchemaResponse($response) |
||
857 | |||
858 | /** |
||
859 | * check if a schema is of the app type |
||
860 | * |
||
861 | * @param \stdClass $schema schema from service to validate |
||
862 | * |
||
863 | * @return void |
||
864 | */ |
||
865 | private function assertIsAppSchema(\stdClass $schema) |
||
890 | } |
||
891 |
This check looks for the generic type
array
as a return type and suggests a more specific type. This type is inferred from the actual code.