Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for nested calls to apply #90

Merged
merged 9 commits into from
Oct 4, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ keywords = ["testing", "mocking"]
license = "MIT"
desc = "Allows Julia function calls to be temporarily overloaded for purpose of testing"
author = ["Curtis Vogt"]
version = "0.7.2"
version = "0.8.0"
tpgillam marked this conversation as resolved.
Show resolved Hide resolved

[deps]
ExprTools = "e2ba6199-217a-4e67-a87a-7c52f15ade04"
Expand Down
78 changes: 77 additions & 1 deletion src/patch.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,46 @@ end

PatchEnv(debug::Bool=false) = PatchEnv(Dict{Any,Vector{Function}}(), debug)

function patches(pe::PatchEnv)
#! format: off
return [
Patch(target, func)
for (target, alternate_funcs) in pe.mapping
for func in alternate_funcs
tpgillam marked this conversation as resolved.
Show resolved Hide resolved
]
#! format: on
end

tpgillam marked this conversation as resolved.
Show resolved Hide resolved
function Base.:(==)(pe1::PatchEnv, pe2::PatchEnv)
return pe1.mapping == pe2.mapping && pe1.debug == pe2.debug
end

"""
merge(pe1::PatchEnv, pe2::PatchEnv)
tpgillam marked this conversation as resolved.
Show resolved Hide resolved

Merge the two `PatchEnv` instances.

This is done in such a way that the following always holds:

```
patches_1 = Patch[...]
patches_2 = Patch[...]
patches = vcat(patches_1, patches_2)

pe1 = PatchEnv(patches_1)
pe2 = PatchEnv(patches_2)
pe = PatchEnv(patches)

@assert pe == merge(pe1, pe2)
```

The `debug` flag will be set to true if either `pe1` or `pe2` have it set to true.
"""
function Base.merge(pe1::PatchEnv, pe2::PatchEnv)
new_patches = vcat(patches(pe1), patches(pe2))
oxinabox marked this conversation as resolved.
Show resolved Hide resolved
return PatchEnv(new_patches, pe1.debug || pe2.debug)
tpgillam marked this conversation as resolved.
Show resolved Hide resolved
end

function apply!(pe::PatchEnv, p::Patch)
alternate_funcs = get!(Vector{Function}, pe.mapping, p.target)
push!(alternate_funcs, p.alternate)
Expand All @@ -55,11 +95,47 @@ function apply!(pe::PatchEnv, patches)
for p in patches
apply!(pe, p)
end
return pe
end

"""
apply(body::Function, patches; debug::Bool=false)
apply(body::Function, pe::PatchEnv)

Convenience function to run `body` in the context of the given `patches`.

This is intended to be used with do-block notation, e.g.:

```
patch = @patch ...
apply(patch) do
...
end
```

## Nesting
tpgillam marked this conversation as resolved.
Show resolved Hide resolved

Note that calls to apply will nest the patches that are applied. So the following two
examples are equivalent:

```
patch_2 = @patch ...
apply([patch, patch_2]) do
...
end
```

```
apply(patch) do
apply(patch_2) do
...
end
end
```
"""
function apply(body::Function, pe::PatchEnv)
prev_pe = get_active_env()
set_active_env(pe)
set_active_env(merge(prev_pe, pe))
oxinabox marked this conversation as resolved.
Show resolved Hide resolved
try
return body()
finally
Expand Down
28 changes: 28 additions & 0 deletions test/merge.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@testset "merge PatchEnv instances" begin
multiply(x::Number) = 2x
multiply(x::Int) = 2x - 1
add(x::Number) = x + 2
add(x::Int) = x + 1

patches = Patch[
@patch multiply(x::Integer) = 3x
@patch multiply(x::Int) = 4x
@patch add(x::Int) = x + 4
]

@testset "simple" begin
pe1 = Mocking.PatchEnv(patches[1])
pe2 = Mocking.PatchEnv(patches[2:3])
pe = Mocking.PatchEnv(patches)

@test pe == merge(pe1, pe2)
end

@testset "debug flag" begin
pe1 = Mocking.PatchEnv(patches[1], true)
pe2 = Mocking.PatchEnv(patches[2:3])
pe = Mocking.PatchEnv(patches, true)

@test pe == merge(pe1, pe2)
end
end
78 changes: 78 additions & 0 deletions test/nested_apply.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Nesting calls to apply should take the appropriate union of patches.
@testset "nested apply calls" begin
multiply(x::Number) = 2x
multiply(x::Int) = 2x - 1
add(x::Number) = x + 2
add(x::Int) = x + 1

@testset "simple" begin
patches = Patch[
@patch multiply(x::Integer) = 3x
@patch multiply(x::Int) = 4x
@patch add(x::Int) = x + 4
]

apply(patches) do
@test (@mock multiply(2)) == 8
@test (@mock multiply(0x2)) == 0x6
@test (@mock multiply(2//1)) == 4//1
@test (@mock add(2//1)) == 4//1
@test (@mock add(2)) == 6
end

apply(patches[1]) do
@test (@mock multiply(2)) == 6
@test (@mock multiply(0x2)) == 0x6
@test (@mock multiply(2//1)) == 4//1
@test (@mock add(2//1)) == 4//1
@test (@mock add(2)) == 3

apply(patches[2]) do
@test (@mock multiply(2)) == 8
@test (@mock multiply(0x2)) == 0x6
@test (@mock multiply(2//1)) == 4//1
@test (@mock add(2//1)) == 4//1
@test (@mock add(2)) == 3

apply(patches[3]) do
@test (@mock multiply(2)) == 8
@test (@mock multiply(0x2)) == 0x6
@test (@mock multiply(2//1)) == 4//1
@test (@mock add(2//1)) == 4//1
@test (@mock add(2)) == 6
end

@test (@mock multiply(2)) == 8
@test (@mock multiply(0x2)) == 0x6
@test (@mock multiply(2//1)) == 4//1
@test (@mock add(2//1)) == 4//1
@test (@mock add(2)) == 3
end

@test (@mock multiply(2)) == 6
@test (@mock multiply(0x2)) == 0x6
@test (@mock multiply(2//1)) == 4//1
@test (@mock add(2//1)) == 4//1
@test (@mock add(2)) == 3
end
end

@testset "repeated patch" begin
patches = Patch[
@patch multiply(x::Integer) = 3x
@patch multiply(x::Integer) = 4x
]

apply(patches) do
@test (@mock multiply(2)) == 8
end

apply(patches[1]) do
@test (@mock multiply(2)) == 6
apply(patches[2]) do
@test (@mock multiply(2)) == 8
end
@test (@mock multiply(2)) == 6
end
end
end
2 changes: 2 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@ using Mocking: anon_morespecific, anonymous_signature, dispatch, type_morespecif
include("anonymous-param.jl")
include("reuse.jl")
include("args.jl")
include("merge.jl")
include("nested_apply.jl")
end