Skip to content

Test whether a call is dispatched to a method in your module

License

Notifications You must be signed in to change notification settings

jlapeyre/MethodInSrc.jl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MethodInSrc

Build Status Build Status Codecov Coveralls

This package requires a Julia development branch f8f7088 from July 24, 2018, or later. (Because it uses splitpath)

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.)

Motivation

Some code provides efficient, specialized methods for particular data 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, do you test your new implementation ? 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.

Macros @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.

Macros @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.

Examples

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

Package using MethodInSrc

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.

Other examples

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

About

Test whether a call is dispatched to a method in your module

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Languages