src/mock/MethodPatcher.ps1
# Copyright 2019, Adam Edwards # # 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. $__MethodPatcher_Singleton = $null function __MethodPatcher_Get { if ( $script:__MethodPatcher_Singleton ) { return $script:__MethodPatcher_Singleton } $newPatcher = [PSCustomObject] @{ Methods = @{} StaticMethodTemplate = $null NonstaticMethodTemplate = $null } $script:__MethodPatcher_Singleton = $newPatcher $newPatcher $newPatcher.StaticMethodTemplate = @' {0} @args '@ $newPatcher.NonstaticMethodTemplate = @' set-strictmode -version 2 $__patchedMethod = __MethodPatcher_GetPatchedMethodByFunctionName (__MethodPatcher_Get) '{0}' if ( $__patchedMethod ) {{ $__objectMockScriptBlock = __PatchedClassMethod_GetMockedObjectScriptBlock $__patchedMethod $this if ( $__objectMockScriptBlock ) {{ # Invoke the object-specific mock $__result = . $__objectMockScriptBlock @args return $__result }} else {{ if ( ! $__patchedMethod.AllInstances ) {{ # Invoke the original unmocked method $__result = . $__patchedMethod.OriginalScriptBlock @args return $__result }} }} }} # Invoke the all-instance mock of this method {0} @args '@ } function __MethodPatcher_GetPatchedMethods($patcher) { $patcher.Methods.Values } function __MethodPatcher_QueryPatchedMethods($patcher, $className, $method, $staticMethods, $object) { $methodClass = $className $methodNames = if ( $method ) { @($method) } else { $classData = if ( $ClassName ) { __MethodPatcher_GetClassDefinition $patcher $className } else { __MethodPatcher_GetClassDefinition $patcher $object.scriptclass.classname } $methodClass = $classData.prototype.pstypename if ( $staticMethods ) { $classData.prototype.scriptclass.psobject.methods | select -expandproperty name } else { $classData.instancemethods.keys } } if ( $object ) { $methodClass = $object.scriptclass.classname } $patchedClassMethods = $methodNames | foreach { __MethodPatcher_GetMockableMethodFunction $patcher $methodClass $_ $staticMethods ($object -eq $null) } $patchedClassMethods } function __MethodPatcher_GetMockableMethodFunction( $patcher, $className, $methodName, $isStatic, $allInstances ) { $functionName = __PatchedClassMethod_GetMockableMethodName $className $methodName $isStatic $existingPatchMethod = __MethodPatcher_GetPatchedMethodByFunctionName $patcher $functionName if ( $existingPatchMethod ) { $existingPatchMethod } else { $classDefinition = __MethodPatcher_GetClassDefinition $className $originalMethodBlock = __MethodPatcher_GetClassMethod $classDefinition $methodName $isStatic $replacementMethodBlock = __MethodPatcher_CreateMethodPatchScriptBlock $patcher $functionName $isStatic new-item "function:script:$($functionName)" -value $originalMethodBlock -force | out-null $patchedClassMethod = __PatchedClassMethod_New $classDefinition $methodName $isStatic $allInstances $originalMethodBlock $replacementMethodBlock $patcher.Methods[$patchedClassMethod.FunctionName] = $patchedClassMethod $patchedClassMethod } } function __MethodPatcher_GetClassDefinition($className) { $classDefinition = $__classTable[$className] if ( ! $classDefinition ) { throw "The specified class '$className' was not found" } $classDefinition } function __MethodPatcher_GetClassMethod($classDefinition, $methodName, $isStatic) { $methodBlock = if ( $isStatic ) { $classDefinition.prototype.scriptclass.psobject.methods[$methodName].script } else { $classDefinition.instancemethods[$methodName] } if ( ! $methodBlock ) { throw "Method '$methodName', static='$isStatic', was not found for class '$($classDefinition.prototype.pstypename)'" } $methodBlock } function __MethodPatcher_CreateMethodPatchScriptBlock($patcher, $functionName, $isStatic ) { if ( $isStatic ) { [ScriptBlock]::Create($patcher.StaticMethodTemplate -f $functionName) } else { [ScriptBlock]::Create($patcher.NonstaticMethodTemplate -f $functionName) } } function __MethodPatcher_PatchMethod( $patcher, $className, $methodName, $isStatic, $object ) { $mockableMethod = __MethodPatcher_GetMockableMethodFunction $patcher $className $methodName $isStatic ($object -eq $null) __PatchedClassMethod_Patch $mockableMethod $object $mockableMethod } function __MethodPatcher_GetPatchedMethodByFunctionName($patcher, $functionName) { $patcher.Methods[$functionName] } function __MethodPatcher_Unpatch($patcher, $patchedMethod, $object) { __PatchedClassMethod_Unpatch $patchedMethod $object if ( ! ( __PatchedClassMethod_IsActive $patchedMethod ) ) { gi "function:$($_.functionname)" | rm $patcher.Methods.Remove($patchedMethod.functionname) } } |