Question on mocking of arbitrary function for testing

This is my first experiments with this sort of tooling, so it can be suboptimal, but instead of Cassette.jl one can use IRTools.jl.

Considering the same function myprogram

function myprogram()
	alpha = 0.0
	println("sin($(alpha)) is $(sin(alpha))")
	println("cos($(alpha)) is $(cos(alpha))")
end

One can either work with IR directly

using IRTools: IR, xcall, isexpr

@dynamo function sin2cos(a...)
    ir = IR(a...)
    ir === nothing && return
    for (x, stmt) in ir
        isexpr(stmt.expr, :call) || continue
        fn = stmt.expr.args[1]
        if fn isa GlobalRef && fn.name == :sin
            ir[x].expr.args[1] = GlobalRef(Base, :cos)
        else
            ir[x] = xcall(sin2cos, stmt.expr.args...)
        end
    end
    return ir
end

julia> sin2cos() do
           myprogram()
       end
sin(0.0) is 1.0
cos(0.0) is 1.0

or even better, use multiple dispatch

using IRTools: IR, recurse!

mock(::typeof(sin), x) = cos(x)
@dynamo function mock(a...)
    ir = IR(a...)
    ir === nothing && return
    recurse!(ir)
    return ir
end

julia> mock() do
           myprogram()
       end
sin(0.0) is 1.0
cos(0.0) is 1.0

and of course it can substitute any number of function calls simultaneously

mock(::typeof(sin), x) = cos(x)
mock(::typeof(cos), x) = sin(x)

julia> mock() do
           myprogram()
       end
sin(0.0) is 1.0
cos(0.0) is 0.0