MethodInSrc
provides tools to use in your test suite that verify that
a function call dispatches (or does not) to a method defined in your module, or in another specified module.
They are meant to verify that a specialized method is called rather than a more generic one. (Or vice versa.)
Some code provides efficient, specialized methods for particular data types, rather than relying on fallack methods defined for more abstract types. It's important that the input, the output, and the function call look exactly as they did before implementing the efficient method. So the test shouldn't change:
@test sum(A) = n
How then, apart from benchmarking, can you test that your new implementation is indeed called? This module is a step towards a solution.
The problem becomes more complicated, and more error prone, if you have a file defining binary operations, say multiplication, for combinations of various particular and abstract types. Are you sure dispatch is occurring as you intend?
Furthermore, due to changes in other code, someone may remove a specialized method that has become redundant, or add a new one. When reading this code (even if you changed it yourself last year) and you can't find a method, did it go missing, or was removed intentionally ?
"... wait a minute, foofunc
isn't in the source, but there's a test for it in the test suite--- and it's passing!"
If you had written @test @insrc(foofunc(A, b)) == c
, then the test would have failed as soon as foofunc
was
removed from the source.
@isinsrc
, @insrc
, and @ninsrc
@isinsrc f(x)
returns true
if the method for f(x)
is found under ../src
. But, it does not evaluate f(x)
.
@insrc f(x)
throws an error if the method for f(x)
is not found under ../src
. Otherwise, it evaluates f(x)
.
@ninsrc
is the same as @insrc
except that it throws if the method is under ../src
.
These three macros are meant to be used in files in your module's "test" directory.
In this case, @isinsrc f([x,...])
returns true
if the method that would be called by f([x,...])
was defined
in the module's "src" directory.
@isinmodule
, @inmodule
, and @ninmodule
@isinmodule ModuleName f(x)
returns true
if the method for f(x)
is found in the source directory
of ModuleName
. But, it does not evaluate f(x)
.
@inmodule ModuleName f(x)
throws an error if the method for f(x)
is not found under the source directory of ModuleName
.
Otherwise, it evaluates f(x)
.
@ninmodule
is the same as @inmodule
except that it throws if the method is under the source directory of ModuleName
.
Suppose the type MyPackage.AMatrix
represents a square matrix whose elements are all equal to 1
.
MyPackage
extends Base.sum
with an efficient method for ::AMatrix
.
MyPackage
also includes an efficient function prod(::AMatrix)
.
But, we neglected to write Base.prod
or import Base: prod
.
So MyPackage.prod
is not an extension of Base.prod
.
@isinsrc
Use @isinsrc
to
check that both sum
and prod
extend Base
functions. (prod
does not!)
using MyPackage
using MethodInSrc
using Test
m = MyPackage.AMatrix{Int}(3)
@test @isinsrc sum(m)
@test @isinsrc prod(m) # This will fail!
@insrc
, @ninsrc
Use @insrc
if you are too lazy to write two tests,
one to verify that you have the correct method,
and another to test its correctness.
The following example assumes we know that MyPackage.prod
and Base.prod
are different functions.
These tests all pass.
using MyPackage
using MethodInSrc
using Test
N = 3
m = MyPackage.AMatrix{Int}(N)
# Note that `@insrc` takes the next expression as an argument.
# So, `@insrc prod(m) == 1` will fail to locate the source for `prod`
@test_throws ErrorException 1 == @insrc prod(m) # This finds the source for the method.
@test_throws ErrorException @insrc(prod(m)) == 1 # This does too.
@test 1 == @ninsrc prod(m) # Do the test if the method *is* generic
# The following methods are found in "../src", so the expressions are evaluated
@test N^2 == @insrc sum(m)
@test (@insrc sum(m)) == N^2
@test @insrc(MyPackage.prod(m)) == 1
MethodInSrc
Following is obsolete
IdentityMatrix.jl
uses MethodInSrc
in runtests.jl.
IdentityMatrix
(a misnomer) includes methods for types from Base
, LinearAlgebra
and FillArrays
. It serves as a way station for
some efficient methods. Methods have been moved from IdentityMatrix
to LinearAlgebra
and FillArrays
.
These are methods for one
, sum
, inv
, etc.
Keeping track of where I want the methods to be, and ensuring that they are indeed there, was as
good as impossible without something like MethodInSrc
.
The test suite for MethodInSrc has more detailed examples based on a toy type implemented in ./src/testmethods.jl and ./src/subdir/method_in_subdir.jl