From 20ebb19fb79e7e7f67adb52e7bd420e9dfa1099c Mon Sep 17 00:00:00 2001 From: Adam Bullmer Date: Fri, 29 Apr 2016 03:35:30 -0500 Subject: [PATCH] Added Unit Testing --- .editorconfig | 11 ++ circle.yml | 11 ++ composer.json | 3 + php/RuleFormatException.php | 4 + php/StateNotFoundException.php | 4 + php/Validator.php | 119 +++++++++++++--- php/ValueException.php | 4 + phpunit.xml | 13 ++ test/ValidatorTest.php | 242 +++++++++++++++++++++++++++++++++ 9 files changed, 389 insertions(+), 22 deletions(-) create mode 100644 .editorconfig create mode 100644 circle.yml create mode 100644 php/RuleFormatException.php create mode 100644 php/StateNotFoundException.php create mode 100644 php/ValueException.php create mode 100644 phpunit.xml create mode 100644 test/ValidatorTest.php diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c3f6353 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/circle.yml b/circle.yml new file mode 100644 index 0000000..5c6b17f --- /dev/null +++ b/circle.yml @@ -0,0 +1,11 @@ +machine: + php: + version: 7.0.3 + +dependencies: + override: + - composer install + +test: + override: + - ./vendor/bin/phpunit --coverage-text diff --git a/composer.json b/composer.json index bd96327..2e51d56 100644 --- a/composer.json +++ b/composer.json @@ -15,5 +15,8 @@ }, "require": { "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "5.3.*" } } diff --git a/php/RuleFormatException.php b/php/RuleFormatException.php new file mode 100644 index 0000000..fc254c2 --- /dev/null +++ b/php/RuleFormatException.php @@ -0,0 +1,4 @@ + json_decode(file_get_contents(__DIR__.'/../regex.json'), TRUE), 'verbose' => FALSE, 'caseInsensitive' => FALSE, ); $options = array_merge($defaults, $options); + $this->rules = $options['rules']; $this->verbose = $options['verbose']; $this->caseInsensitive = $options['caseInsensitive']; } @@ -37,36 +42,106 @@ function __construct($options = array()) { * in the provided string. * * @param String $state 2 letter abbreviation of the state - * @param String $dl_string string representatino of the driver license number + * @param String $dlString string representatino of the driver license number * * @return Bool TRUE/FALSE if the number is valid */ - public function validate($state, $dl_string) { - global $rules; - - $state = strtoupper($state); - $rule = $rules[$state]['rule']; - $regexFlags = ''; + public function validate($state, $dlString) { + $rule = $this->__getRule($state); + $regexFlags = $this->__getValidatorFlags(); if ($this->verbose === TRUE) { - echo "Validating $dl_string using rule $rule\n"; + echo "Validating $dlString using rule $rule\n"; } - if ($this->caseInsensitive === TRUE) { - $regexFlags .= 'i'; - } + $isValid = (bool)preg_match("/$rule/$regexFlags", $dlString); - if (!preg_match("/$rule/$regexFlags", $dl_string)) { - if ($this->verbose === TRUE) { - echo "Didn't match against the following criteria:\n"; - foreach ($rules[$state]['description'] as $description) { - echo " $description \n"; - } + if ($isValid === FALSE && $this->verbose === TRUE) { + echo "Didn't match against the following criteria:\n"; + foreach ($this->__getDescriptions($state) as $description) { + echo " $description\n"; } + } + + return $isValid; + } + + /** + * Finds and returns the registered state definition. + * + * @param String $key State to lookup rules for + * + * @return Array Array of Rule and Descriptions + * + * @throws USDLRegex\ValueException No State key was provided + * @throws USDLRegex\StateNotFoundException Passed state doesn't exist in key + */ + private function __getState($key=NULL) { + if ($key === NULL) { + throw new ValueException('Must provide a key to get a rule'); + } + + $key = strtoupper($key); - return FALSE; + if (!array_key_exists($key, $this->rules)) { + throw new StateNotFoundException("State '$key' not found in rule set"); + } + + return $this->rules[$key]; + } + + /** + * Finds and returns the validation rule for the state. + * + * @param String $key State to lookup rules for + * + * @return String String representation of regex + * + * @throws USDLRegex\RuleFormatException 'rule' key doesn't exist for the passed + * state. + */ + private function __getRule($key=NULL) { + $state = $this->__getState($key); + + if (!array_key_exists('rule', $state)) { + throw new RuleFormatException("Malformed rules JSON: State '$key' does not have a 'rule' key."); + } + + return $state['rule']; + } + + /** + * Finds and returns the list of friendly descriptions for the state rule regex. + * + * @param String $key State to lookup rules for + * + * @return Array Array of strings with friendly regex rules + * + * @throws USDLRegex\RuleFormatException 'description' key doesn't exist for the + * passed state. + */ + private function __getDescriptions($key=NULL) { + $state = $this->__getState($key); + + if (!array_key_exists('description', $state)) { + throw new RuleFormatException("Malformed rules JSON: State '$key' does not have a 'description' key."); + } + + return $state['description']; + } + + /** + * Compiles a list of regex directives to be used while validating. + * + * @return String string of regex directives + */ + private function __getValidatorFlags() { + $validatorFlags = ''; + + if ($this->caseInsensitive === TRUE) { + $validatorFlags .= 'i'; } - return TRUE; + return $validatorFlags; } } diff --git a/php/ValueException.php b/php/ValueException.php new file mode 100644 index 0000000..1a6369e --- /dev/null +++ b/php/ValueException.php @@ -0,0 +1,4 @@ + + + + + ./test/ + + + + + ./php + + + diff --git a/test/ValidatorTest.php b/test/ValidatorTest.php new file mode 100644 index 0000000..75e8e6a --- /dev/null +++ b/test/ValidatorTest.php @@ -0,0 +1,242 @@ +getMethod($methodName); + $method->setAccessible(TRUE); + return $method; + } + + + public function test_constructor() { + $validator = new \USDLRegex\Validator(); + + $this->assertFalse($validator->verbose); + $this->assertFalse($validator->caseInsensitive); + $this->assertEquals(gettype($validator->rules), 'array'); + $this->assertCount(51, $validator->rules); + } + + public function rule_override_params() { + return array( + array(NULL, FALSE, NULL, FALSE, NULL, 51, 'TX'), + array(TRUE, TRUE, NULL, FALSE, NULL, 51, 'TX'), + array(FALSE, FALSE, NULL, FALSE, NULL, 51, 'TX'), + array(NULL, FALSE, TRUE, TRUE, NULL, 51, 'TX'), + array(TRUE, TRUE, TRUE, TRUE, NULL, 51, 'TX'), + array(FALSE, FALSE, TRUE, TRUE, NULL, 51, 'TX'), + array(NULL, FALSE, FALSE, FALSE, NULL, 51, 'TX'), + array(TRUE, TRUE, FALSE, FALSE, NULL, 51, 'TX'), + array(FALSE, FALSE, FALSE, FALSE, NULL, 51, 'TX'), + array(NULL, FALSE, NULL, FALSE, array('TEST_STATE' => array()), 1, 'TEST_STATE'), + array(TRUE, TRUE, NULL, FALSE, array('TEST_STATE' => array()), 1, 'TEST_STATE'), + array(FALSE, FALSE, NULL, FALSE, array('TEST_STATE' => array()), 1, 'TEST_STATE'), + array(NULL, FALSE, TRUE, TRUE, array('TEST_STATE' => array()), 1, 'TEST_STATE'), + array(TRUE, TRUE, TRUE, TRUE, array('TEST_STATE' => array()), 1, 'TEST_STATE'), + array(FALSE, FALSE, TRUE, TRUE, array('TEST_STATE' => array()), 1, 'TEST_STATE'), + array(NULL, FALSE, FALSE, FALSE, array('TEST_STATE' => array()), 1, 'TEST_STATE'), + array(TRUE, TRUE, FALSE, FALSE, array('TEST_STATE' => array()), 1, 'TEST_STATE'), + array(FALSE, FALSE, FALSE, FALSE, array('TEST_STATE' => array()), 1, 'TEST_STATE'), + ); + } + + /** + * @dataProvider rule_override_params + */ + public function test_constructor_rule_override($verbose, $verboseTest, $caseInsensitive, $caseInsensitiveTest, $rules, $rulesLengthTest, $rulesKeyTest) { + $options = array(); + + if ($verbose !== NULL) { + $options['verbose'] = $verbose; + } + if ($caseInsensitive !== NULL) { + $options['caseInsensitive'] = $caseInsensitive; + } + if ($rules !== NULL) { + $options['rules'] = $rules; + } + + + $validator = new \USDLRegex\Validator($options); + + $this->assertEquals($validator->verbose, $verboseTest); + $this->assertEquals($validator->caseInsensitive, $caseInsensitiveTest); + $this->assertEquals(gettype($validator->rules), 'array'); + $this->assertCount($rulesLengthTest, $validator->rules); + $this->assertArrayHasKey($rulesKeyTest, $validator->rules); + } + + public function test_validate() { + $validator = new \USDLRegex\Validator(); + $this->assertTrue($validator->validate('TX', '12345678')); + } + + public function test_validate_lowercase() { + $validator = new \USDLRegex\Validator(); + $this->assertTrue($validator->validate('tx', '12345678')); + } + + public function test_validate_output() { + $validator = new \USDLRegex\Validator(array( + 'verbose' => TRUE, + 'rules' => array( + 'DUMMY_STATE' => array( + 'rule' => "TEST_RULE", + 'description' => array( + "I'm a description" + ) + ) + ) + )); + $this->assertTrue($validator->validate('DUMMY_STATE', "TEST_RULE")); + + $this->expectOutputString("Validating TEST_RULE using rule TEST_RULE\n"); + } + + public function test_validate_output_failed() { + $validator = new \USDLRegex\Validator(array( + 'verbose' => TRUE, + 'rules' => array( + 'DUMMY_STATE' => array( + 'rule' => "TEST_RULE", + 'description' => array( + "description1", + "description2" + ) + ) + ) + )); + $this->assertFalse($validator->validate('DUMMY_STATE', "123")); + $this->expectOutputRegex("/(\\n description1\\n description2\\n)+/"); + } + + public function test_getState() { + $getState = self::getMethod('__getState'); + $validator = new \USDLRegex\Validator(array( + 'rules' => array( + 'DUMMY_STATE' => array( + 'rule' => "I'm a rule", + 'description' => array( + "I'm a description" + ) + ) + ) + )); + + $state = $getState->invokeArgs($validator, array('DUMMY_STATE')); + + $this->assertArrayHasKey('rule', $state); + $this->assertArrayHasKey('description', $state); + } + + /** + * @expectedException \USDLRegex\ValueException + */ + public function test_getState_failure() { + $getState = self::getMethod('__getState'); + $validator = new \USDLRegex\Validator(); + + $state = $getState->invokeArgs($validator, array()); + } + + /** + * @expectedException \USDLRegex\StateNotFoundException + */ + public function test_getState_not_found() { + $getState = self::getMethod('__getState'); + $validator = new \USDLRegex\Validator(); + + $state = $getState->invokeArgs($validator, array('FAKE_STATE')); + } + + public function test_getRule() { + $getRule = self::getMethod('__getRule'); + $validator = new \USDLRegex\Validator(array( + 'rules' => array( + 'DUMMY_STATE' => array( + 'rule' => "I'm a rule", + 'description' => array( + "I'm a description" + ) + ) + ) + )); + + $rule = $getRule->invokeArgs($validator, array('DUMMY_STATE')); + + $this->assertEquals("I'm a rule", $rule); + } + + /** + * @expectedException \USDLRegex\RuleFormatException + */ + public function test_getRule_malformed() { + $getRule = self::getMethod('__getRule'); + $validator = new \USDLRegex\Validator(array( + 'rules' => array( + 'DUMMY_STATE' => array( + 'description' => array( + "I'm a description" + ) + ) + ) + )); + + $rule = $getRule->invokeArgs($validator, array('DUMMY_STATE')); + } + + public function test_getDescriptions() { + $getDescriptions = self::getMethod('__getDescriptions'); + $validator = new \USDLRegex\Validator(array( + 'rules' => array( + 'DUMMY_STATE' => array( + 'rule' => "I'm a rule", + 'description' => array( + "I'm a description" + ) + ) + ) + )); + + $description = $getDescriptions->invokeArgs($validator, array('DUMMY_STATE')); + + $this->assertEquals(gettype($description), 'array'); + $this->assertEquals("I'm a description", $description[0]); + } + + /** + * @expectedException \USDLRegex\RuleFormatException + */ + public function test_getDescription_malformed() { + $getDescriptions = self::getMethod('__getDescriptions'); + $validator = new \USDLRegex\Validator(array( + 'rules' => array( + 'DUMMY_STATE' => array( + 'rule' => "I'm a rule", + ) + ) + )); + + $rule = $getDescriptions->invokeArgs($validator, array('DUMMY_STATE')); + } + + public function validatorFlagProvider() { + return array( + array(array('caseInsensitive' => FALSE), ''), + array(array('caseInsensitive' => TRUE), 'i'), + ); + } + + /** + * @dataProvider validatorFlagProvider + */ + public function test_getValidatorFlags($options, $expectedFlags) { + $getValidatorFlags = self::getMethod('__getValidatorFlags'); + $validator = new \USDLRegex\Validator($options); + + $flagsString = $getValidatorFlags->invokeArgs($validator, array()); + + $this->assertEquals($flagsString, $expectedFlags); + } +}