How to test whether a function is dispatched correctly?

e.g.

foo(x::Int) = fpp(x)
foo(x::Float64) = fqq(x)

@test iscalled(foo, 1.0, fqq)

have you considered code_lowered?

fpp(x)=x
fqq(x)=2*x
foo(x::Int) = fpp(x)
foo(x::Float64) = fqq(x)

foo(1)
foo(1.0)

@code_lowered foo(1)
@code_lowered foo(1.0)

Thanks, I need to write it into a test file. Is it possible?

I guess so.
But there are probably more elegant answers to your question. Let us hope someone else chimes in.

Then again, I am not sure if I understand your problem; i.e. why you want to test this.

I guess you could search the result of code_lowered for the string/symbol that you are looking for.

res=@code_lowered foo(1.0)
dump(res)

How about functionloc(foo, (Float64,)) == @functionloc foo(1.0)? Is it necessary to make fqq get involved?

Isn’t @which is what you’re looking for?

julia> fpp(x)=x
fpp (generic function with 1 method)

julia> fqq(x)=2*x
fqq (generic function with 1 method)

julia> foo(x::Int) = fpp(x)
foo (generic function with 1 method)

julia> foo(x::Float64) = fqq(x)
foo (generic function with 2 methods)

julia> @which foo(1)
foo(x::Int64) in Main at REPL[3]:1

julia> @which foo(1.0)
foo(x::Float64) in Main at REPL[4]:1

But I don’t know how to use it in tests.

1 Like

using case:

I have a fallback of slow implementation, I want to make sure it is dispatched to the faster one in test.

@code_lowered, @which and functionloc only dive into the first level. Is it possible to show the list of function stack?

Well, to test you need to figure out how to identify the result once you figured out which one to test. In this case, I believe what you should test is the signature of the method, if you have something more specific you want to test, you’ll also likely need to calculate that based on the signature.

After you decided on that, which(...).sig gives you the signature and you can do whatever test you want on that type.

1 Like

I guess this can also be done using explicitly trait-based dispatch like Array-traits unless you want to keep the signature of foo concise.

Well, this is a requirement you’ll have to define precisely before it could be tested. You see, in general, there can be arbitrary code inside a function and they can all dispatch to different methods. Also, in general, the dispatch is not fixed until runtime so it’s impossible to determine that without running the function. It’s also important to define what do you mean “dispatch to the faster one”. Does it have to call that method? Or does it have to NOT call anything else? Or both?

You can avoid the problem of having to run the function by requiring static dispatch. Or you can use the debugger (interpreter) and trace every single function call and make your decision based on that. (I don’t know how it’s done but it should be possible.)

And if you want to rely on static dispatch information, you’ll need to use the code_typed family. You can walk the code recursively from there though you’ll also need to make the decision if you want to rely on any optimizations that requires inlining. If yes, then you’ll have to look at the line info if your function is inlined, which might still not be super reliable for small functions, or you can disable inlining but that can cause dynamic dispatch to show up that wouldn’t otherwise in the real code.

Thanks,
The correct statement is
it have to call that method
And the function will be runned in the test.

yes, something like @enter in Debugger which searches AST upto a certain depth, to see whether a function is called. But should be able to be included in a test (useful in test driven development).

If a runtime call of that method is good enough for you then interpreter should work. The code in the interpreter should have all what you need to do that in a test. I’ve never used it so I don’t know if it’s set up to do it but I’ll be surprised if it isn’t… Someone that have used the debugger should help you on that instead…

1 Like

Sorry if I am missing some detail, but I am wondering if this this something that is reasonable to test for.

In Julia, one usually tests for

  1. getting the correct results (like all other languages),

  2. the code being compiler-friendly (eg Test.@inferred, this is specific to Julia).

The chain of calls that achieves this is an implementation detail and may not survive a refactoring, so hardcoding this in unit tests is a cost one undertakes for little benefit.

1 Like

Maybe I should use btime in the test to make sure it is faster. This is more fundamental. Thanks

That’s exactly what I’m talking about defining the test subject. It’s on the OP to come up with a stable enough target to test for. This is related to the complexity of the project and involves figuring out what changes are likely.

The threshold for such a test is certainly higher, so it should be done only for certain important calls (testing a single dispatch with which is easy and doesn’t have to be too limited). This is a performance test so not surviving refactoring isn’t necessarily an issue (it doesn’t signal breaking) as long as the test can be fixed accordingly (and ideally the test is unlikely turning into no-op after refactoring)

2 Likes