Permalink
Browse files

Initial commit of existing shunt library files.

  • Loading branch information...
0 parents commit df942ed88e300c3ab001be62efedc405cf1a9962 @jadell committed May 1, 2009
Showing with 1,241 additions and 0 deletions.
  1. +72 −0 Shunt.php
  2. +102 −0 ShuntClass.php
  3. +30 −0 ShuntException.php
  4. +177 −0 ShuntMethod.php
  5. +170 −0 tests/ShuntClassTest.php
  6. +320 −0 tests/ShuntMethodTest.php
  7. +370 −0 tests/ShuntTest.php
72 Shunt.php
@@ -0,0 +1,72 @@
+<?php
+/**
+ * Copyright 2009 iContact Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @package Shunt
+ * @author Josh Adell <jadell@icontact.com>
+ * @version $Id$
+ */
+
+/**
+ * Generates classes that provide access to protected properties and methods
+ */
+class Shunt
+{
+ /**
+ * Generates a shunt class for testing
+ *
+ * @param string $sClassName required=true class to provide access to
+ * @return string name of shunt class that was created
+ * @throws Exception if the given class does not exist
+ */
+ public static function generate($sClassName)
+ {
+ if (!class_exists($sClassName)) {
+ throw new ShuntException("Class [{$sClassName}] can not be shunted; it does not exist.", ShuntException::ClassNotFound);
+ }
+
+ $oShuntClass = new ShuntClass($sClassName);
+ $sShuntClassName = $oShuntClass->getName();
+
+ if (!class_exists($sShuntClassName)) {
+ $sClassDefinition = $oShuntClass->getDeclarationCode();
+ eval($sClassDefinition);
+ }
+
+ return $sShuntClassName;
+ }
+
+ /**
+ * Generate the shunt class and return an instantiation of it
+ *
+ * @param string $sClassName required=true class to provide access to
+ * @param array $aConstructorArgs required=false
+ * @return object instantiated from the args passes
+ * @throws Exception if the given class does not exist
+ */
+ public static function get($sClassName, $aConstructorArgs=array())
+ {
+ $sShuntName = self::generate($sClassName);
+ $oClass = new ReflectionClass($sShuntName);
+
+ $oConstructor = $oClass->getConstructor();
+ if ($oConstructor) {
+ return $oClass->newInstanceArgs($aConstructorArgs);
+ } else {
+ return $oClass->newInstance();
+ }
+ }
+}
+?>
102 ShuntClass.php
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Copyright 2009 iContact Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @package Shunt
+ * @author Josh Adell <jadell@icontact.com>
+ * @version $Id$
+ */
+
+/**
+ * Generates a shunt of a specific class
+ */
+class ShuntClass extends ReflectionClass
+{
+ const ClassPrefix = '';
+ const ClassSuffix = 'Shunt';
+
+ /**
+ * Return complete code for a shunt class
+ *
+ * @return string function declaration and internal code
+ */
+ public function getDeclarationCode()
+ {
+ $sClassName = $this->getNameBeingShunted();
+ $sShuntClassName = $this->getName();
+
+ $aMethodCodes = array();
+
+ foreach ($this->getMethods() as $oMethod) {
+ $oMethod = new ShuntMethod($sClassName, $oMethod->getName());
+
+ if ($oMethod->isShuntable()) {
+ $aMethodCodes[] = $oMethod->getDeclarationCode();
+ }
+ }
+ $sMethods = implode('', $aMethodCodes);
+
+ $sClassDefinition = <<<EOT
+class {$sShuntClassName} extends {$sClassName}
+{
+ static public function _shuntGetStatic(\$sPropertyName)
+ {
+ return self::\${\$sPropertyName};
+ }
+
+ static public function _shuntSetStatic(\$sPropertyName, \$sPropertyValue=null)
+ {
+ self::\${\$sPropertyName} = \$sPropertyValue;
+ return self::_shuntGetStatic(\$sPropertyName);
+ }
+
+ public function _shuntGet(\$sPropertyName)
+ {
+ return \$this->\$sPropertyName;
+ }
+
+ public function _shuntSet(\$sPropertyName, \$sPropertyValue=null)
+ {
+ \$this->\$sPropertyName = \$sPropertyValue;
+ return \$this->_shuntGet(\$sPropertyName);
+ }
+
+ {$sMethods}
+}
+EOT;
+
+ return $sClassDefinition;
+ }
+
+ /**
+ * Return the shunt class name for the set class
+ */
+ public function getName()
+ {
+ $sClassName = $this->getNameBeingShunted();
+ $sShuntClassName = self::ClassPrefix . $sClassName . self::ClassSuffix;
+
+ return $sShuntClassName;
+ }
+
+ /**
+ * Return the shunt class name for the set class
+ */
+ public function getNameBeingShunted()
+ {
+ return parent::getName();
+ }
+}
+?>
30 ShuntException.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * Copyright 2009 iContact Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @package Shunt
+ * @author Josh Adell <jadell@icontact.com>
+ * @version $Id$
+ */
+
+/**
+ * Exception code definitions for shunt exceptions
+ */
+class ShuntException extends Exception
+{
+ const ClassNotFound = 1;
+ const AbstractMethodCall = 2;
+}
+?>
177 ShuntMethod.php
@@ -0,0 +1,177 @@
+<?php
+/**
+ * Copyright 2009 iContact Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @package Shunt
+ * @author Josh Adell <jadell@icontact.com>
+ * @version $Id$
+ */
+
+/**
+ * Generates code for creating a shunt of a specific method
+ */
+class ShuntMethod extends ReflectionMethod
+{
+ const MethodPrefix = '';
+ const MethodSuffix = 'Shunt';
+
+ /**
+ * Return complete code for a shunt method
+ *
+ * @return string function declaration and internal code
+ */
+ public function getDeclarationCode()
+ {
+ $sParamDeclarations = $this->getParameterDeclarations(true);
+ $sMethodSignature = $this->getMethodSignature();
+ $sMethodCode = $this->getMethodCode();
+
+ $sMethodDeclaration = "{$sMethodSignature}({$sParamDeclarations}){$sMethodCode}";
+ return $sMethodDeclaration;
+ }
+
+ /**
+ * Return shunt method code for calling protected methods
+ *
+ * @return string
+ */
+ public function getMethodCode()
+ {
+ $sMethodName = $this->getNameBeingShunted();
+
+ $sArgs = $this->getParameterDeclarations(true, true);
+
+ if ($this->isAbstract()) {
+ $sCode = "{ throw new ShuntException('Cannot call an abstract method.', ShuntException::AbstractMethodCall); }";
+ } else {
+ $sCode = "{ return parent::{$sMethodName}({$sArgs}); }";
+ }
+ return $sCode;
+ }
+
+ /**
+ * Return method signature for calling shunted method
+ * Methods declared 'protected' are returned as 'public'
+ *
+ * @return string
+ */
+ public function getMethodSignature()
+ {
+ $sClassName = $this->getDeclaringClass()->getName();
+ $sMethodName = $this->getNameBeingShunted();
+
+ if ($this->isAbstract()) {
+ $sShuntName = $sMethodName;
+ } else {
+ $sShuntName = $this->getName();
+ }
+
+ $aModifiers = array();
+ $aModifiers[] = 'public';
+ if ($this->isStatic()) {
+ $aModifiers[] = 'static';
+ }
+ $sModifiers = implode(' ', $aModifiers);
+
+ $sMethodSignature = sprintf("%s function %s", $sModifiers, $sShuntName);
+ return $sMethodSignature;
+ }
+
+ /**
+ * Return the shunt method name for the set method
+ *
+ * @return string name of method as a shunt method
+ */
+ public function getName()
+ {
+ $sMethodName = $this->getNameBeingShunted();
+ $sShuntMethodName = self::MethodPrefix . $sMethodName . self::MethodSuffix;
+
+ return $sShuntMethodName;
+ }
+
+ /**
+ * Return the shunt method name for the set method
+ *
+ * @return string name of method being shunted
+ */
+ public function getNameBeingShunted()
+ {
+ return parent::getName();
+ }
+
+ /**
+ * Returns an array of method parameter declaration strings
+ *
+ * @param boolean $bAsString required=false if true, return as a comma-separated string instead of an array
+ * @param boolean $bAsArgs required=false if true, return as method arguments instead of parameters
+ * @return mixed an array of strings from ShuntParameter::getDeclarationCode where name => declaration code, or a comma-separated string
+ */
+ public function getParameterDeclarations($bAsString=false, $bAsArgs=false)
+ {
+ $sClassName = $this->getDeclaringClass()->getName();
+ $sMethodName = $this->getName();
+ $aParamDeclarations = array();
+
+ foreach ($this->getParameters() as $i => $oParam) {
+ $sParamName = $oParam->getName();
+ $sCode = "\${$sParamName}";
+
+ if (!$bAsArgs) {
+ if ($oParam->isPassedByReference()) {
+ $sCode = "&{$sCode}";
+ }
+ if ($oParam->isOptional()) {
+ $sCode .= '=' . var_export($oParam->getDefaultValue(), true);
+ }
+ }
+
+ $aParamDeclarations[$sParamName] = $sCode;
+ }
+
+ if ($bAsString) {
+ $sParamDeclarations = implode(', ', $aParamDeclarations);
+ return $sParamDeclarations;
+ } else {
+ return $aParamDeclarations;
+ }
+ }
+
+ /**
+ * Is the current method declared in a parent class?
+ *
+ * @return bool true if this method was declared in a parent class
+ */
+ public function isInherited()
+ {
+ $sDeclaringClass = $this->getDeclaringClass()->getName();
+ $sClassName = $this->class;
+ return ($sDeclaringClass != $sClassName);
+ }
+
+ /**
+ * Should the method be shunted?
+ *
+ * @return bool true if this method should (can) be shunted
+ */
+ public function isShuntable()
+ {
+ return (
+ ($this->isProtected() && !$this->isInherited())
+ || ($this->isAbstract())
+ );
+ }
+}
+?>
170 tests/ShuntClassTest.php
@@ -0,0 +1,170 @@
+<?php
+/**
+ * Copyright 2009 iContact Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @package Shunt
+ * @subpackage Test
+ * @author Josh Adell <jadell@icontact.com>
+ * @version $Id$
+ */
+
+class ShuntClassTest extends PHPUnit_Framework_TestCase
+{
+ protected $sTestClass;
+ protected $sChildClass;
+
+ public function setup()
+ {
+ $this->sTestClass = uniqid('ShuntTester');
+ $this->sChildClass = uniqid('ShuntTesterChild');
+ }
+
+ // getDeclarationCode()
+ public function testGetDeclarationCode_BaseClass_ReturnsCorrectCode()
+ {
+ eval("
+ class {$this->sTestClass}
+ {
+ public function methodPublic(){}
+ protected function methodProtected(){}
+ private function methodPrivate(){}
+ }");
+
+ $oClassShunt = new ShuntClass($this->sTestClass);
+ $sClassCode = $oClassShunt->getDeclarationCode();
+
+ $sShuntClassName = $oClassShunt->getName();
+ $sBaseClassName = $oClassShunt->getNameBeingShunted();
+
+ $sExpectedDeclaration = "class {$sShuntClassName} extends {$sBaseClassName}";
+ self::assertContains($sExpectedDeclaration, $sClassCode, 'Code contains correct class declaration');
+
+ $aExpectedMethods = array(
+ '_shuntGet',
+ '_shuntSet',
+ '_shuntGetStatic',
+ '_shuntSetStatic',
+ 'methodProtectedShunt',
+ );
+ foreach ($aExpectedMethods as $sExpectedMethod) {
+ self::assertContains($sExpectedMethod, $sClassCode, 'Code contains expected method: ' . $sExpectedMethod);
+ }
+
+ $aUnexpectedMethods = array(
+ 'methodPublicShunt',
+ 'methodPrivateShunt',
+ );
+ foreach ($aUnexpectedMethods as $sUnexpectedMethod) {
+ self::assertNotContains($sUnexpectedMethod, $sClassCode, 'Code does not contain unexpected method: ' . $sUnexpectedMethod);
+ }
+ }
+
+ public function testGetDeclarationCode_ChildClass_ReturnsCorrectCodeWithoutInheritedMethods()
+ {
+ eval("
+ class {$this->sTestClass}
+ {
+ protected function methodInParent(){}
+ protected function methodOverwritten(){}
+ }");
+
+ eval("
+ class {$this->sChildClass} extends {$this->sTestClass}
+ {
+ protected function methodOverwritten(){}
+ protected function methodInChild(){}
+ }");
+
+
+ $oClassShunt = new ShuntClass($this->sChildClass);
+ $sClassCode = $oClassShunt->getDeclarationCode();
+
+ $sShuntClassName = $oClassShunt->getName();
+ $sBaseClassName = $oClassShunt->getNameBeingShunted();
+
+ $sExpectedDeclaration = "class {$sShuntClassName} extends {$sBaseClassName}";
+ self::assertContains($sExpectedDeclaration, $sClassCode, 'Code contains correct class declaration');
+
+ $aExpectedMethods = array(
+ '_shuntGet',
+ '_shuntSet',
+ '_shuntGetStatic',
+ '_shuntSetStatic',
+ // inherited but overwritten
+ 'methodOverwrittenShunt',
+ // child only method
+ 'methodInChildShunt',
+ );
+ foreach ($aExpectedMethods as $sExpectedMethod) {
+ self::assertContains($sExpectedMethod, $sClassCode, 'Code contains expected method: ' . $sExpectedMethod);
+ }
+
+ $aUnexpectedMethods = array(
+ // inherited and not overwritten
+ 'methodInParentShunt',
+
+ );
+ foreach ($aUnexpectedMethods as $sUnexpectedMethod) {
+ self::assertNotContains($sUnexpectedMethod, $sClassCode, 'Code does not contain unexpected method: ' . $sUnexpectedMethod);
+ }
+ }
+
+ public function testGetDeclarationCode_AbstractClass_ReturnsCorrectCode()
+ {
+ eval("
+ abstract class {$this->sTestClass}
+ {
+ abstract public function methodPublicAbstract();
+ abstract protected function methodProtectedAbstract();
+ }");
+
+ $oClassShunt = new ShuntClass($this->sTestClass);
+ $sClassCode = $oClassShunt->getDeclarationCode();
+
+ $aExpectedMethods = array(
+ '_shuntGet',
+ '_shuntSet',
+ '_shuntGetStatic',
+ '_shuntSetStatic',
+ 'methodPublicAbstract',
+ 'methodProtectedAbstract',
+ );
+ foreach ($aExpectedMethods as $sExpectedMethod) {
+ self::assertContains($sExpectedMethod, $sClassCode, 'Code contains expected method: ' . $sExpectedMethod);
+ }
+ }
+
+ // getName()
+ public function testGetName_ReturnsCorrectName()
+ {
+ eval("class {$this->sTestClass}{}");
+ $oClassShunt = new ShuntClass($this->sTestClass);
+
+ $sExpected = ShuntClass::ClassPrefix . $this->sTestClass . ShuntClass::ClassSuffix;
+ $sResult = $oClassShunt->getName();
+ self::assertEquals($sExpected, $sResult, 'Shunt class name matches expected.');
+ }
+
+ // getNameBeingShunted()
+ public function testGetNameBeingShunted_ReturnsCorrectName()
+ {
+ eval("class {$this->sTestClass}{}");
+ $oClassShunt = new ShuntClass($this->sTestClass);
+
+ $sResult = $oClassShunt->getNameBeingShunted();
+ self::assertEquals($this->sTestClass, $sResult, 'Class name matches expected.');
+ }
+}
+?>
320 tests/ShuntMethodTest.php
@@ -0,0 +1,320 @@
+<?php
+/**
+ * Copyright 2009 iContact Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @package Shunt
+ * @subpackage Test
+ * @author Josh Adell <jadell@icontact.com>
+ * @version $Id$
+ */
+
+class ShuntMethodTest extends PHPUnit_Framework_TestCase
+{
+ protected $sTestClass;
+ protected $sChildClass;
+
+ public function setup()
+ {
+ $this->sTestClass = uniqid('ShuntTester');
+ $this->sChildClass = uniqid('ShuntTesterChild');
+ }
+
+ // getDeclarationCode()
+ public function testGetDeclarationCode_Socket_ReturnsCorrectCode()
+ {
+ eval("class {$this->sTestClass}{ protected function someMethod(\$arg0=false){} }");
+ $oMethodShunt = new ShuntMethod($this->sTestClass, 'someMethod');
+
+ $sParams = $oMethodShunt->getParameterDeclarations(true);
+ $sSignature = $oMethodShunt->getMethodSignature();
+ $sCode = $oMethodShunt->getMethodCode();
+
+ $sExpected = "{$sSignature}({$sParams}){$sCode}";
+
+ $sResult = $oMethodShunt->getDeclarationCode();
+ self::assertEquals($sExpected, $sResult, 'Shunt method declaration code matches expected.');
+ }
+
+ // getMethodCode()
+ public function testGetMethodCode_ReturnsCorrectCode()
+ {
+ eval("class {$this->sTestClass}{ protected function someMethod(\$arg0=false){} }");
+
+ $sMethodName = 'someMethod';
+ $oMethodShunt = new ShuntMethod($this->sTestClass, $sMethodName);
+
+ $sExpected = "{ return parent::{$sMethodName}(\$arg0); }";
+
+ $sResult = $oMethodShunt->getMethodCode();
+ self::assertEquals($sExpected, $sResult, 'Shunt method code matches expected.');
+ }
+
+ // getMethodSignature()
+ public function testGetMethodSignature_PublicMethod_ReturnsCorrectSignature()
+ {
+ eval("class {$this->sTestClass}{ public function methodPublic(){} }");
+
+ $sMethodName = 'methodPublic';
+ $oMethodShunt = new ShuntMethod($this->sTestClass, $sMethodName);
+
+ $sExpected = 'public function methodPublicShunt';
+ $sResult = $oMethodShunt->getMethodSignature();
+ self::assertEquals($sExpected, $sResult, 'Shunt method signature matches expected.');
+ }
+
+ public function testGetMethodSignature_ProtectedMethod_ReturnsCorrectSignature()
+ {
+ eval("class {$this->sTestClass}{ protected function methodProtected(){} }");
+
+ $sMethodName = 'methodProtected';
+ $oMethodShunt = new ShuntMethod($this->sTestClass, $sMethodName);
+
+ $sExpected = 'public function methodProtectedShunt';
+ $sResult = $oMethodShunt->getMethodSignature();
+ self::assertEquals($sExpected, $sResult, 'Shunt method signature matches expected.');
+ }
+
+ public function testGetMethodSignature_AbstractMethod_ReturnsCorrectSignature()
+ {
+ eval("abstract class {$this->sTestClass}{ abstract protected function methodProtectedAbstract(); }");
+
+ $sMethodName = 'methodProtectedAbstract';
+ $oMethodShunt = new ShuntMethod($this->sTestClass, $sMethodName);
+
+ $sExpected = 'public function methodProtectedAbstract';
+ $sResult = $oMethodShunt->getMethodSignature();
+ self::assertEquals($sExpected, $sResult, 'Shunt method signature matches expected.');
+ }
+
+ // getName()
+ public function testGetName_ReturnsCorrectName()
+ {
+ eval("class {$this->sTestClass}{ public function methodPublic(){} }");
+
+ $sMethodName = 'methodPublic';
+ $oMethodShunt = new ShuntMethod($this->sTestClass, $sMethodName);
+
+ $sExpected = ShuntMethod::MethodPrefix . $sMethodName . ShuntMethod::MethodSuffix;
+ $sResult = $oMethodShunt->getName();
+ self::assertEquals($sExpected, $sResult, 'Shunt method name matches expected.');
+ }
+
+ // getNameBeingShunted()
+ public function testGetNameBeingShunted_ReturnsCorrectName()
+ {
+ eval("class {$this->sTestClass}{ public function methodPublic(){} }");
+
+ $sMethodName = 'methodPublic';
+ $oMethodShunt = new ShuntMethod($this->sTestClass, $sMethodName);
+
+ $sResult = $oMethodShunt->getNameBeingShunted();
+ self::assertEquals($sMethodName, $sResult, 'Method name matches expected.');
+ }
+
+ // getParameterDeclarations()
+ public function testGetParameterDeclarations_AsArray_ReturnsCorrectArrayOfParameters()
+ {
+ eval("class {$this->sTestClass}{ protected function someMethod(&\$arg0, \$arg1=1, \$arg2='something'){} }");
+
+ $sMethodName = 'someMethod';
+ $oMethodShunt = new ShuntMethod($this->sTestClass, 'someMethod');
+
+ $sExpected = array(
+ 'arg0' => "&\$arg0",
+ 'arg1' => "\$arg1=1",
+ 'arg2' => "\$arg2='something'",
+ );
+
+ $sResult = $oMethodShunt->getParameterDeclarations();
+ self::assertEquals($sExpected, $sResult, 'Parameter declarations match expected.');
+ }
+
+ public function testGetParameterDeclarations_AsArrayWithArrayParam_ReturnsCorrectArrayOfParameters()
+ {
+ eval("class {$this->sTestClass}{ protected function someMethod(\$arg0, \$arg1=array()){} }");
+
+ $sMethodName = 'someMethod';
+ $oMethodShunt = new ShuntMethod($this->sTestClass, $sMethodName);
+
+ $sExpected = array(
+ 'arg0' => "\$arg0",
+ 'arg1' => "\$arg1=array (\n)",
+ );
+
+ $sResult = $oMethodShunt->getParameterDeclarations();
+ self::assertEquals($sExpected, $sResult, 'Parameter declarations match expected.');
+ }
+
+ public function testGetParameterDeclarations_AsArrayWithBooleanParams_ReturnsCorrectArrayOfParameters()
+ {
+ eval("class {$this->sTestClass}{ protected function someMethod(\$arg0=true, \$arg1=false){} }");
+
+ $sMethodName = 'someMethod';
+ $oMethodShunt = new ShuntMethod($this->sTestClass, $sMethodName);
+
+ $sExpected = array(
+ 'arg0' => "\$arg0=true",
+ 'arg1' => "\$arg1=false",
+ );
+
+ $sResult = $oMethodShunt->getParameterDeclarations();
+ self::assertEquals($sExpected, $sResult, 'Parameter declarations match expected.');
+ }
+
+ public function testGetParameterDeclarations_AsArrayWithNullParam_ReturnsCorrectArrayOfParameters()
+ {
+ eval("class {$this->sTestClass}{ protected function someMethod(\$arg0=null){} }");
+
+ $sMethodName = 'someMethod';
+ $oMethodShunt = new ShuntMethod($this->sTestClass, $sMethodName);
+
+ $sExpected = array(
+ 'arg0' => "\$arg0=NULL",
+ );
+
+ $sResult = $oMethodShunt->getParameterDeclarations();
+ self::assertEquals($sExpected, $sResult, 'Parameter declarations match expected.');
+ }
+
+ public function testGetParameterDeclarations_AsArrayWithReferenceParam_ReturnsCorrectArrayOfParameters()
+ {
+ eval("class {$this->sTestClass}{ protected function someMethod(&\$arg0){} }");
+
+ $sMethodName = 'someMethod';
+ $oMethodShunt = new ShuntMethod($this->sTestClass, $sMethodName);
+
+ $sExpected = array(
+ 'arg0' => "&\$arg0",
+ );
+
+ $sResult = $oMethodShunt->getParameterDeclarations();
+ self::assertEquals($sExpected, $sResult, 'Parameter declarations match expected.');
+ }
+
+ public function testGetParameterDeclarations_AsString_ReturnsCorrectParameterString()
+ {
+ eval("class {$this->sTestClass}{ protected function someMethod(\$arg0, \$arg1=1){} }");
+
+ $sMethodName = 'someMethod';
+ $oMethodShunt = new ShuntMethod($this->sTestClass, $sMethodName);
+
+ $sExpected = '$arg0, $arg1=1';
+
+ $sResult = $oMethodShunt->getParameterDeclarations(true);
+ self::assertEquals($sExpected, $sResult, 'Parameter declarations match expected.');
+ }
+
+ public function testGetParameterDeclarations_AsMethodArgument_ReturnsCorrectArgumentString()
+ {
+ eval("class {$this->sTestClass}{ protected function someMethod(&\$arg0, \$arg1=1){} }");
+
+ $sMethodName = 'someMethod';
+ $oMethodShunt = new ShuntMethod($this->sTestClass, $sMethodName);
+
+ $sExpected = '$arg0, $arg1';
+
+ $sResult = $oMethodShunt->getParameterDeclarations(true, true);
+ self::assertEquals($sExpected, $sResult, 'Parameter declarations match expected.');
+ }
+
+ // isInherited()
+ public function testIsInherited_InheritedMethod_ReturnsTrue()
+ {
+ eval("class {$this->sTestClass}{ public function methodPublic(){} }");
+ eval("class {$this->sChildClass} extends {$this->sTestClass}{}");
+
+ $sMethodName = 'methodPublic';
+ $oMethodShunt = new ShuntMethod($this->sChildClass, $sMethodName);
+
+ $bResult = $oMethodShunt->isInherited();
+ self::assertTrue($bResult, 'Method is inherited.');
+ }
+
+ public function testIsInherited_OverwrittenInheritedMethod_ReturnsFalse()
+ {
+ eval("class {$this->sTestClass}{ public function methodPublic(){} }");
+ eval("class {$this->sChildClass} extends {$this->sTestClass}{ public function methodPublic(){} }");
+
+ $sMethodName = 'methodPublic';
+ $oMethodShunt = new ShuntMethod($this->sChildClass, $sMethodName);
+
+ $bResult = $oMethodShunt->isInherited();
+ self::assertFalse($bResult, 'Method is not inherited.');
+ }
+
+ public function testIsInherited_NonInheritedMethod_ReturnsFalse()
+ {
+ eval("class {$this->sTestClass}{ public function methodPublic(){} }");
+ eval("class {$this->sChildClass} extends {$this->sTestClass}{ public function methodNotInherited(){} }");
+
+ $sMethodName = 'methodNotInherited';
+ $oMethodShunt = new ShuntMethod($this->sChildClass, $sMethodName);
+
+ $bResult = $oMethodShunt->isInherited();
+ self::assertFalse($bResult, 'Method is not inherited.');
+ }
+
+ // isShuntable()
+ public function testIsShuntable_ProtectedMethod_ReturnsTrue()
+ {
+ eval("class {$this->sTestClass}{ protected function methodProtected(){} }");
+
+ $sMethodName = 'methodProtected';
+ $oMethodShunt = new ShuntMethod($this->sTestClass, $sMethodName);
+
+ $bResult = $oMethodShunt->isShuntable();
+ self::assertTrue($bResult, 'Method can be shunted.');
+ }
+
+ public function testIsShuntable_PublicMethod_ReturnsFalse()
+ {
+ eval("class {$this->sTestClass}{ public function methodPublic(){} }");
+
+ $sMethodName = 'methodPublic';
+ $oMethodShunt = new ShuntMethod($this->sTestClass, $sMethodName);
+
+ $bResult = $oMethodShunt->isShuntable();
+ self::assertFalse($bResult, 'Method can not be shunted.');
+ }
+
+ public function testIsShuntable_InheritedMethod_ReturnsFalse()
+ {
+ eval("class {$this->sTestClass}{ public function methodPublic(){} }");
+ eval("class {$this->sChildClass} extends {$this->sTestClass}{}");
+
+ $sMethodName = 'methodPublic';
+ $oMethodShunt = new ShuntMethod($this->sChildClass, $sMethodName);
+
+ $bResult = $oMethodShunt->isShuntable();
+ self::assertFalse($bResult, 'Method can not be shunted.');
+ }
+
+ public function testIsShuntable_AbstractMethods_ReturnsTrue()
+ {
+ eval("abstract class {$this->sTestClass}{ abstract public function methodPublic(); abstract protected function methodProtected(); }");
+
+ $sMethodName1 = 'methodPublic';
+ $sMethodName2 = 'methodProtected';
+
+ $oMethodShunt1 = new ShuntMethod($this->sTestClass, $sMethodName1);
+ $oMethodShunt2 = new ShuntMethod($this->sTestClass, $sMethodName2);
+
+ $bResult1 = $oMethodShunt1->isShuntable();
+ $bResult2 = $oMethodShunt2->isShuntable();
+ self::assertTrue($bResult1, 'Abstract public method can be shunted.');
+ self::assertTrue($bResult2, 'Abstract protected method can be shunted.');
+ }
+}
+?>
370 tests/ShuntTest.php
@@ -0,0 +1,370 @@
+<?php
+/**
+ * Copyright 2009 iContact Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @package Shunt
+ * @subpackage Test
+ * @author Josh Adell <jadell@icontact.com>
+ * @version $Id$
+ */
+
+class ShuntTest extends PHPUnit_Framework_TestCase
+{
+ protected $sTestClass;
+ protected $sChildClass;
+
+ public function setup()
+ {
+ $this->sTestClass = uniqid('ShuntTester');
+ $this->sChildClass = uniqid('ShuntTesterChild');
+ }
+
+ // generate()
+ public function testGenerate_ExistingClassShunted_ReturnsCorrectNameAndShuntClassExists()
+ {
+ eval("class {$this->sTestClass}{}");
+
+ $sResult = Shunt::generate($this->sTestClass);
+
+ $oReflection = new ShuntClass($this->sTestClass);
+ $sExpected = $oReflection->getName();
+
+ self::assertEquals($sExpected, $sResult, 'Shunt class name matches expected');
+ self::assertTrue(class_exists($sResult, false), 'Shunt class exists');
+ }
+
+ public function testGenerate_ExistingClassMultipleTimes_ReturnsSameNameEachTime()
+ {
+ eval("class {$this->sTestClass}{}");
+
+ $sExpected = Shunt::generate($this->sTestClass);
+
+ for ($i = 0; $i < 3; $i++) {
+ $sResult = Shunt::generate($this->sTestClass);
+ self::assertEquals($sExpected, $sResult, 'Shunt name matches on generate attempt ' . ($i+1));
+ }
+ }
+
+ public function testGenerate_NonExistingClassShunted_ThrowsException()
+ {
+ $sTestClass = 'ClassDoesNotExist';
+
+ self::setExpectedException('ShuntException');
+ Shunt::generate($sTestClass);
+ }
+
+ // get()
+ public function testGet_ExistingClassWithArgs_ReturnsShuntedClassWithSetProperties()
+ {
+ eval("
+ class {$this->sTestClass}
+ {
+ public \$iValue;
+
+ public function __construct(\$iValue)
+ {
+ \$this->iValue = \$iValue;
+ }
+ }
+ ");
+
+ $oReflection = new ShuntClass($this->sTestClass);
+ $sShuntClass = $oReflection->getName();
+
+ $iValue = 123;
+ $oTest = Shunt::get($this->sTestClass, array($iValue));
+
+ $iResultProperty = $oTest->_shuntGet('iValue');
+
+ self::assertType($sShuntClass, $oTest, 'Class has expected type');
+ self::assertEquals($iResultProperty, $iValue, 'Constructor properly set value');
+ }
+
+ public function testGet_ClassWIthNoConstructor_ReturnsShuntedClass()
+ {
+ eval("class {$this->sTestClass}{}");
+ $oTest = Shunt::get($this->sTestClass);
+ self::assertType($this->sTestClass, $oTest, 'Class has expected type');
+ }
+
+ public function testGet_AbstractClass_ReturnsShuntedClass()
+ {
+ eval("abstract class {$this->sTestClass}{ abstract protected function methodAbstract(); }");
+ $oTest = Shunt::get($this->sTestClass);
+ self::assertType($this->sTestClass, $oTest, 'Class has expected type');
+
+ self::setExpectedException('ShuntException');
+ $oTest->methodAbstract();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // The following tests work with the actual shunted classes
+ // to prove that the shunt class properly passes through to the
+ // class being shunted
+ ////////////////////////////////////////////////////////////////////////////////
+
+ public function testShunt_ShuntGetAndShuntSet_CanSetAndInspectProtectedValues()
+ {
+ eval("
+ class {$this->sTestClass}
+ {
+ protected \$iValue;
+
+ public function __construct(\$iValue)
+ {
+ \$this->iValue = \$iValue;
+ }
+ }
+ ");
+
+ $iOldValue = 123;
+ $iNewValue = 987;
+ $oTest = Shunt::get($this->sTestClass, array($iOldValue));
+
+ self::assertEquals($iOldValue, $oTest->_shuntGet('iValue'), 'Protected property is inspectable');
+
+ $oTest->_shuntSet('iValue', $iNewValue);
+ self::assertEquals($iNewValue, $oTest->_shuntGet('iValue'), 'Protected property is settable');
+ }
+
+ public function testShunt_ShuntGetStaticAndShuntSetStatic_CanSetAndInspectStaticProtectedValues()
+ {
+ eval("
+ class {$this->sTestClass}
+ {
+ protected static \$iValueStatic;
+ }
+ ");
+
+ $iOldStaticValue = 456;
+ $iNewStaticValue = 987;
+
+ $sTestClass = Shunt::generate($this->sTestClass);
+
+ call_user_func(array($sTestClass, '_shuntSetStatic'), 'iValueStatic', $iOldStaticValue);
+ $iResultValue = call_user_func(array($sTestClass, '_shuntGetStatic'), 'iValueStatic');
+ self::assertEquals($iResultValue, $iOldStaticValue, 'Static protected property is settable and inspectable');
+
+ call_user_func(array($sTestClass, '_shuntSetStatic'), 'iValueStatic', $iNewStaticValue);
+ $iResultValue = call_user_func(array($sTestClass, '_shuntGetStatic'), 'iValueStatic');
+ self::assertEquals($iResultValue, $iNewStaticValue, 'Static protected property can be reset');
+
+ }
+
+ public function testShunt_MethodWithDefault_CalledAndReturnsCorrectValuesArray()
+ {
+ eval("
+ class {$this->sTestClass}
+ {
+ protected function methodWithDefault(\$arg0, \$arg1=1)
+ {
+ return array(\$arg0, \$arg1);
+ }
+ }
+ ");
+
+ $oTest = Shunt::get($this->sTestClass);
+
+ $arg0 = 'test0';
+ $arg1 = 9;
+
+ $aExpected = array($arg0, $arg1);
+ $aResult = $oTest->methodWithDefaultShunt($arg0, $arg1);
+ self::assertEquals($aExpected, $aResult, 'Result array expected with both given');
+
+ $aExpected = array($arg0, 1);
+ $aResult = $oTest->methodWithDefaultShunt($arg0);
+ self::assertEquals($aExpected, $aResult, 'Result array expected with one given');
+ }
+
+ public function testShunt_MethodWithReference_CalledAndReturnsCorrectValuesArray()
+ {
+ eval("
+ class {$this->sTestClass}
+ {
+ protected function methodWithReference(&\$arg0)
+ {
+ \$arg0 = 'blarg!';
+ return array(\$arg0);
+ }
+ }
+ ");
+
+ $oTest = Shunt::get($this->sTestClass, array(123));
+
+ $arg0 = 'test0';
+ $sExpectedArg0 = 'blarg!';
+ $aExpected = array($sExpectedArg0);
+
+ $aResult = $oTest->methodWithReferenceShunt($arg0);
+ self::assertEquals($aExpected, $aResult, 'Result array expected');
+ self::assertEquals($aExpected[0], $arg0, 'Argument passed by reference modified as expected');
+ }
+
+ public function testShunt_MethodWithManyArgTypes_CalledAndReturnsCorrectValuesArray()
+ {
+ eval("
+ class {$this->sTestClass}
+ {
+ protected function methodWithManyArgTypes(\$arg0=true, \$arg1=false, \$arg2=null, \$arg3='somestring', \$arg4=array())
+ {
+ return array(\$arg0, \$arg1, \$arg2, \$arg3, \$arg4);
+ }
+ }
+ ");
+
+ $oTest = Shunt::get($this->sTestClass, array(123));
+
+ $arg0 = false;
+ $arg1 = true;
+ $arg2 = -123;
+ $arg3 = 'anotherstring';
+ $arg4 = array(9,8,7);
+
+ $aExpected = array($arg0, $arg1, $arg2, $arg3, $arg4);
+ $aResult = $oTest->methodWithManyArgTypesShunt($arg0, $arg1, $arg2, $arg3, $arg4);
+ self::assertEquals($aExpected, $aResult, 'Result array expected with all given');
+
+ $aExpected = array(true, false, null, 'somestring', array());
+ $aResult = $oTest->methodWithManyArgTypesShunt();
+ self::assertEquals($aExpected, $aResult, 'Result array expected with none given');
+ }
+
+ public function testShunt_MethodStatic_CalledAndReturnsCorrectValuesArray()
+ {
+ eval("
+ class {$this->sTestClass}
+ {
+ protected static function methodStatic(\$arg0)
+ {
+ return array(\$arg0);
+ }
+ }
+ ");
+
+ $oTest = Shunt::get($this->sTestClass, array(123));
+
+ $arg0 = 'test0';
+
+ $aExpected = array($arg0);
+ $aResult = call_user_func(get_class($oTest) .'::methodStaticShunt', $arg0);
+ self::assertEquals($aExpected, $aResult, 'Result array expected from static call');
+ }
+
+ public function testShunt_MethodPrivate_MethodStillPrivate()
+ {
+ eval("
+ class {$this->sTestClass}
+ {
+ private function methodPrivate(\$arg0)
+ {
+ return array(\$arg0);
+ }
+ }
+ ");
+
+ $oTest = Shunt::get($this->sTestClass, array(123));
+
+ $oMethod = new ReflectionMethod(get_class($oTest), 'methodPrivate');
+ self::setExpectedException('ReflectionException', "Trying to invoke private method {$this->sTestClass}::methodPrivate() from scope ReflectionMethod");
+ $oMethod->invoke($oTest);
+ }
+
+ public function testShunt_MethodPrivate_MethodUnshunted()
+ {
+ eval("
+ class {$this->sTestClass}
+ {
+ private function methodPrivate(\$arg0)
+ {
+ return array(\$arg0);
+ }
+ }
+ ");
+
+ $oTest = Shunt::get($this->sTestClass);
+
+ self::setExpectedException('ReflectionException', "Method {$this->sTestClass}Shunt::methodPrivateShunt() does not exist");
+ $oMethod = new ReflectionMethod(get_class($oTest), 'methodPrivateShunt');
+ }
+
+ public function testShunt_MethodPassToParent_CalledAndReturnsCorrectValuesArray()
+ {
+ eval("
+ class {$this->sTestClass}
+ {
+ protected function methodWithDefault(\$arg0, \$arg1=1)
+ {
+ return array(\$arg0, \$arg1);
+ }
+ }
+ ");
+
+ eval("
+ class {$this->sChildClass} extends {$this->sTestClass}
+ {
+ protected function methodPassToParent(\$arg0)
+ {
+ return parent::methodWithDefault(\$arg0);
+ }
+ }
+ ");
+
+ $oTest = Shunt::get($this->sChildClass);
+
+ $arg0 = 'test0';
+
+ $aExpected = array($arg0, 1);
+ $aResult = $oTest->methodPassToParentShunt($arg0);
+ self::assertEquals($aExpected, $aResult, 'Result array expected from child pass-thru to parent');
+ }
+
+ public function testShunt_MethodWithDefaultOverWritten_CalledAndReturnsCorrectValuesArray()
+ {
+ eval("
+ class {$this->sTestClass}
+ {
+ protected function methodWithDefault(\$arg0, \$arg1=1)
+ {
+ return array(\$arg0, \$arg1);
+ }
+ }
+ ");
+
+ eval("
+ class {$this->sChildClass} extends {$this->sTestClass}
+ {
+ protected function methodWithDefault(\$arg0, \$arg1=2)
+ {
+ return array(\$arg0, \$arg1);
+ }
+ }
+ ");
+
+ $oTest = Shunt::get($this->sChildClass);
+
+ $arg0 = 'test0';
+ $arg1 = 9;
+
+ $aExpected = array($arg0, $arg1);
+ $aResult = $oTest->methodWithDefaultShunt($arg0, $arg1);
+ self::assertEquals($aExpected, $aResult, 'Result array expected from overwritten method with both given');
+
+ $aExpected = array($arg0, 2);
+ $aResult = $oTest->methodWithDefaultShunt($arg0);
+ self::assertEquals($aExpected, $aResult, 'Result array expected from overwritten method with one given');
+ }
+}
+?>

0 comments on commit df942ed

Please sign in to comment.