diff --git a/base/pkg.jl b/base/pkg.jl index 80334f7dda710..491abe1d3f18d 100644 --- a/base/pkg.jl +++ b/base/pkg.jl @@ -2,7 +2,7 @@ module Pkg export Git, Dir, GitHub, Types, Reqs, Cache, Read, Query, Resolve, Write, Generate, Entry export dir, init, rm, add, available, installed, status, clone, checkout, - release, fix, update, resolve, register, tag, publish, generate + release, fix, update, resolve, register, tag, publish, generate, test const DEFAULT_META = "git://github.com/JuliaLang/METADATA.jl" const META_BRANCH = "metadata-v2" @@ -62,6 +62,10 @@ build(pkgs::String...) = cd(Entry.build,[pkgs...]) generate(pkg::String, license::String; force::Bool=false) = cd(Generate.package,pkg,license,force=force) + +test() = cd(Entry.test) +test(pkgs::String...) = cd(Entry.test,String[pkgs...]) + @deprecate release free @deprecate fixup build @deprecate fix pin diff --git a/base/pkg/entry.jl b/base/pkg/entry.jl index 67da7c1d84e93..3c930462a6ae8 100644 --- a/base/pkg/entry.jl +++ b/base/pkg/entry.jl @@ -663,4 +663,50 @@ function updatehook(pkgs::Vector) """) end +@windows_only const JULIA = joinpath(JULIA_HOME, ENV["JULIA_EXE"]) +@unix_only const JULIA = joinpath(JULIA_HOME, "julia-readline") + +function test!(pkg::String, errs::Vector{String}, notests::Vector{String}) + const reqs_path = abspath(pkg,"test","REQUIRE") + if isfile(reqs_path) + const tests_require = Reqs.parse(reqs_path) + if (!isempty(tests_require)) + info("Computing test dependencies for $pkg...") + resolve(tests_require) + end + end + const test_path = abspath(pkg,"test","runtests.jl") + if isfile(test_path) + info("Testing $pkg") + cd(dirname(test_path)) do + try + run(`$JULIA $test_path`) + info("$pkg tests passed") + catch err + warnbanner(err, label="[ ERROR: $pkg ]") + push!(errs,pkg) + end + end + else + push!(notests,pkg) + end + resolve() +end + +function test(pkgs::Vector{String}) + errs = String[] + notests = String[] + for pkg in pkgs + test!(pkg,errs,notests) + end + if !isempty(errs) || !isempty(notests) + messages = String[] + isempty(errs) || push!(messages, "$(join(errs,", "," and ")) had test errors") + isempty(notests) || push!(messages, "$(join(notests,", "," and ")) did not provide a test/runtests.jl file") + error(join(messages, "and")) + end +end + +test() = test(sort!(String[keys(installed())...])) + end # module diff --git a/doc/stdlib/pkg.rst b/doc/stdlib/pkg.rst index 47463cce32565..0a56858559f14 100644 --- a/doc/stdlib/pkg.rst +++ b/doc/stdlib/pkg.rst @@ -131,3 +131,11 @@ to use them, you'll need to prefix each function call with an explicit ``Pkg.``, For each new package version tagged in ``METADATA`` not already published, make sure that the tagged package commits have been pushed to the repo at the registered URL for the package and if they all have, push ``METADATA``. +.. function:: test() + + Run the tests for all installed packages ensuring that each package's test dependencies are installed for the duration of the test. A package is tested by running its ``test/runtests.jl`` file and test dependencies are specified in ``test/REQUIRE``. + +.. function:: test(pkgs...) + + Run the tests for each package in ``pkgs`` ensuring that each package's test dependencies are installed for the duration of the test. A package is tested by running its ``test/runtests.jl`` file and test dependencies are specified in ``test/REQUIRE``. + diff --git a/test/pkg.jl b/test/pkg.jl index 428502675a5e1..4b8316dee0d6d 100644 --- a/test/pkg.jl +++ b/test/pkg.jl @@ -1,15 +1,79 @@ -ENV["JULIA_PKGDIR"] = string("tmp.",randstring()) -@test !isdir(Pkg.dir()) -try # ensure directory removal - Pkg.init() - @test isdir(Pkg.dir()) - Pkg.resolve() - - @test isempty(Pkg.installed()) - Pkg.add("Example") - @test [keys(Pkg.installed())...] == ["Example"] - Pkg.rm("Example") - @test isempty(Pkg.installed()) -finally - run(`rm -rf $(Pkg.dir())`) +function temp_pkg_dir(fn::Function) + # Used in tests below to setup and teardown a sandboxed package directory + const tmpdir = ENV["JULIA_PKGDIR"] = abspath(string("tmp.",randstring())) + @test !isdir(Pkg.dir()) + try + Pkg.init() + @test isdir(Pkg.dir()) + Pkg.resolve() + + fn() + finally + run(`rm -rf $tmpdir`) + end +end + +# Test adding a removing a package +temp_pkg_dir() do + @test isempty(Pkg.installed()) + Pkg.add("Example") + @test [keys(Pkg.installed())...] == ["Example"] + Pkg.rm("Example") + @test isempty(Pkg.installed()) +end + +# testing a package with test dependencies causes them to be installed for the duration of the test +temp_pkg_dir() do + Pkg.generate("PackageWithTestDependencies", "MIT") + @test [keys(Pkg.installed())...] == ["PackageWithTestDependencies"] + + isdir(Pkg.dir("PackageWithTestDependencies","test")) || mkdir(Pkg.dir("PackageWithTestDependencies","test")) + open(Pkg.dir("PackageWithTestDependencies","test","REQUIRE"),"w") do f + println(f,"Example") + end + + open(Pkg.dir("PackageWithTestDependencies","test","runtests.jl"),"w") do f + println(f,"using Base.Test") + println(f,"@test haskey(Pkg.installed(), \"Example\")") + end + + Pkg.resolve() + @test [keys(Pkg.installed())...] == ["PackageWithTestDependencies"] + + Pkg.test("PackageWithTestDependencies") + + @test [keys(Pkg.installed())...] == ["PackageWithTestDependencies"] end + +# testing a package with no runtests.jl errors +temp_pkg_dir() do + Pkg.generate("PackageWithNoTests", "MIT") + + if isfile(Pkg.dir("PackageWithNoTests", "test", "runtests.jl")) + rm(Pkg.dir("PackageWithNoTests", "test", "runtests.jl")) + end + + try + Pkg.test("PackageWithNoTests") + catch err + @test err.msg == "PackageWithNoTests did not provide a test/runtests.jl file" + end +end + +# testing a package with failing tests errors +temp_pkg_dir() do + Pkg.generate("PackageWithFailingTests", "MIT") + + isdir(Pkg.dir("PackageWithFailingTests","test")) || mkdir(Pkg.dir("PackageWithFailingTests","test")) + open(Pkg.dir("PackageWithFailingTests", "test", "runtests.jl"),"w") do f + println(f,"using Base.Test") + println(f,"@test false") + end + + try + Pkg.test("PackageWithFailingTests") + catch err + @test err.msg == "PackageWithFailingTests had test errors" + end +end +