From a50369e71ee3373da5e686df82fe7d0db0426155 Mon Sep 17 00:00:00 2001 From: Danack Date: Sat, 19 Jul 2014 14:37:48 +0100 Subject: [PATCH] Tests, Travis and Cache implementations - Add tests - Configure repo for use with Travis - Add some more Cache implementations --- .travis.yml | 14 + README.md | 3 + composer.json | 9 +- composer.lock | 970 ++++++++++++++++++++++ lib/Addr/Cache.php | 35 - lib/Addr/Client.php | 49 +- lib/Addr/MemoryCache.php | 62 -- lib/Addr/ResolverFactory.php | 6 +- lib/AddrCache/APCCache.php | 55 ++ lib/AddrCache/Cache.php | 34 + lib/AddrCache/MemoryCache.php | 75 ++ lib/AddrCache/RedisCache.php | 89 ++ test/AddrTest/AddrTest.php | 116 +++ test/AddrTest/CacheTest.php | 176 ++++ test/AddrTest/ResponseInterpreterTest.php | 45 + test/phpunit.xml | 41 + test/testBootstrap.php | 92 ++ 17 files changed, 1754 insertions(+), 117 deletions(-) create mode 100644 .travis.yml create mode 100644 composer.lock delete mode 100644 lib/Addr/Cache.php delete mode 100644 lib/Addr/MemoryCache.php create mode 100644 lib/AddrCache/APCCache.php create mode 100644 lib/AddrCache/Cache.php create mode 100644 lib/AddrCache/MemoryCache.php create mode 100644 lib/AddrCache/RedisCache.php create mode 100644 test/AddrTest/AddrTest.php create mode 100644 test/AddrTest/CacheTest.php create mode 100644 test/AddrTest/ResponseInterpreterTest.php create mode 100644 test/phpunit.xml create mode 100644 test/testBootstrap.php diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..122e280 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +language: php + +git: + submodules: false + +php: + - 5.5 + - 5.4 + +before_script: + - composer install + +script: php ./vendor/bin/phpunit --configuration test/phpunit.xml --coverage-text + diff --git a/README.md b/README.md index e05ffa9..eb0da3f 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,6 @@ Addr ==== Asynchronous DNS resolver using [Alert](https://github.com/rdlowrey/Alert). + + +[![Build Status](https://travis-ci.org/Danack/Addr.svg?branch=AddingTestsAndActualCaches)](https://travis-ci.org/Danack/Addr) \ No newline at end of file diff --git a/composer.json b/composer.json index 60af0e9..6b5a25d 100644 --- a/composer.json +++ b/composer.json @@ -16,9 +16,16 @@ "rdlowrey/alert": "~0.8.1", "daverandom/libdns": "~0.2.1" }, + "require-dev": { + "mockery/mockery": ">=0.9.1", + "rdlowrey/auryn": ">=0.13.0", + "predis/predis": "dev-v0.9/phpdoc-method-tags as v0.8.4", + "phpunit/phpunit": "~4.1.0" + }, "autoload": { "psr-4": { - "Addr\\": "lib/Addr/" + "Addr\\": "lib/Addr/", + "AddrCache\\": "lib/AddrCache/" } } } diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..bd0a015 --- /dev/null +++ b/composer.lock @@ -0,0 +1,970 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "6ac53a76066ea6f5b5a4b6621ca23adb", + "packages": [ + { + "name": "daverandom/libdns", + "version": "v0.2.1", + "source": { + "type": "git", + "url": "https://github.com/DaveRandom/LibDNS.git", + "reference": "f94f92f3fa8b2776ff2593c87d93d87480efeb73" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DaveRandom/LibDNS/zipball/f94f92f3fa8b2776ff2593c87d93d87480efeb73", + "reference": "f94f92f3fa8b2776ff2593c87d93d87480efeb73", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "LibDNS": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "DNS protocol implementation written in pure PHP", + "keywords": [ + "dns" + ], + "time": "2013-11-28 15:57:15" + }, + { + "name": "rdlowrey/alert", + "version": "v0.8.1", + "source": { + "type": "git", + "url": "https://github.com/rdlowrey/alert.git", + "reference": "a09cc62f81d8950e05e6c715668be3cb2a07d716" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rdlowrey/alert/zipball/a09cc62f81d8950e05e6c715668be3cb2a07d716", + "reference": "a09cc62f81d8950e05e6c715668be3cb2a07d716", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net", + "role": "Creator / Lead Developer" + } + ], + "description": "Event loops for event-driven/async PHP", + "homepage": "https://github.com/rdlowrey/alert", + "keywords": [ + "async", + "events", + "libevent", + "non-blocking" + ], + "time": "2014-06-11 14:09:10" + } + ], + "packages-dev": [ + { + "name": "mockery/mockery", + "version": "0.9.1", + "source": { + "type": "git", + "url": "https://github.com/padraic/mockery.git", + "reference": "17f63ee40ed14a8afb7ba1f0ae15cc4491d719d1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/padraic/mockery/zipball/17f63ee40ed14a8afb7ba1f0ae15cc4491d719d1", + "reference": "17f63ee40ed14a8afb7ba1f0ae15cc4491d719d1", + "shasum": "" + }, + "require": { + "lib-pcre": ">=7.0", + "php": ">=5.3.2" + }, + "require-dev": { + "hamcrest/hamcrest-php": "~1.1", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "~0.7@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.9.x-dev" + } + }, + "autoload": { + "psr-0": { + "Mockery": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "http://blog.astrumfutura.com" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "http://davedevelopment.co.uk" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succint API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.", + "homepage": "http://github.com/padraic/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "time": "2014-05-02 12:16:45" + }, + { + "name": "phpunit/php-code-coverage", + "version": "2.0.9", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "ed8ac99ce38c3fd134128c898f7ca74665abef7f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ed8ac99ce38c3fd134128c898f7ca74665abef7f", + "reference": "ed8ac99ce38c3fd134128c898f7ca74665abef7f", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": "~1.3.1", + "phpunit/php-text-template": "~1.2.0", + "phpunit/php-token-stream": "~1.2.2", + "sebastian/environment": "~1.0.0", + "sebastian/version": "~1.0.3" + }, + "require-dev": { + "ext-xdebug": ">=2.1.4", + "phpunit/phpunit": "~4.0.14" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.2.1", + "ext-xmlwriter": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2014-06-29 08:14:40" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.3.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb", + "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "File/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2013-10-10 15:34:57" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", + "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "Text/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2014-01-30 17:20:04" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c", + "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2013-08-02 07:42:54" + }, + { + "name": "phpunit/php-token-stream", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/ad4e1e23ae01b483c16f600ff1bebec184588e32", + "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2014-03-03 05:10:30" + }, + { + "name": "phpunit/phpunit", + "version": "4.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "a71c4842c5fb836d8b200624583b859ec34e8a26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a71c4842c5fb836d8b200624583b859ec34e8a26", + "reference": "a71c4842c5fb836d8b200624583b859ec34e8a26", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpunit/php-code-coverage": "~2.0", + "phpunit/php-file-iterator": "~1.3.1", + "phpunit/php-text-template": "~1.2", + "phpunit/php-timer": "~1.0.2", + "phpunit/phpunit-mock-objects": "~2.1", + "sebastian/comparator": "~1.0", + "sebastian/diff": "~1.1", + "sebastian/environment": "~1.0", + "sebastian/exporter": "~1.0", + "sebastian/version": "~1.0", + "symfony/yaml": "~2.0" + }, + "suggest": { + "phpunit/php-invoker": "~1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "", + "../../symfony/yaml/" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2014-07-18 07:15:58" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "2.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "7878b9c41edb3afab92b85edf5f0981014a2713a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/7878b9c41edb3afab92b85edf5f0981014a2713a", + "reference": "7878b9c41edb3afab92b85edf5f0981014a2713a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-text-template": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.1" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2014-06-12 07:22:15" + }, + { + "name": "predis/predis", + "version": "dev-v0.9/phpdoc-method-tags", + "source": { + "type": "git", + "url": "https://github.com/nrk/predis.git", + "reference": "6741b788cc2c8b37bd62c6ed5d38a671940993c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nrk/predis/zipball/6741b788cc2c8b37bd62c6ed5d38a671940993c3", + "reference": "6741b788cc2c8b37bd62c6ed5d38a671940993c3", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "suggest": { + "ext-curl": "Allows access to Webdis when paired with phpiredis", + "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol" + }, + "type": "library", + "autoload": { + "psr-0": { + "Predis": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniele Alessandri", + "email": "suppakilla@gmail.com", + "homepage": "http://clorophilla.net" + } + ], + "description": "Flexible and feature-complete PHP client library for Redis", + "homepage": "http://github.com/nrk/predis", + "keywords": [ + "nosql", + "predis", + "redis" + ], + "time": "2013-12-21 17:28:51" + }, + { + "name": "rdlowrey/auryn", + "version": "v0.13.0", + "source": { + "type": "git", + "url": "https://github.com/rdlowrey/Auryn.git", + "reference": "c83b741ccddf3b8e91f02dbdc5b0eeecbf20f09f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rdlowrey/Auryn/zipball/c83b741ccddf3b8e91f02dbdc5b0eeecbf20f09f", + "reference": "c83b741ccddf3b8e91f02dbdc5b0eeecbf20f09f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Levi Morrison", + "email": "levim@php.net", + "homepage": "http://morrisonlevi.github.com/", + "role": "Developer" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@gmail.com", + "role": "Creator / Lead Developer" + }, + { + "name": "Dan Ackroyd", + "email": "Danack@basereality.com", + "homepage": "http://www.basereality.com", + "role": "Developer" + } + ], + "description": "Auryn is a dependency injector for bootstrapping object-oriented PHP applications.", + "homepage": "https://github.com/rdlowrey/Auryn", + "keywords": [ + "dependency injection", + "dic", + "ioc" + ], + "time": "2014-04-01 12:48:07" + }, + { + "name": "sebastian/comparator", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "f7069ee51fa9fb6c038e16a9d0e3439f5449dcf2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/f7069ee51fa9fb6c038e16a9d0e3439f5449dcf2", + "reference": "f7069ee51fa9fb6c038e16a9d0e3439f5449dcf2", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.1", + "sebastian/exporter": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2014-05-02 07:05:58" + }, + { + "name": "sebastian/diff", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "1e091702a5a38e6b4c1ba9ca816e3dd343df2e2d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/1e091702a5a38e6b4c1ba9ca816e3dd343df2e2d", + "reference": "1e091702a5a38e6b4c1ba9ca816e3dd343df2e2d", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "http://www.github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2013-08-03 16:46:33" + }, + { + "name": "sebastian/environment", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "79517609ec01139cd7e9fded0dd7ce08c952ef6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/79517609ec01139cd7e9fded0dd7ce08c952ef6a", + "reference": "79517609ec01139cd7e9fded0dd7ce08c952ef6a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "4.0.*@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2014-02-18 16:17:19" + }, + { + "name": "sebastian/exporter", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "1f9a98e6f5dfe0524cb8c6166f7c82f3e9ae1529" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/1f9a98e6f5dfe0524cb8c6166f7c82f3e9ae1529", + "reference": "1f9a98e6f5dfe0524cb8c6166f7c82f3e9ae1529", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "4.0.*@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net", + "role": "Lead" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2014-02-16 08:26:31" + }, + { + "name": "sebastian/version", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43", + "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43", + "shasum": "" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2014-03-07 15:35:33" + }, + { + "name": "symfony/yaml", + "version": "v2.5.2", + "target-dir": "Symfony/Component/Yaml", + "source": { + "type": "git", + "url": "https://github.com/symfony/Yaml.git", + "reference": "f868ecdbcc0276b6158dfbf08b9e98ce07f014e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/f868ecdbcc0276b6158dfbf08b9e98ce07f014e1", + "reference": "f868ecdbcc0276b6158dfbf08b9e98ce07f014e1", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Yaml\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "http://symfony.com", + "time": "2014-07-09 09:05:48" + } + ], + "aliases": [ + { + "alias": "v0.8.4", + "alias_normalized": "0.8.4.0", + "version": "dev-v0.9/phpdoc-method-tags", + "package": "predis/predis" + } + ], + "minimum-stability": "stable", + "stability-flags": { + "predis/predis": 20 + }, + "platform": { + "php": ">=5.4.0" + }, + "platform-dev": [ + + ] +} diff --git a/lib/Addr/Cache.php b/lib/Addr/Cache.php deleted file mode 100644 index b15bb38..0000000 --- a/lib/Addr/Cache.php +++ /dev/null @@ -1,35 +0,0 @@ -redirectPendingLookup($id, $addr); } } else if ($addr !== null) { - if ($this->cache) { - $this->cache->store($name, $addr, $type, $ttl); - } - + $this->store($name, $addr, $type, $ttl); $this->completeRequest($request, $addr, $type); } else { foreach ($request['lookups'] as $id => $lookup) { @@ -255,6 +252,28 @@ class Client } } + /** + * Generates the cache key used to store the result for hostname + * and type. + * @param $name + * @param $type + * @return string + */ + function generateCacheKey($name, $type) { + return 'Name:'.$name.',Type:'.$type; + } + + /** + * @param $name + * @param $addr + * @param $type + * @param $ttl + */ + public function store($name, $addr, $type, $ttl) { + $key = $this->generateCacheKey($name, $type); + $this->cache->store($key, $addr, $ttl); + } + /** * Call a response callback with the result * @@ -300,19 +319,17 @@ class Client $name = $this->pendingLookups[$id]['name']; $type = array_shift($this->pendingLookups[$id]['requests']); - if ($this->cache) { - $this->cache->resolve($name, $type, function($addr) use ($id, $name, $type) { - if ($addr !== null) { - $this->completePendingLookup($id, $addr, $type); - } else { - $this->dispatchRequest($id, $name, $type); - } - }); + $key = $this->generateCacheKey($name, $type); + list($cacheHit, $addr) = $this->cache->get($key); + + if ($cacheHit === true) { + $this->completePendingLookup($id, $addr, $type); } else { $this->dispatchRequest($id, $name, $type); } } - + + /** * Send a request to the server * diff --git a/lib/Addr/MemoryCache.php b/lib/Addr/MemoryCache.php deleted file mode 100644 index 5d8e3e8..0000000 --- a/lib/Addr/MemoryCache.php +++ /dev/null @@ -1,62 +0,0 @@ - [], - AddressModes::INET6_ADDR => [], - ]; - - /** - * Look up an entry in the cache - * - * @param string $name - * @param int $type - * @param callable $callback - */ - public function resolve($name, $type, callable $callback) - { - if (!isset($this->data[$type][$name]) || $this->data[$type][$name][1] < time()) { - unset($this->data[$type][$name]); - $callback(null); - } else { - $callback($this->data[$type][$name][0]); - } - } - - /** - * Store an entry in the cache - * - * @param string $name - * @param string $addr - * @param int $type - * @param int $ttl - */ - public function store($name, $addr, $type, $ttl) - { - $this->data[$type][$name] = [$addr, time() + $ttl]; - } - - /** - * Remove expired records from the cache - */ - public function collectGarbage() - { - $now = time(); - - foreach ([AddressModes::INET4_ADDR, AddressModes::INET6_ADDR] as $type) { - while (list($name, $data) = each($this->data[$type])) { - if ($data[1] < $now) { - unset($this->data[$type][$name]); - } - } - } - } -} diff --git a/lib/Addr/ResolverFactory.php b/lib/Addr/ResolverFactory.php index 3ae0fef..cd862c7 100644 --- a/lib/Addr/ResolverFactory.php +++ b/lib/Addr/ResolverFactory.php @@ -17,7 +17,7 @@ class ResolverFactory * @param string $serverAddr * @param int $serverPort * @param int $requestTimeout - * @param Cache $cache + * @param \AddrCache\Cache $cache * @param string $hostsFilePath * @return Resolver */ @@ -26,11 +26,11 @@ class ResolverFactory $serverAddr = null, $serverPort = null, $requestTimeout = null, - Cache $cache = null, + \AddrCache\Cache $cache = null, $hostsFilePath = null ) { $nameValidator = new NameValidator; - $cache = $cache ?: new MemoryCache; + $cache = $cache ?: new \AddrCache\MemoryCache; $client = new Client( $reactor, diff --git a/lib/AddrCache/APCCache.php b/lib/AddrCache/APCCache.php new file mode 100644 index 0000000..c66c014 --- /dev/null +++ b/lib/AddrCache/APCCache.php @@ -0,0 +1,55 @@ +prefix = $prefix; + } + + /** + * Attempt to retrieve a value from the cache + * + * Returns an array [$cacheHit, $value] + * [true, $valueFromCache] - if it existed in the cache + * [false, null] - if it didn't already exist in the cache + * + * @param $key + * @return array + */ + public function get($key) { + $key = $this->prefix.$key; + $value = apc_fetch($key, $success); + + if ($success) { + return [true, $value]; + } + return [false, null]; + } + + /** + * Stores a value in the cache. Overwrites the previous value if there was one. + * + * @param $key + * @param $value + * @param null $ttl + */ + public function store($key, $value, $ttl = null) { + $key = $this->prefix.$key; + apc_store($key, $value, $ttl); + } + + /** + * Deletes an entry from the cache. + * @param $key + */ + public function delete($key) { + $key = $this->prefix.$key; + apc_delete($key); + } +} \ No newline at end of file diff --git a/lib/AddrCache/Cache.php b/lib/AddrCache/Cache.php new file mode 100644 index 0000000..f6e9805 --- /dev/null +++ b/lib/AddrCache/Cache.php @@ -0,0 +1,34 @@ +valueAndTTLArray) == false) { + return [false, null]; + } + + list($value, $expireTime) = $this->valueAndTTLArray[$key]; + + if ($expireTime <= time()) { + return [false, null]; //It's already expired, so don't return cached value; + } + + return [true, $value]; + } + + /** + * Stores a value in the cache. Overwrites the previous value if there was one. + * + * @param $key + * @param $value + * @param null $ttl + */ + public function store($key, $value, $ttl = null) { + if ($ttl === null) { + $ttl = self::MAX_TTL; + } + + $this->valueAndTTLArray[$key] = [$value, time() + $ttl]; + } + + /** + * Deletes an entry from the cache. + * @param $key + */ + public function delete($key) { + unset($this->valueAndTTLArray[$key]); + } + + /** + * Remove expired records from the cache + */ + public function collectGarbage() + { + $now = time(); + + foreach ($this->valueAndTTLArray as $key => $valueAndTTL) { + if ($valueAndTTL[1] <= $now) { + unset($this->valueAndTTLArray); + } + } + } +} + + \ No newline at end of file diff --git a/lib/AddrCache/RedisCache.php b/lib/AddrCache/RedisCache.php new file mode 100644 index 0000000..56a9733 --- /dev/null +++ b/lib/AddrCache/RedisCache.php @@ -0,0 +1,89 @@ +redisClient = $redisClient; + $this->prefix = $prefixKey; + } + + /** + * Stores a value in the cache. Overwrites the previous value if there was one. + * + * @param $key + * @param $value + * @param null $ttl + */ + public function store($key, $value, $ttl = null) { + $key = $this->prefix.$key; + $ttl = intval($ttl); + if ($ttl > 0) { + $this->redisClient->set($key, $value, 'EX', $ttl); + } + else { + $this->redisClient->set($key, $value); + } + } + + + /** + * Attempt to retrieve a value from the cache + * + * Returns an array [$cacheHit, $value] + * [true, $valueFromCache] - if it existed in the cache + * [false, null] - if it didn't already exist in the cache + * + * @param $key + * @return array + */ + public function get($key) { + $key = $this->prefix.$key; + list($wasHit, $value) = $this->redisClient->eval(self::getLuaScript, 1, $key); + if ($wasHit) { + return [true, $value]; + } + + return [false, null]; + } + + + /** + * @param $key + */ + public function delete($key) { + $key = $this->prefix.$key; + $this->redisClient->del([$key]); + } +} + + \ No newline at end of file diff --git a/test/AddrTest/AddrTest.php b/test/AddrTest/AddrTest.php new file mode 100644 index 0000000..c02b41b --- /dev/null +++ b/test/AddrTest/AddrTest.php @@ -0,0 +1,116 @@ + 2, + 'read_write_timeout' => 2, + ); + + public static function setUpBeforeClass() { + try { + $predisClient = new \Predis\Client(self::$redisParameters, []); + $predisClient->ping(); + //It's connected + } + catch (\Predis\Connection\ConnectionException $ce) { + self::$redisEnabled = false; + } + } + + function testWithNullCache() { + $this->basicRun(null); + } + + function testWithMemoryCache() { + $memoryCache = new \AddrCache\MemoryCache(); + $this->basicRun($memoryCache); + } + + /** + * @requires extension APC + */ + function testWithApcCache() { + $prefix = time().uniqid('CacheTest'); + $apcCache = new \AddrCache\APCCache($prefix); + $this->basicRun($apcCache); + } + + + function testWithRedisCache() { + if (self::$redisEnabled != true) { + $this->markTestSkipped("Could not connect to Redis, skipping test."); + return; + } + + $prefix = time().'_'.uniqid('CacheTest'); + try { + $predisClient = new \Predis\Client(self::$redisParameters, []); + } + catch (\Predis\Connection\ConnectionException $ce) { + $this->markTestIncomplete("Could not connect to Redis server, cannot test redis cache."); + return; + } + + $redisCache = new \AddrCache\RedisCache($predisClient, $prefix); + $this->basicRun($redisCache); + } + + + /** + * @group internet + */ + function basicRun(\AddrCache\Cache $cache = null) { + $names = [ + 'google.com', + 'github.com', + 'stackoverflow.com', + 'localhost', + '192.168.0.1', + '::1', + ]; + + $reactor = (new ReactorFactory)->select(); + $resolver = (new ResolverFactory)->createResolver( + $reactor, + null, // $serverAddr = null, + null, //$serverPort = null, + null, //$requestTimeout = null, + $cache, + null //$hostsFilePath = null + ); + + $results = []; + + foreach ($names as $name) { + $resolver->resolve($name, function($addr) use($name, $resolver, &$results) { + $results[$name] = $addr; + }); + } + + $reactor->run(); + + foreach ($results as $name => $addr) { + $validIP = filter_var($addr, FILTER_VALIDATE_IP); + $this->assertNotFalse( + $validIP, + "Server name $name did not resolve to a valid IP address" + ); + } + + $this->assertCount( + count($names), + $results, + "At least one of the name lookups did not resolve." + ); + + } +} diff --git a/test/AddrTest/CacheTest.php b/test/AddrTest/CacheTest.php new file mode 100644 index 0000000..7c45a87 --- /dev/null +++ b/test/AddrTest/CacheTest.php @@ -0,0 +1,176 @@ + 2, + 'read_write_timeout' => 2, + ); + + public static function setUpBeforeClass() { + try { + $predisClient = new \Predis\Client(self::$redisParameters, []); + $predisClient->ping(); + //It's connected + } + catch (\Predis\Connection\ConnectionException $ce) { + self::$redisEnabled = false; + } + } + + + /** + * Create a mocked cache from the interface, and test that it works + * according to it's spec. + */ + function testCacheWorks() { + + $mock = \Mockery::mock('AddrCache\Cache'); + + $cacheValues = []; + + $cacheGetFunction = function($key) use (&$cacheValues) { + if (array_key_exists($key, $cacheValues)) { + return [true, $cacheValues[$key]]; + } + return [false, null]; + }; + + $cacheStoreFunction = function($key, $value, $ttl = null) use (&$cacheValues) { + $cacheValues[$key] = $value; + }; + + $cacheDeleteFunction = function($key) use (&$cacheValues) { + unset($cacheValues[$key]); + }; + + $mock->shouldReceive('get')->withAnyArgs()->andReturnUsing($cacheGetFunction); + $mock->shouldReceive('store')->withAnyArgs()->andReturnUsing($cacheStoreFunction); + $mock->shouldReceive('delete')->withAnyArgs()->andReturnUsing($cacheDeleteFunction); + + $this->runCacheTest($mock); + } + + + /** + * Test that the APC cache works as expected. Skipped if APC is not available. + * + * @requires extension APC + */ + function testAPCCache() { + $result = @apc_cache_info(); + + if ($result === false) { + $this->markTestSkipped("APC does not appear to be functioning, skipping test testAPCCache."); + return; + } + + $prefix = time().uniqid('CacheTest'); + $apcCache = new \AddrCache\APCCache($prefix); + $this->runCacheTest($apcCache); + } + + /** + * Test the redis cache works as expected. + */ + function testRedisCache() { + + if (self::$redisEnabled == false) { + $this->markTestSkipped("Could not connect to Redis, skipping test."); + return; + } + + $prefix = time().'_'.uniqid('CacheTest'); + try { + $predisClient = new \Predis\Client(self::$redisParameters, []); + } + catch (\Predis\Connection\ConnectionException $ce) { + $this->markTestIncomplete("Could not connect to Redis server, cannot test redis cache."); + return; + } + + $redisCache = new \AddrCache\RedisCache($predisClient, $prefix); + $this->runCacheTest($redisCache); + } + + + function testMemoryCache() { + $memoryCache = new \AddrCache\MemoryCache; + $this->runCacheTest($memoryCache); + } + + /** + * Runs the actual test against an instance of a cache. + * @param \AddrCache\Cache $cache + */ + function runCacheTest(\AddrCache\Cache $cache) { + $key = 'TestKey'; + $value = '12345'; + $secondValue = '54321'; + + list($alreadyExisted, $retrievedValue) = $cache->get($key); + $this->assertFalse($alreadyExisted); + $this->assertNull($retrievedValue); + + $cache->store($key, $value); + + list($alreadyExisted, $retrievedValue) = $cache->get($key); + $this->assertTrue($alreadyExisted); + $this->assertEquals($value, $retrievedValue); + + $cache->delete($key); + + list($alreadyExisted, $retrievedValue) = $cache->get($key); + $this->assertFalse($alreadyExisted); + $this->assertNull($retrievedValue); + + $cache->store($key, $secondValue); + + list($alreadyExisted, $retrievedValue) = $cache->get($key); + $this->assertTrue($alreadyExisted); + $this->assertEquals($secondValue, $retrievedValue); + } + + + function testMemoryCacheGarbageCollection() { + + $key = "TestKey"; + $value = '12345'; + + $memoryCache = new \AddrCache\MemoryCache; + + //A TTL of zero should be expired instantly + $memoryCache->store($key, $value, 0); + list($cacheHit, $cachedValue) = $memoryCache->get($key); + $this->assertFalse($cacheHit); + + + //A negative TTL oshould be expired instantly + $memoryCache->store($key, $value, -5); + list($cacheHit, $cachedValue) = $memoryCache->get($key); + $this->assertFalse($cacheHit); + + + //A positive TTL should be cached + $memoryCache->store($key, $value, 5); + list($cacheHit, $cachedValue) = $memoryCache->get($key); + $this->assertTrue($cacheHit); + $this->assertEquals($value, $cachedValue); + + + //check that garbage collection collects. + $memoryCache->store($key, $value, 0); + $memoryCache->collectGarbage(); + + //TODO - check that the memoryCache contains a single item + } +} + + + + + \ No newline at end of file diff --git a/test/AddrTest/ResponseInterpreterTest.php b/test/AddrTest/ResponseInterpreterTest.php new file mode 100644 index 0000000..18f25e6 --- /dev/null +++ b/test/AddrTest/ResponseInterpreterTest.php @@ -0,0 +1,45 @@ +shouldReceive('decode')->withAnyArgs()->andThrow("Exception", "Testing bad packet"); + $responseInterpreter = new ResponseInterpreter($decoder); + $result = $responseInterpreter->decode("SomePacket"); + $this->assertNull($result); + } + + function testInvalidMessage() { + $message = \Mockery::mock('LibDNS\Messages\Message'); + $message->shouldReceive('getType')->once()->andReturn(\LibDNS\Messages\MessageTypes::QUERY); + + $decoder = \Mockery::mock('LibDNS\Decoder\Decoder'); + $decoder->shouldReceive('decode')->once()->andReturn($message); + + $responseInterpreter = new ResponseInterpreter($decoder); + $result = $responseInterpreter->decode("SomePacket"); + $this->assertNull($result); + } + + function testInvalidResponseCode() { + $message = \Mockery::mock('LibDNS\Messages\Message'); + $message->shouldReceive('getType')->once()->andReturn(\LibDNS\Messages\MessageTypes::RESPONSE); + $message->shouldReceive('getResponseCode')->once()->andReturn(42); + + $decoder = \Mockery::mock('LibDNS\Decoder\Decoder'); + $decoder->shouldReceive('decode')->once()->andReturn($message); + + $responseInterpreter = new ResponseInterpreter($decoder); + $result = $responseInterpreter->decode("SomePacket"); + $this->assertNull($result); + } +} + + \ No newline at end of file diff --git a/test/phpunit.xml b/test/phpunit.xml new file mode 100644 index 0000000..c8db489 --- /dev/null +++ b/test/phpunit.xml @@ -0,0 +1,41 @@ + + + + + + + SQL + + + + + + ../lib + + + + + + ./AddrTest/ + + + + + + + + + + + + + + + + + + + + diff --git a/test/testBootstrap.php b/test/testBootstrap.php new file mode 100644 index 0000000..0037085 --- /dev/null +++ b/test/testBootstrap.php @@ -0,0 +1,92 @@ +add('AddrTest', [realpath(__DIR__)."/"]); + + + +function createProvider($implementations = array(), $shareClasses = array()) { + + $provider = new Provider(); + + $standardImplementations = [ + ]; + + $standardShares = [ + ]; + + $redisParameters = array( + 'connection_timeout' => 2, + 'read_write_timeout' => 2, + ); + + $provider->define( + 'Predis\Client', + array( + ':parameters' => $redisParameters, + ':options' => [], + ) + ); + + $provider->share('Predis\Client'); + + setImplementations($provider, $standardImplementations, $implementations); + setShares($provider, $standardShares, $shareClasses); + $provider->share($provider); //YOLO + + return $provider; + +} + + +function setImplementations(Provider $provider, $standardImplementations, $implementations) { + foreach ($standardImplementations as $interface => $implementation) { + if (array_key_exists($interface, $implementations)) { + if (is_object($implementations[$interface]) == true) { + $provider->alias($interface, get_class($implementations[$interface])); + $provider->share($implementations[$interface]); + } + else { + $provider->alias($interface, $implementations[$interface]); + } + unset($implementations[$interface]); + } + else { + if (is_object($implementation)) { + $implementation = get_class($implementation); + } + $provider->alias($interface, $implementation); + } + } + + foreach ($implementations as $class => $implementation) { + if (is_object($implementation) == true) { + $provider->alias($class, get_class($implementation)); + $provider->share($implementation); + } + else { + $provider->alias($class, $implementation); + } + } +} + +function setShares(Provider $provider, $standardShares, $shareClasses) { + foreach ($standardShares as $class => $share) { + if (array_key_exists($class, $shareClasses)) { + $provider->share($shareClasses[$class]); + unset($shareClasses[$class]); + } + else { + $provider->share($share); + } + } + + foreach ($shareClasses as $class => $share) { + $provider->share($share); + } +} \ No newline at end of file