├── .gitignore
├── .hgignore
├── .travis.yml
├── LICENSE.txt
├── README.md
├── build.local.xml
├── build.properties
├── build.xml
├── composer.json
├── package.xml
├── phpunit.xml
└── src
├── .empty
├── README.txt
├── bin
└── .empty
├── data
└── .empty
├── docs
└── .empty
├── php
├── .empty
└── Phix_Project
│ └── ContractLib2
│ ├── Contract.php
│ ├── ContractInvariant.php
│ ├── E5xx
│ └── ContractFailedException.php
│ └── OldValues.php
├── tests
├── .empty
├── functional-tests
│ └── .empty
├── integration-tests
│ └── .empty
└── unit-tests
│ ├── .empty
│ ├── bin
│ └── .empty
│ ├── bootstrap.php
│ ├── php
│ ├── .empty
│ └── Phix_Project
│ │ └── ContractLib2
│ │ ├── ContractTest.php
│ │ └── OldValuesTest.php
│ └── www
│ └── .empty
└── www
└── .empty
/.gitignore:
--------------------------------------------------------------------------------
1 | .build
2 | composer.phar
3 | dist
4 | .tmp
5 | nbproject
6 | review
7 | vendor
8 |
--------------------------------------------------------------------------------
/.hgignore:
--------------------------------------------------------------------------------
1 | syntax: glob
2 |
3 | .build
4 | .dist
5 | nbproject
6 | review
7 | tmp
8 | vendor
9 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | php:
3 | - 5.3
4 | - 5.4
5 | before_script:
6 | - composer install --dev
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | New BSD License
2 | ===============
3 |
4 | Copyright (c) 2011, Stuart Herbert
5 | All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without
8 | modification, are permitted provided that the following conditions are met:
9 |
10 | * Redistributions of source code must retain the above copyright notice,
11 | this list of conditions and the following disclaimer.
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 | * Neither the names of the copyright holders nor the names of its
16 | contributors may be used to endorse or promote products derived from this
17 | software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 | POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ContractLib
2 | ===========
3 |
4 | **ContractLib** is a simple-to-use PHP component for easily enforcing programming contracts throughout your PHP components. These programming contracts can go a long way to helping you, and the users of your components, develop more robust code.
5 |
6 | ContractLib is loosely inspired by Microsoft Research's work on the
7 | [Code Contracts Library for .NET](http://research.microsoft.com/en-us/projects/contracts/userdoc.pdf)
8 |
9 | System-Wide Installation
10 | ------------------------
11 |
12 | ContractLib should be installed using the [PEAR Installer](http://pear.php.net). This installer is the PHP community's de-facto standard for installing PHP components.
13 |
14 | sudo pear channel-discover pear.phix-project.org
15 | sudo pear install --alldeps phix/ContractLib
16 |
17 | As A Dependency On Your Component
18 | ---------------------------------
19 |
20 | If you are creating a component that relies on ContractLib, please make sure that you add LicenseLib to your component's package.xml file:
21 |
22 | ```xml
23 |
24 |
25 |
26 | ContractLib
27 | pear.phix-project.org
28 | 1.0.0
29 | 1.999.9999
30 |
31 |
32 |
33 | ```
34 |
35 | Usage
36 | -----
37 |
38 | The best documentation for ContractLib are the unit tests, which are shipped in the package. You will find them installed into your PEAR repository, which on Linux systems is normally /usr/share/php/test.
39 |
40 | You can find them online on GitHub: http://github.com/stuartherbert/ContractLib/
41 |
42 | Development Environment
43 | -----------------------
44 |
45 | If you want to patch or enhance this component, you will need to create a suitable development environment, by [installing phix](http://phix-project.org#install).
46 |
47 | You can then clone the git repository:
48 |
49 | # ContractLib
50 | git clone git@github.com:stuartherbert/ContractLib.git
51 |
52 | Then, install a local copy of this component's dependencies to complete the development environment:
53 |
54 | # build vendor/ folder
55 | phing build-vendor
56 |
57 | To make life easier for you, common tasks (such as running unit tests, generating code review analytics, and creating the PEAR package) have been automated using [phing](http://phing.info). You'll find the automated steps inside the build.xml file that ships with the component.
58 |
59 | Run the command 'phing' in the component's top-level folder to see the full list of available automated tasks.
60 |
61 | License
62 | -------
63 |
64 | See LICENSE.txt for full license details.
65 |
--------------------------------------------------------------------------------
/build.local.xml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/build.properties:
--------------------------------------------------------------------------------
1 | project.name=ContractLib2
2 | project.channel=pear.phix-project.org
3 | project.majorVersion=2
4 | project.minorVersion=1
5 | project.patchLevel=4
6 | project.snapshot=false
7 |
8 | checkstyle.standard=Zend
9 |
10 | component.type=php-library
11 | component.version=12
12 |
13 | pear.local=/var/www/${project.channel}
14 |
--------------------------------------------------------------------------------
/build.xml:
--------------------------------------------------------------------------------
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 | The code coverage report is in file://${project.review.codecoveragedir}
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 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 | Populating vendor/ with dependencies
280 |
281 |
282 |
283 | Making sure vendor/ does not contain ${project.name}
284 | Removing ${project.name}'s code from vendor/
285 |
286 |
287 |
288 |
289 | Removing ${project.name}'s docs from vendor/
290 |
291 |
292 |
293 |
294 |
295 |
296 | Removing ${project.name}'s unit tests from vendor/
297 |
298 |
299 |
300 |
301 |
302 | Your vendor/ folder has been built.
303 | You only need to run 'phing build-vendor' again if you change the
304 | dependencies listed in your package.xml file.
305 |
306 |
307 |
308 |
309 |
310 |
311 | Creating vendor/ as a sandboxed PEAR install folder
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 | Building release directory
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 | Creating ${project.tarfile} PEAR package
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 | project.lastBuiltTarfile=${project.tarfile}
403 |
404 |
405 | Your PEAR package is in ${project.tarfile}
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 | Cannot find PEAR package file ${project.lastBuiltTarfile}
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 | Please run 'phing pear-package' first, then try again.
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 | Cannot find PEAR package file ${project.lastBuiltTarfile}
460 | Run 'phing pear-package' to create a new PEAR package, then try again
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 | Please run 'phing pear-package' first, then try again.
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 | Cannot find PEAR package file ${project.lastBuiltTarfile}
491 | Run 'phing pear-package' to create a new PEAR package, then try again
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "phix/contractlib",
3 | "description": "ContractLib is a lightweight PHP library designed to help you write more robust PHP components by helping your components enforce programming contracts throughout your code",
4 | "homepage": "https://github.com/stuartherbert/ContractLib/",
5 | "license": "BSD-3-Clause",
6 | "authors": [
7 | {
8 | "name": "Stuart Herbert",
9 | "email": "stuart@stuartherbert.com",
10 | "homepage": "http://www.stuartherbert.com",
11 | "role": "Developer"
12 | }
13 | ],
14 | "support": {
15 | "issues": "https://github.com/stuartherbert/ContractLib/issues",
16 | "source": "https://github.com/stuartherbert/ContractLib/"
17 | },
18 | "require": {
19 | "phix/exceptionslib": "1.*"
20 | },
21 | "require-dev": {
22 | "phix/autoloader": "4.*"
23 | },
24 | "autoload": {
25 | "psr-0": { "" : "src/php/" }
26 | }
27 | }
--------------------------------------------------------------------------------
/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | ${project.name}
4 | ${project.channel}
5 | An easy-to-use PHP library for helping with Programming By Contract in PHP
6 |
7 | ContractLib is a lightweight PHP library designed to help you write more robust PHP components by helping your components enforce programming contracts throughout your code.
8 |
9 |
10 | Stuart Herbert
11 | stuartherbert
12 | stuart@stuartherbert.com
13 | yes
14 |
15 | ${build.date}
16 |
17 |
18 | ${project.version}
19 | ${project.majorVersion}.${project.minorVersion}
20 |
21 |
22 | ${project.stability}
23 | stable
24 |
25 | New BSD license
26 |
27 | No notes.
28 |
29 |
30 |
31 | ${contents}
32 |
33 |
34 |
35 |
36 |
37 | 5.3.0
38 |
39 |
40 | 1.9.4
41 |
42 |
43 | Autoloader4
44 | pear.phix-project.org
45 | 4.0.0
46 | 4.999.9999
47 |
48 |
49 | ExceptionsLib1
50 | pear.phix-project.org
51 | 1.0.0
52 | 1.999.9999
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | 1.0.0
61 | 1.0
62 |
63 |
64 | stable
65 | stable
66 |
67 | Your release date
68 | New BSD license
69 |
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | src/tests/unit-tests
6 |
7 |
8 |
9 |
10 | vendor
11 | src/tests
12 |
13 |
14 | src/bin
15 | src/php
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/.empty:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stuartherbert/ContractLib/52fcad32b3f42bf862b08f47b502858274b98c88/src/.empty
--------------------------------------------------------------------------------
/src/README.txt:
--------------------------------------------------------------------------------
1 | Your src/ folder
2 | ================
3 |
4 | This src/ folder is where you put all of your code for release. There's
5 | a folder for each type of file that the PEAR Installer supports. You can
6 | find out more about these file types online at:
7 |
8 | http://blog.stuartherbert.com/php/2011/04/04/explaining-file-roles/
9 |
10 | * bin/
11 |
12 | If you're creating any command-line tools, this is where you'd put
13 | them. Files in here get installed into /usr/bin on Linux et al.
14 |
15 | There is more information available here: http://blog.stuartherbert.com/php/2011/04/06/php-components-shipping-a-command-line-program/
16 |
17 | You can find an example here: https://github.com/stuartherbert/phix/tree/master/src/bin
18 |
19 | * data/
20 |
21 | If you have any data files (any files that aren't PHP code, and which
22 | don't belong in the www/ folder), this is the folder to put them in.
23 |
24 | There is more information available here: http://blog.stuartherbert.com/php/2011/04/11/php-components-shipping-data-files-with-your-components/
25 |
26 | You can find an example here: https://github.com/stuartherbert/ComponentManagerPhpLibrary/tree/master/src/data
27 |
28 | * php/
29 |
30 | This is where your component's PHP code belongs. Everything that goes
31 | into this folder must be PSR0-compliant, so that it works with the
32 | supplied autoloader.
33 |
34 | There is more information available here: http://blog.stuartherbert.com/php/2011/04/05/php-components-shipping-reusable-php-code/
35 |
36 | You can find an example here: https://github.com/stuartherbert/ContractLib/tree/master/src/php
37 |
38 | * tests/functional-tests/
39 |
40 | Right now, this folder is just a placeholder for future functionality.
41 | You're welcome to make use of it yourself.
42 |
43 | * tests/integration-tests/
44 |
45 | Right now, this folder is just a placeholder for future functionality.
46 | You're welcome to make use of it yourself.
47 |
48 | * tests/unit-tests/
49 |
50 | This is where all of your PHPUnit tests go.
51 |
52 | It needs to contain _exactly_ the same folder structure as the src/php/
53 | folder. For each of your PHP classes in src/php/, there should be a
54 | corresponding test file in test/unit-tests.
55 |
56 | There is more information available here: http://blog.stuartherbert.com/php/2011/08/15/php-components-shipping-unit-tests-with-your-component/
57 |
58 | You can find an example here: https://github.com/stuartherbert/ContractLib/tree/master/test/unit-tests
59 |
60 | * www/
61 |
62 | This folder is for any files that should be published in a web server's
63 | DocRoot folder.
64 |
65 | It's quite unusual for components to put anything in this folder, but
66 | it is there just in case.
67 |
68 | There is more information available here: http://blog.stuartherbert.com/php/2011/08/16/php-components-shipping-web-pages-with-your-components/
69 |
--------------------------------------------------------------------------------
/src/bin/.empty:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stuartherbert/ContractLib/52fcad32b3f42bf862b08f47b502858274b98c88/src/bin/.empty
--------------------------------------------------------------------------------
/src/data/.empty:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stuartherbert/ContractLib/52fcad32b3f42bf862b08f47b502858274b98c88/src/data/.empty
--------------------------------------------------------------------------------
/src/docs/.empty:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stuartherbert/ContractLib/52fcad32b3f42bf862b08f47b502858274b98c88/src/docs/.empty
--------------------------------------------------------------------------------
/src/php/.empty:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stuartherbert/ContractLib/52fcad32b3f42bf862b08f47b502858274b98c88/src/php/.empty
--------------------------------------------------------------------------------
/src/php/Phix_Project/ContractLib2/Contract.php:
--------------------------------------------------------------------------------
1 |
39 | * @copyright 2011-present Stuart Herbert
40 | * @license http://www.opensource.org/licenses/bsd-license.php BSD License
41 | * @link http://phix-project.org
42 | * @version @@PACKAGE_VERSION@@
43 | */
44 |
45 | namespace Phix_Project\ContractLib2;
46 |
47 | class Contract
48 | {
49 | /**
50 | * Are we currently enforcing any contracts passed to
51 | * self::Enforce()?
52 | *
53 | * By default, we do not!
54 | *
55 | * @var boolean
56 | */
57 | static protected $enforcing = false;
58 |
59 | /**
60 | * A set of OldValues objects
61 | *
62 | * You can get the right object for your code's current scope by
63 | * calling Contract::OldValues()
64 | *
65 | * Objects in this array that are no longer required are nuked by
66 | * the Contract::Postconditions() wrapper
67 | *
68 | * @var array(OldValues)
69 | */
70 | static protected $oldValues = array();
71 |
72 | /**
73 | * Global library; cannot instantiate
74 | *
75 | * You can't instantiate this library because it needs to preserve
76 | * state even as the execution scope in your code changes :(
77 | *
78 | * @codeCoverageIgnore
79 | */
80 | protected function __construct()
81 | {
82 | // do nothing
83 | }
84 |
85 | /**
86 | * Precondition: is the expression $expr true?
87 | *
88 | * Use this method at the start of your method to make sure you're
89 | * happy with the data that you have been passed, and with the
90 | * current state of your object
91 | *
92 | * Throws an E5xx_ContractPreconditionException if the parameter
93 | * passed in is false
94 | *
95 | * @throw E5xx_ContractPreconditionException
96 | * @param boolean $expr
97 | * @param string $reason error message to show on failure
98 | * @return boolean true on success
99 | */
100 | static public function Requires($expr, $reason = null)
101 | {
102 | if (!$expr)
103 | {
104 | throw new E5xx_ContractFailedException('Requires', $reason);
105 | }
106 |
107 | return true;
108 | }
109 |
110 | /**
111 | * Precondition: is the expression $expr true?
112 | *
113 | * Use this method at the start of your method to make sure you're
114 | * happy with the data that you have been passed, and with the
115 | * current state of your object
116 | *
117 | * Throws an E5xx_ContractPreconditionException if $expr is false,
118 | * and adds $value to the exception's error message so that you
119 | * can see which value failed the test
120 | *
121 | * @param mixed $value
122 | * @param boolean $expr
123 | * @param string $reason error message to show on failure
124 | * @return boolean true on success
125 | */
126 | static public function RequiresValue($value, $expr, $reason = null)
127 | {
128 | if (!$expr)
129 | {
130 | throw new E5xx_ContractFailedException('RequiresValue', $reason, true, $value);
131 | }
132 |
133 | return true;
134 | }
135 |
136 | /**
137 | * Postcondition: is the expression $expr true?
138 | *
139 | * Use this method at the end of your method to make sure you're
140 | * happy with the results before your method returns to the caller
141 | *
142 | * Throws an E5xx_ContractPostconditionException if $expr is false.
143 | *
144 | * @param boolean $expr
145 | * @param string $reason error message to show on failure
146 | * @return boolean true on success
147 | */
148 | static public function Ensures($expr, $reason = null)
149 | {
150 | if (!$expr)
151 | {
152 | throw new E5xx_ContractFailedException('Ensures', $reason);
153 | }
154 |
155 | return true;
156 | }
157 |
158 | /**
159 | * Postcondition: is the expression $expr true?
160 | *
161 | * Use this method at the end of your method to make sure you're
162 | * happy with the results before your method returns to the caller
163 | *
164 | * Throws an E5xx_ContractPostConditionException if $expr is false,
165 | * and adds $value to the exception's error message so that you
166 | * can see which value failed the test
167 | *
168 | * @param mixed $value
169 | * @param boolean $expr
170 | * @param string $reason error message to show on failure
171 | * @return boolean true on success
172 | */
173 | static public function EnsuresValue($value, $expr, $reason = null)
174 | {
175 | if (!$expr)
176 | {
177 | throw new E5xx_ContractFailedException('EnsuresValue', $reason, true, $value);
178 | }
179 |
180 | return true;
181 | }
182 |
183 | /**
184 | * Condition: is the expr $expr true?
185 | *
186 | * Use this method in the middle of your method, to check the
187 | * workings of your method before continuing.
188 | *
189 | * Throws an E5xx_ContractConditionException if $expr is false.
190 | *
191 | * @param boolean $expr
192 | * @param string $reason error message to show on failure
193 | * @return boolean true on success
194 | */
195 | static public function Asserts($expr, $reason = null)
196 | {
197 | if (!$expr)
198 | {
199 | throw new E5xx_ContractFailedException('Asserts', $reason);
200 | }
201 |
202 | return true;
203 | }
204 |
205 | /**
206 | * Condition: is the expr $expr true?
207 | *
208 | * Use this method in the middle of your method, to check the
209 | * workings of your method before continuing.
210 | *
211 | * Throws an E5xx_ContractConditionException if $expr is false,
212 | * and adds $value to the exception's error message so that you
213 | * can see which value failed the test
214 | *
215 | * @param mixed $value
216 | * @param boolean $expr
217 | * @param string $reason error message to show on failure
218 | * @return boolean true on success
219 | */
220 | static public function AssertsValue($value, $expr, $reason = null)
221 | {
222 | if (!$expr)
223 | {
224 | throw new E5xx_ContractFailedException('AssertsValue', $reason, true, $value);
225 | }
226 |
227 | return true;
228 | }
229 |
230 | /**
231 | * Apply the same condition (or set of conditions) to the values
232 | * in an array
233 | *
234 | * @param array $values
235 | * @param callback $callback
236 | * @param boolean true on success
237 | */
238 | static public function ForAll($values, $callback)
239 | {
240 | array_walk($values, $callback);
241 |
242 | return true;
243 | }
244 |
245 | // ================================================================
246 | //
247 | // Old values support
248 | //
249 | // ----------------------------------------------------------------
250 |
251 | /**
252 | * Obtain a value, suitable for use as an array key, based on the
253 | * current execution scope in an app
254 | *
255 | * @return array(string, string)
256 | * The caller, plus the scope
257 | */
258 | static protected function determineScope()
259 | {
260 | // the scope comes from interpreting the current
261 | // execution stack
262 | //
263 | // we are looking for the function or method that has
264 | // called either Contract::Preconditions or
265 | // Contract::Postconditions
266 | //
267 | // this algorithm is annoyingly expensive, but it should
268 | // ensure that there are no problems with actions in one
269 | // scope ever affecting the old values remembered in any
270 | // other scope
271 |
272 | $debug_backtrace = debug_backtrace();
273 |
274 | $caller = null;
275 | $maxIndex = count($debug_backtrace);
276 | $i = 0;
277 |
278 | while ($caller == null && $i < $maxIndex)
279 | {
280 | // var_dump(substr($debug_backtrace[$i]['function'], -10, 10));
281 |
282 | if (isset($debug_backtrace[$i]['class'])
283 | && $debug_backtrace[$i]['class'] == 'Phix_Project\ContractLib2\Contract'
284 | && substr($debug_backtrace[$i]['function'], -10, 10) == 'conditions'
285 | )
286 | {
287 | $caller = $debug_backtrace[$i]['function'];
288 | }
289 | $i++;
290 | }
291 |
292 | // did we find what we are looking for?
293 | if ($caller == null)
294 | {
295 | // no - throw an exception
296 | throw new \RuntimeException('You can only use ContractLib2 Old Values support inside Preconditions or Postconditions');
297 | }
298 |
299 | // at this point, $debug_backtrace[0] points at the caller
300 | // to the precondition or postcondition
301 | //
302 | // we want to remember the file and function, but not the
303 | // specific line number
304 | //
305 | // this makes sure that we return the same result when
306 | // called in both the preconditions and postconditions
307 | // in the same function, with the same callstack
308 | if (isset($debug_backtrace[$i]['line']))
309 | {
310 | // this never seems to get called, but I assume
311 | // that one day the backtrace might get 'fixed'
312 | // @codeCoverageIgnoreStart
313 | unset($debug_backtrace[$i]['line']);
314 | // @codeCoverageIgnoreEnd
315 | }
316 |
317 | // now, let's build up this scope
318 | $scope = '';
319 | for (;$i < $maxIndex; $i++)
320 | {
321 | foreach (array('file', 'line', 'function') as $key)
322 | {
323 | if (isset($debug_backtrace[$i][$key]))
324 | {
325 | $scope .= $debug_backtrace[$i][$key];
326 | }
327 | }
328 | }
329 |
330 | // var_dump($scope);
331 |
332 | return array($caller, $scope);
333 | }
334 |
335 | /**
336 | * Obtain the old value of an argument
337 | *
338 | * @param string $argName
339 | * @return mixed
340 | */
341 | static public function OldValue($argName)
342 | {
343 | // work out the current scope
344 | list($caller, $scope) = self::determineScope();
345 |
346 | // do we have an existing OldValues object for this scope?
347 | if (!isset(self::$oldValues[$scope]))
348 | {
349 | return null;
350 | }
351 |
352 | // do we have a stashed value for this argument name?
353 | if (self::$oldValues[$scope]->hasStashed($argName))
354 | {
355 | // yes we do
356 | return self::$oldValues[$scope]->unpack($argName);
357 | }
358 |
359 | // no we don't
360 | return null;
361 | }
362 |
363 | /**
364 | * Remember an old value for future comparisons
365 | *
366 | * @param string $argName
367 | * The name of the value to remember
368 | * @param mixed $argValue
369 | * The value to remember
370 | */
371 | static public function RememberOldValue($argName, $argValue)
372 | {
373 | // work out the current scope
374 | list($caller, $scope) = self::determineScope();
375 |
376 | // we must have been called from Preconditions
377 | if ($caller !== 'Preconditions')
378 | {
379 | throw new \RuntimeException('You can only remember old values inside Contract::Preconditions');
380 | }
381 |
382 | // do we have an existing OldValues object for this scope?
383 | if (!isset(self::$oldValues[$scope]))
384 | {
385 | // no - create one!
386 | self::$oldValues[$scope] = new OldValues();
387 | }
388 |
389 | // stash the value
390 | self::$oldValues[$scope]->stash($argName, $argValue);
391 | }
392 |
393 | /**
394 | * Free up memory by forgetting the old values we may have
395 | * remembered in the current scope
396 | */
397 | static public function ForgetOldValues()
398 | {
399 | // work out the current scope
400 | list($caller, $scope) = self::determineScope();
401 |
402 | // release an object, if we have one
403 | if (isset(self::$oldValues[$scope]))
404 | {
405 | unset(self::$oldValues[$scope]);
406 | }
407 | }
408 |
409 | /**
410 | * Get the internal list of scopes that are remembering old
411 | * values
412 | *
413 | * This method exists only to help with debugging
414 | *
415 | * @return array
416 | */
417 | static public function _rememberedScopes()
418 | {
419 | return self::$oldValues;
420 | }
421 |
422 | // ================================================================
423 | //
424 | // Unreachable code support
425 | //
426 | // ----------------------------------------------------------------
427 |
428 | static public function Unreachable($file, $line)
429 | {
430 | throw new E5xx_ContractFailedException('Unreachable', "Unreachable code in file $file at line $line has somehow been reached. Go figure!");
431 | }
432 |
433 | // ================================================================
434 | //
435 | // Wrapped contract support
436 | //
437 | // ----------------------------------------------------------------
438 |
439 | /**
440 | * Tell us to enforce contracts passed to self::Enforce()
441 | */
442 | static public function EnforceWrappedContracts()
443 | {
444 | self::$enforcing = true;
445 | }
446 |
447 | /**
448 | * Tell us to enforce only calls made directly to the individual
449 | * contract conditions: Requires, Assert, Ensures et al
450 | */
451 | static public function EnforceOnlyDirectContracts()
452 | {
453 | self::$enforcing = false;
454 | }
455 |
456 | /**
457 | * Check a set of preconditions *if* we are enforcing wrapped
458 | * contracts.
459 | *
460 | * This exists as a performance boost, allowing us to leave
461 | * contracts in the code even in production environments
462 | *
463 | * @param callback $callback
464 | * @param array $params
465 | * @return boolean true on success
466 | */
467 | static public function Preconditions($callback, $params = array())
468 | {
469 | if (self::$enforcing)
470 | {
471 | call_user_func_array($callback, $params);
472 | }
473 |
474 | return true;
475 | }
476 |
477 | /**
478 | * Check a set of postconditions *if* we are enforcing wrapped
479 | * contracts.
480 | *
481 | * This exists as a performance boost, allowing us to leave
482 | * contracts in the code even in production environments
483 | *
484 | * @param callback $callback
485 | * @param array $params
486 | * @return boolean true on success
487 | */
488 | static public function Postconditions($callback, $params = array())
489 | {
490 | if (self::$enforcing)
491 | {
492 | call_user_func_array($callback, $params);
493 | }
494 |
495 | // success!
496 | return true;
497 | }
498 |
499 | /**
500 | * Check a set of conditions mid-method *if* we are enforcing
501 | * wrapped contracts.
502 | *
503 | * This exists as a performance boost, allowing us to leave
504 | * contracts in the code even in production environments
505 | *
506 | * @param callback $callback
507 | * @param array $params
508 | * @return boolean true on success
509 | */
510 | static public function Conditionals($callback, $params = array())
511 | {
512 | if (self::$enforcing)
513 | {
514 | call_user_func_array($callback, $params);
515 | }
516 |
517 | return true;
518 | }
519 | }
--------------------------------------------------------------------------------
/src/php/Phix_Project/ContractLib2/ContractInvariant.php:
--------------------------------------------------------------------------------
1 |
39 | * @copyright 2011-present Stuart Herbert
40 | * @license http://www.opensource.org/licenses/bsd-license.php BSD License
41 | * @link http://phix-project.org
42 | * @version @@PACKAGE_VERSION@@
43 | */
44 |
45 | namespace Phix_Project\ContractLib2;
46 |
47 | interface ContractInvariant
48 | {
49 | /**
50 | * A test method that can be called at any time during the life
51 | * of an object, which will run a series of assert()s to prove
52 | * that the object is in a sane state
53 | */
54 | public function objectInvariant();
55 | }
--------------------------------------------------------------------------------
/src/php/Phix_Project/ContractLib2/E5xx/ContractFailedException.php:
--------------------------------------------------------------------------------
1 |
39 | * @copyright 2011-present Stuart Herbert
40 | * @license http://www.opensource.org/licenses/bsd-license.php BSD License
41 | * @link http://phix-project.org
42 | * @version @@PACKAGE_VERSION@@
43 | */
44 |
45 | namespace Phix_Project\ContractLib2;
46 |
47 | use Phix_Project\ExceptionsLib1\E5xx_InternalServerErrorException;
48 |
49 | /**
50 | * This exception is thrown when an assert() statement fails
51 | */
52 | class E5xx_ContractFailedException extends E5xx_InternalServerErrorException
53 | {
54 | public function __construct($contract, $reason, $hasValue = false, $value = false)
55 | {
56 | $message = 'Contract::' . $contract . '() failed';
57 |
58 | if ($hasValue)
59 | {
60 | $message .= "; value tested was '" . print_r($value, true) . "'";
61 | }
62 |
63 | if ($reason !== null)
64 | {
65 | $message .= '; reason for failure was: ' . $reason;
66 | }
67 |
68 | parent::__construct($message);
69 | }
70 | }
--------------------------------------------------------------------------------
/src/php/Phix_Project/ContractLib2/OldValues.php:
--------------------------------------------------------------------------------
1 |
39 | * @copyright 2011-present Stuart Herbert
40 | * @license http://www.opensource.org/licenses/bsd-license.php BSD License
41 | * @link http://phix-project.org
42 | * @version @@PACKAGE_VERSION@@
43 | */
44 |
45 | namespace Phix_Project\ContractLib2;
46 |
47 | /**
48 | * Helper class for remember a set of named values
49 | */
50 | class OldValues
51 | {
52 | /**
53 | * Add a value to the list
54 | *
55 | * @param string $name
56 | * @param mixed $value
57 | */
58 | public function stash($name, $value)
59 | {
60 | $this->values[$name] = serialize($value);
61 | }
62 |
63 | /**
64 | * Do we have a named value in the list?
65 | *
66 | * @param string $name
67 | * @return boolean
68 | */
69 | public function hasStashed($name)
70 | {
71 | return isset($this->values[$name]);
72 | }
73 |
74 | /**
75 | * Retrieve a value from the list
76 | *
77 | * @param string $name
78 | * @return mixed
79 | */
80 | public function unpack($name)
81 | {
82 | return unserialize($this->values[$name]);
83 | }
84 | }
--------------------------------------------------------------------------------
/src/tests/.empty:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stuartherbert/ContractLib/52fcad32b3f42bf862b08f47b502858274b98c88/src/tests/.empty
--------------------------------------------------------------------------------
/src/tests/functional-tests/.empty:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stuartherbert/ContractLib/52fcad32b3f42bf862b08f47b502858274b98c88/src/tests/functional-tests/.empty
--------------------------------------------------------------------------------
/src/tests/integration-tests/.empty:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stuartherbert/ContractLib/52fcad32b3f42bf862b08f47b502858274b98c88/src/tests/integration-tests/.empty
--------------------------------------------------------------------------------
/src/tests/unit-tests/.empty:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stuartherbert/ContractLib/52fcad32b3f42bf862b08f47b502858274b98c88/src/tests/unit-tests/.empty
--------------------------------------------------------------------------------
/src/tests/unit-tests/bin/.empty:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stuartherbert/ContractLib/52fcad32b3f42bf862b08f47b502858274b98c88/src/tests/unit-tests/bin/.empty
--------------------------------------------------------------------------------
/src/tests/unit-tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |
39 | * @copyright 2011-present Stuart Herbert
40 | * @license http://www.opensource.org/licenses/bsd-license.php BSD License
41 | * @link http://phix-project.org
42 | * @version @@PACKAGE_VERSION@@
43 | */
44 |
45 |
46 | namespace Phix_Project\ContractLib2;
47 |
48 | use Exception;
49 | use ReflectionClass;
50 | use PHPUnit_Framework_TestCase;
51 |
52 | class ContractTest extends PHPUnit_Framework_TestCase
53 | {
54 | public function testCannotInstantiate()
55 | {
56 | $refClass = new ReflectionClass('Phix_Project\ContractLib2\Contract');
57 | $refMethod = $refClass->getMethod('__construct');
58 | $this->assertFalse($refMethod->isPublic());
59 | }
60 |
61 | public function testPreconditionsMustBeTrue()
62 | {
63 | // prove that the precondition checks do not throw an
64 | // exception when they are passed the value of TRUE
65 | $this->assertTrue(Contract::Requires(true));
66 | $this->assertTrue(Contract::RequiresValue(0, true));
67 |
68 | // prove that the precondition checks do throw an exception
69 | // when they are passed the value of FALSE
70 | $caughtException = false;
71 | try
72 | {
73 | Contract::Requires(false);
74 | }
75 | catch (E5xx_ContractFailedException $e)
76 | {
77 | $caughtException = true;
78 | }
79 | $this->assertTrue($caughtException);
80 |
81 | // repeat the check with another of the precondition
82 | // check methods
83 | $caughtException = false;
84 | try
85 | {
86 | Contract::RequiresValue(10, false);
87 | }
88 | catch (E5xx_ContractFailedException $e)
89 | {
90 | $caughtException = true;
91 | }
92 | $this->assertTrue($caughtException);
93 | }
94 |
95 | public function testPostconditionsMustBeTrue()
96 | {
97 | // prove that the postcondition checks do not throw an
98 | // exception when they are passed the value of TRUE
99 | $this->assertTrue(Contract::Ensures(true));
100 | $this->assertTrue(Contract::EnsuresValue(0, true));
101 |
102 | // prove that the postcondition checks do throw an
103 | // exception when they are passed the value of FALSE
104 | $caughtException = false;
105 | try
106 | {
107 | Contract::Ensures(false);
108 | }
109 | catch (E5xx_ContractFailedException $e)
110 | {
111 | $caughtException = true;
112 | }
113 | $this->assertTrue($caughtException);
114 |
115 | // repeat the test for another of the postcondition
116 | // check methods
117 | $caughtException = false;
118 | try
119 | {
120 | Contract::EnsuresValue(10, false);
121 | }
122 | catch (E5xx_ContractFailedException $e)
123 | {
124 | $caughtException = true;
125 | }
126 | $this->assertTrue($caughtException);
127 | }
128 |
129 | public function testMidConditionsMustBeTrue()
130 | {
131 | // prove that the condition checks do not throw an
132 | // exception when they are passed the value of TRUE
133 | $this->assertTrue(Contract::Asserts(true));
134 | $this->assertTrue(Contract::AssertsValue(0, true));
135 |
136 | // prove that the condition checks do throw an exception
137 | // when they are passed the value of FALSE
138 | $caughtException = false;
139 | try
140 | {
141 | Contract::Asserts(false);
142 | }
143 | catch (E5xx_ContractFailedException $e)
144 | {
145 | $caughtException = true;
146 | }
147 | $this->assertTrue($caughtException);
148 |
149 | // repeat the test with another of the condition check
150 | // methods
151 | $caughtException = false;
152 | try
153 | {
154 | Contract::AssertsValue(10, false);
155 | }
156 | catch (E5xx_ContractFailedException $e)
157 | {
158 | $caughtException = true;
159 | }
160 | $this->assertTrue($caughtException);
161 | }
162 |
163 | public function testCanApplyConditionsToArrays()
164 | {
165 | $testData1 = array (1,2,3,4,5);
166 | $testData2 = array (6,7,8,9,10);
167 |
168 | // these contracts are satisfied
169 | Contract::ForAll($testData1, function($value) { Contract::Requires($value < 6); });
170 | $this->assertTrue(true);
171 | Contract::ForAll($testData2, function($value) { Contract::Requires($value > 5); });
172 | $this->assertTrue(true);
173 |
174 | // these contracts are not satisfied
175 | $caughtException = false;
176 | try
177 | {
178 | Contract::ForAll($testData1, function($value) { Contract::Requires($value > 5); });
179 | }
180 | catch (E5xx_ContractFailedException $e)
181 | {
182 | $caughtException = true;
183 | }
184 | $this->assertTrue($caughtException);
185 |
186 | // these contracts are not satisfied
187 | $caughtException = false;
188 | try
189 | {
190 | Contract::ForAll($testData2, function($value) { Contract::Requires($value < 6); });
191 | }
192 | catch (E5xx_ContractFailedException $e)
193 | {
194 | $caughtException = true;
195 | }
196 | $this->assertTrue($caughtException);
197 | }
198 |
199 | public function testCanSeeTheValueThatFailedThePrecondition()
200 | {
201 | $caughtException = false;
202 | try
203 | {
204 | Contract::RequiresValue(5, false);
205 | }
206 | catch (E5xx_ContractFailedException $e)
207 | {
208 | $caughtException = $e->getMessage();
209 | }
210 |
211 | // did we catch the exception?
212 | $this->assertTrue($caughtException !== false);
213 |
214 | // did we get the message we expect?
215 | $expected = "Internal server error: Contract::RequiresValue() failed; value tested was '5'";
216 | $this->assertEquals($expected, $caughtException);
217 | }
218 |
219 | public function testCanSeeTheValueThatFailedThePostcondition()
220 | {
221 | $caughtException = false;
222 | try
223 | {
224 | Contract::EnsuresValue(5, false);
225 | }
226 | catch (E5xx_ContractFailedException $e)
227 | {
228 | $caughtException = $e->getMessage();
229 | }
230 |
231 | // did we catch the exception?
232 | $this->assertTrue($caughtException !== false);
233 |
234 | // did we get the message we expect?
235 | $expected = "Internal server error: Contract::EnsuresValue() failed; value tested was '5'";
236 | $this->assertEquals($expected, $caughtException);
237 | }
238 |
239 | public function testCanSeeTheValueThatFailedTheMidCondition()
240 | {
241 | $caughtException = false;
242 | try
243 | {
244 | Contract::AssertsValue(5, false);
245 | }
246 | catch (E5xx_ContractFailedException $e)
247 | {
248 | $caughtException = $e->getMessage();
249 | }
250 |
251 | // did we catch the exception?
252 | $this->assertTrue($caughtException !== false);
253 |
254 | // did we get the message we expect?
255 | $expected = "Internal server error: Contract::AssertsValue() failed; value tested was '5'";
256 | $this->assertEquals($expected, $caughtException);
257 | }
258 |
259 | public function testCanSeeTheReasonWhenThePreconditionFailed()
260 | {
261 | $caughtException = false;
262 | try
263 | {
264 | Contract::Requires(false, "my reason");
265 | }
266 | catch (E5xx_ContractFailedException $e)
267 | {
268 | $caughtException = $e->getMessage();
269 | }
270 |
271 | // did we catch the exception?
272 | $this->assertTrue($caughtException !== false);
273 |
274 | // did we get the message we expect?
275 | $expected = "Internal server error: Contract::Requires() failed; reason for failure was: my reason";
276 | $this->assertEquals($expected, $caughtException);
277 |
278 | $caughtException = false;
279 | try
280 | {
281 | Contract::RequiresValue(5, false, "my reason");
282 | }
283 | catch (E5xx_ContractFailedException $e)
284 | {
285 | $caughtException = $e->getMessage();
286 | }
287 |
288 | // did we catch the exception?
289 | $this->assertTrue($caughtException !== false);
290 |
291 | // did we get the message we expect?
292 | $expected = "Internal server error: Contract::RequiresValue() failed; value tested was '5'; reason for failure was: my reason";
293 | $this->assertEquals($expected, $caughtException);
294 | }
295 |
296 | public function testCanSeeTheReasonWhenThePostconditionFailed()
297 | {
298 | $caughtException = false;
299 | try
300 | {
301 | Contract::Ensures(false, 'my reason');
302 | }
303 | catch (E5xx_ContractFailedException $e)
304 | {
305 | $caughtException = $e->getMessage();
306 | }
307 |
308 | // did we catch the exception?
309 | $this->assertTrue($caughtException !== false);
310 |
311 | // did we get the message we expect?
312 | $expected = "Internal server error: Contract::Ensures() failed; reason for failure was: my reason";
313 | $this->assertEquals($expected, $caughtException);
314 |
315 | $caughtException = false;
316 | try
317 | {
318 | Contract::EnsuresValue(5, false, 'my reason');
319 | }
320 | catch (E5xx_ContractFailedException $e)
321 | {
322 | $caughtException = $e->getMessage();
323 | }
324 |
325 | // did we catch the exception?
326 | $this->assertTrue($caughtException !== false);
327 |
328 | // did we get the message we expect?
329 | $expected = "Internal server error: Contract::EnsuresValue() failed; value tested was '5'; reason for failure was: my reason";
330 | $this->assertEquals($expected, $caughtException);
331 | }
332 |
333 | public function testCanSeeTheReasonTheMidConditionFailed()
334 | {
335 | $caughtException = false;
336 | try
337 | {
338 | Contract::Asserts(false, 'my reason');
339 | }
340 | catch (E5xx_ContractFailedException $e)
341 | {
342 | $caughtException = $e->getMessage();
343 | }
344 |
345 | // did we catch the exception?
346 | $this->assertTrue($caughtException !== false);
347 |
348 | // did we get the message we expect?
349 | $expected = "Internal server error: Contract::Asserts() failed; reason for failure was: my reason";
350 | $this->assertEquals($expected, $caughtException);
351 |
352 | $caughtException = false;
353 | try
354 | {
355 | Contract::AssertsValue(5, false, 'my reason');
356 | }
357 | catch (E5xx_ContractFailedException $e)
358 | {
359 | $caughtException = $e->getMessage();
360 | }
361 |
362 | // did we catch the exception?
363 | $this->assertTrue($caughtException !== false);
364 |
365 | // did we get the message we expect?
366 | $expected = "Internal server error: Contract::AssertsValue() failed; value tested was '5'; reason for failure was: my reason";
367 | $this->assertEquals($expected, $caughtException);
368 | }
369 |
370 | public function testWrappedContractsCanBeDisabled()
371 | {
372 | // ensure wrapped contracts are switched off
373 | Contract::EnforceOnlyDirectContracts();
374 |
375 | // some data to test
376 | $x = 1;
377 | $y = 2;
378 | $z = 3;
379 |
380 | // check wrapped preconditions
381 | $executed = false;
382 | Contract::Preconditions(function($x, $y, $z) use (&$executed) {
383 | Contract::Requires($x < $y);
384 | Contract::Requires($y < $z);
385 | $executed = true;
386 | }, array($x, $y, $z));
387 | $this->assertFalse($executed);
388 |
389 | // check wrapped mid-conditions
390 | $executed = false;
391 | Contract::Conditionals(function() use (&$executed) {
392 | Contract::Asserts(2 > 1);
393 | Contract::Asserts(5 > 4);
394 | $executed = true;
395 | });
396 | $this->assertFalse($executed);
397 |
398 | // check wrapped postconditions
399 | $executed = false;
400 | Contract::Postconditions(function($x, $y, $z) use (&$executed) {
401 | Contract::Ensures($x < $z);
402 | Contract::Ensures($z > $x);
403 | $executed = true;
404 | }, array($x, $y, $z));
405 | $this->assertFalse($executed);
406 | }
407 |
408 | public function testCanWrapContractsForPeformance()
409 | {
410 | // enable wrapped contracts
411 | Contract::EnforceWrappedContracts();
412 |
413 | // some data to test
414 | $x = 1;
415 | $y = 2;
416 | $z = 3;
417 |
418 | // check wrapped preconditions
419 | $executed = false;
420 | Contract::Preconditions(function($x, $y, $z) use (&$executed) {
421 | Contract::Requires($x < $y);
422 | Contract::Requires($y < $z);
423 | $executed = true;
424 | }, array($x, $y, $z));
425 | $this->assertTrue($executed);
426 |
427 | // check wrapped mid-conditions
428 | $executed = false;
429 | Contract::Conditionals(function() use (&$executed) {
430 | Contract::Asserts(2 > 1);
431 | Contract::Asserts(5 > 4);
432 | $executed = true;
433 | });
434 | $this->assertTrue($executed);
435 |
436 | // check wrapped postconditions
437 | $executed = false;
438 | Contract::Postconditions(function($x, $y, $z) use (&$executed) {
439 | Contract::Ensures($x < $z);
440 | Contract::Ensures($z > $x);
441 | $executed = true;
442 | }, array($x, $y, $z));
443 | $this->assertTrue($executed);
444 | }
445 |
446 | public function testCanDisabledWrappedContracts()
447 | {
448 | // enable wrapped contracts
449 | Contract::EnforceWrappedContracts();
450 |
451 | // execute a wrapped contract
452 | $executed = false;
453 | Contract::Preconditions(function() use (&$executed)
454 | {
455 | $executed = true;
456 | });
457 | $this->assertTrue($executed);
458 |
459 | // now, disable wrapped contracts
460 | Contract::EnforceOnlyDirectContracts();
461 |
462 | // repeat the test
463 | $executed = false;
464 | Contract::Preconditions(function() use (&$executed)
465 | {
466 | $executed = true;
467 | });
468 | $this->assertFalse($executed);
469 | }
470 |
471 | public function testCanTrackUnreachableCode()
472 | {
473 | $caughtException = false;
474 | $expectedMessage =
475 | $actualMessage = "Internal server error: Contract::Unreachable() failed; reason for failure was: "
476 | . 'Unreachable code in file ' . __FILE__
477 | . ' at line ' . (__LINE__ + 5)
478 | . ' has somehow been reached. Go figure!';
479 |
480 | try
481 | {
482 | Contract::Unreachable(__FILE__, __LINE__);
483 | }
484 | catch (E5xx_ContractFailedException $e)
485 | {
486 | $caughtException = true;
487 | $actualMessage = $e->getMessage();
488 | }
489 |
490 | // what happened?
491 | $this->assertTrue($caughtException);
492 | $this->assertEquals($expectedMessage, $actualMessage);
493 | }
494 | }
--------------------------------------------------------------------------------
/src/tests/unit-tests/php/Phix_Project/ContractLib2/OldValuesTest.php:
--------------------------------------------------------------------------------
1 |
39 | * @copyright 2011-present Stuart Herbert
40 | * @license http://www.opensource.org/licenses/bsd-license.php BSD License
41 | * @link http://phix-project.org
42 | * @version @@PACKAGE_VERSION@@
43 | */
44 |
45 |
46 | namespace Phix_Project\ContractLib2;
47 |
48 | use Exception;
49 | use ReflectionClass;
50 | use PHPUnit_Framework_TestCase;
51 |
52 | use Phix_Project\ContractLib2\Contract;
53 |
54 | class OldValuesTest extends PHPUnit_Framework_TestCase
55 | {
56 | public function testCanInstantiate()
57 | {
58 | // you *can* create an OldValues object directly yourself,
59 | // but tbh it isn't much use
60 | //
61 | // the right way to use this object is by inference,
62 | // through the OldValue methods on Contract::
63 |
64 | $obj = new OldValues();
65 | $this->assertTrue($obj instanceof OldValues);
66 | }
67 |
68 | public function testOldValuesCanBeRememberedInPreconditions()
69 | {
70 | // make sure wrapped contracts are enabled
71 | Contract::EnforceWrappedContracts();
72 |
73 | // some test data
74 | $arg1 = __LINE__;
75 |
76 | // stash the value
77 | Contract::Preconditions(function() use ($arg1)
78 | {
79 | Contract::RememberOldValue('arg1', $arg1);
80 | });
81 |
82 | // if we get here, the test has passed
83 | $this->assertTrue(true);
84 |
85 | // let's forget that value now :)
86 | Contract::Postconditions(function()
87 | {
88 | Contract::ForgetOldValues();
89 | });
90 | }
91 |
92 | public function testCannotBeRememberedInPostconditions()
93 | {
94 | // tell PHPUnit that this test causes an exception
95 | $this->setExpectedException('RuntimeException');
96 |
97 | // make sure wrapped contracts are enabled
98 | Contract::EnforceWrappedContracts();
99 |
100 | // some test data
101 | $arg1 = __LINE__;
102 |
103 | // stash the value
104 | Contract::Postconditions(function() use ($arg1)
105 | {
106 | Contract::RememberOldValue('arg1', $arg1);
107 | });
108 |
109 | // this should never be reached, but just in case ...
110 | $this->assertTrue(false);
111 | }
112 |
113 | public function testCannotBeRememberedOutsideWrappedContracts()
114 | {
115 | // tell PHPUnit that this test causes an exception
116 | $this->setExpectedException('RuntimeException');
117 |
118 | // some test data
119 | $arg1 = __LINE__;
120 |
121 | // stash the value
122 | Contract::RememberOldValue('arg1', $arg1);
123 |
124 | // this should never be reached, but just in case ...
125 | $this->assertTrue(false);
126 | }
127 |
128 | public function testCanStashValues()
129 | {
130 | // make sure wrapped contracts are enabled
131 | Contract::EnforceWrappedContracts();
132 |
133 | // some test data
134 | $origArg1 = __LINE__;
135 | $origArg2 = __LINE__;
136 |
137 | // take a copy ... because we need to change these variables
138 | // in a bit to prove that we're remembering the original
139 | // value
140 | $arg1 = $origArg1;
141 | $arg2 = $origArg2;
142 |
143 | // stash some values
144 | Contract::Preconditions(function() use ($arg1, $arg2)
145 | {
146 | Contract::RememberOldValue('arg1', $arg1);
147 | Contract::RememberOldValue('arg2', $arg2);
148 | });
149 |
150 | // change variables in this scope
151 | $arg1 = __LINE__;
152 | $arg2 = __LINE__;
153 |
154 | // now, did we get the values?
155 | //
156 | // for this to work propery, we have to test it inside
157 | // the postconditions wrapper
158 | Contract::PostConditions(function($obj) use($origArg1, $origArg2, $arg1, $arg2)
159 | {
160 | // the remembered values should be the originals
161 | $obj->assertEquals($origArg1, Contract::OldValue('arg1'));
162 | $obj->assertEquals($origArg2, Contract::OldValue('arg2'));
163 |
164 | // the remembered values should not be the same
165 | // values that our changed variables now have
166 | $obj->assertNotEquals($arg1, Contract::OldValue('arg1'));
167 | $obj->assertNotEquals($arg2, Contract::OldValue('arg2'));
168 |
169 | // release the memory
170 | Contract::ForgetOldValues();
171 | }, array($this));
172 | }
173 |
174 | public function testReturnsNullWhenNoOldValuesHaveBeenRemembered()
175 | {
176 | // make sure wrapped contracts are enabled
177 | Contract::EnforceWrappedContracts();
178 |
179 | // do the test
180 | // we never remembered any values in this scope!!
181 | Contract::PostConditions(function($obj)
182 | {
183 | $obj->assertNull(Contract::OldValue('arg1'));
184 | }, array($this));
185 | }
186 |
187 | public function testReturnsNullForOldValuesThatHaveNotBeenRemembered()
188 | {
189 | // make sure wrapped contracts are enabled
190 | Contract::EnforceWrappedContracts();
191 |
192 | $arg1 = __LINE__;
193 |
194 | Contract::Preconditions(function() use($arg1)
195 | {
196 | Contract::RememberOldValue('arg1', $arg1);
197 | });
198 |
199 | // do the test
200 | // we never remembered a value for arg2
201 | Contract::PostConditions(function($obj) use($arg1)
202 | {
203 | $obj->assertEquals($arg1, Contract::OldValue('arg1'));
204 | $obj->assertNull(Contract::OldValue('arg2'));
205 |
206 | // remember to forget the remembered values!
207 | Contract::ForgetOldValues();
208 | }, array($this));
209 | }
210 |
211 | public function testCanForgetValuesToSameMemory()
212 | {
213 | // make sure wrapped contracts are enabled
214 | Contract::EnforceWrappedContracts();
215 |
216 | // make sure there are no remembered scopes atm
217 | $this->assertEquals(0, count(Contract::_rememberedScopes()));
218 |
219 | // some test data
220 | $arg1 = 'fred';
221 | $arg2 = 'alice';
222 |
223 | // stash some values
224 | Contract::Preconditions(function() use ($arg1, $arg2)
225 | {
226 | Contract::RememberOldValue('arg1', $arg1);
227 | Contract::RememberOldValue('arg2', $arg2);
228 | });
229 |
230 | // make sure we have remembered the scope
231 | $this->assertEquals(1, count(Contract::_rememberedScopes()));
232 |
233 | // now, did we get the values?
234 | //
235 | // for this to work propery, we have to test it inside
236 | // the postconditions wrapper
237 | Contract::PostConditions(function($obj) use($arg1, $arg2)
238 | {
239 | $obj->assertEquals($arg1, Contract::OldValue('arg1'));
240 | $obj->assertEquals($arg2, Contract::OldValue('arg2'));
241 |
242 | // forget these values, freeing up the memory
243 | Contract::ForgetOldValues();
244 | }, array($this));
245 |
246 | // make sure we have forgotten the scope
247 | $this->assertEquals(0, count(Contract::_rememberedScopes()));
248 | }
249 |
250 | public function testScopeIsUniqueToCaller()
251 | {
252 | // how many scopes have previous tests left in memory?
253 | $currentScopeCount = count(Contract::_rememberedScopes());
254 |
255 | // remember some values, in their own unique scope
256 | // doing so increases the number of scopes that Contract::
257 | // now remembers
258 | $this->rememberSomeValues();
259 | $this->assertEquals($currentScopeCount + 1, count(Contract::_rememberedScopes()));
260 |
261 | // try to recall those values
262 | // this does not affect the number of scopes that Contact::
263 | // remembers
264 | $this->recallSomeValues();
265 | $this->assertEquals($currentScopeCount + 1, count(Contract::_rememberedScopes()));
266 |
267 | // let's remember those values now
268 | $this->rememberSomeValues(true);
269 | $this->assertEquals($currentScopeCount, count(Contract::_rememberedScopes()));
270 | }
271 |
272 | protected function rememberSomeValues($check = false)
273 | {
274 | if (!$check)
275 | {
276 | Contract::Preconditions(function()
277 | {
278 | // the scope for these values is unique
279 | Contract::RememberOldValue('arg1', __LINE__);
280 | Contract::RememberOldValue('arg2', __LINE__);
281 | });
282 | }
283 |
284 | if ($check)
285 | {
286 | Contract::Postconditions(function($obj)
287 | {
288 | $obj->assertNotNull(Contract::OldValue('arg1'));
289 | $obj->assertNotNull(Contract::OldValue('arg2'));
290 |
291 | // clean up after ourselves
292 | Contract::ForgetOldValues();
293 | }, array($this));
294 | }
295 | }
296 |
297 | protected function recallSomeValues()
298 | {
299 | Contract::PostConditions(function($obj)
300 | {
301 | // these values will be null, because they were
302 | // never ever set in this scope
303 | $obj->assertNull(Contract::OldValue('arg1'));
304 | $obj->assertNull(Contract::OldValue('arg2'));
305 | }, array($this));
306 | }
307 | }
--------------------------------------------------------------------------------
/src/tests/unit-tests/www/.empty:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stuartherbert/ContractLib/52fcad32b3f42bf862b08f47b502858274b98c88/src/tests/unit-tests/www/.empty
--------------------------------------------------------------------------------
/src/www/.empty:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stuartherbert/ContractLib/52fcad32b3f42bf862b08f47b502858274b98c88/src/www/.empty
--------------------------------------------------------------------------------