Add some basic tests

This commit is contained in:
Chris de Graaf 2019-08-31 17:33:33 +07:00
parent 810ac5ab28
commit 5adde3ad7c
No known key found for this signature in database
GPG Key ID: 150FFDD9B0073C7B
19 changed files with 202 additions and 53 deletions

8
.gitignore vendored
View File

@ -1,6 +1,6 @@
.DS_Store
*.jl.cov
*.jl.*.cov
*.jl.mem
/docs/build/ /docs/build/
/docs/site/ /docs/site/
.DS_Store
*.jl.*.cov
*.jl.cov
*.jl.mem

View File

@ -11,6 +11,9 @@ julia:
- 1.2 - 1.2
- 1.3 - 1.3
- nightly - nightly
before_script:
- git config --global user.name Tester
- git config --global user.email te@st.er
matrix: matrix:
fast_finish: true fast_finish: true
allow_failures: allow_failures:

View File

@ -106,12 +106,6 @@ version = "0.2.11"
deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] deps = ["Distributed", "InteractiveUtils", "Logging", "Random"]
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
[[URIParser]]
deps = ["Test", "Unicode"]
git-tree-sha1 = "6ddf8244220dfda2f17539fa8c9de20d6c575b69"
uuid = "30578b45-9adc-5946-b283-645ec420af67"
version = "0.4.0"
[[UUIDs]] [[UUIDs]]
deps = ["Random", "SHA"] deps = ["Random", "SHA"]
uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

View File

@ -1,7 +1,7 @@
name = "PkgTemplates" name = "PkgTemplates"
uuid = "14b8a8f1-9102-5b29-a752-f990bacb7fe1" uuid = "14b8a8f1-9102-5b29-a752-f990bacb7fe1"
authors = ["Chris de Graaf <chrisadegraaf@gmail.com>"] authors = ["Chris de Graaf <chrisadegraaf@gmail.com>"]
version = "0.6.2" version = "0.7.0"
[deps] [deps]
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
@ -11,14 +11,14 @@ Mustache = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70"
Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a" Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
URIParser = "30578b45-9adc-5946-b283-645ec420af67"
[compat] [compat]
julia = "1" julia = "1"
[extras] [extras]
Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
ReferenceTests = "324d217c-45ce-50fc-942e-d289b448e8cf"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
[targets] [targets]
test = ["Suppressor", "Test"] test = ["Random", "ReferenceTests", "Test"]

View File

@ -11,7 +11,6 @@ using REPL.TerminalMenus: MultiSelectMenu, RadioMenu, request
using Mustache: entityMap, render using Mustache: entityMap, render
using Parameters: @with_kw using Parameters: @with_kw
using URIParser: URI
export export
Template, Template,
@ -36,5 +35,6 @@ abstract type Plugin end
include("template.jl") include("template.jl")
include("generate.jl") include("generate.jl")
include("plugin.jl") include("plugin.jl")
# include("interactive.jl")
end end

View File

@ -6,7 +6,6 @@ Generate a package named `pkg` from a [`Template`](@ref).
function (t::Template)(pkg::AbstractString) function (t::Template)(pkg::AbstractString)
endswith(pkg, ".jl") && (pkg = pkg[1:end-3]) endswith(pkg, ".jl") && (pkg = pkg[1:end-3])
pkg_dir = joinpath(t.dir, pkg) pkg_dir = joinpath(t.dir, pkg)
ispath(pkg_dir) && throw(ArgumentError("$pkg_dir already exists"))
try try
# Create the directory with some boilerplate inside. # Create the directory with some boilerplate inside.

View File

@ -1,14 +1,13 @@
# TODO: Update the allowed failures as new versions come out.
const VersionsOrStrings = Vector{Union{VersionNumber, String}} const VersionsOrStrings = Vector{Union{VersionNumber, String}}
const ALLOWED_FAILURES = ["1.3", "nightly"] const ALLOWED_FAILURES = ["1.3", "nightly"] # TODO: Update this list with new RCs.
const DEFAULT_CI_VERSIONS = VersionsOrStrings([VERSION, "1.0", "nightly"]) const DEFAULT_CI_VERSIONS = VersionsOrStrings([VERSION, default_version(), "nightly"])
format_version(v::VersionNumber) = "$(v.major).$(v.minor)" format_version(v::VersionNumber) = "$(v.major).$(v.minor)"
format_version(v::AbstractString) = string(v) format_version(v::AbstractString) = string(v)
function collect_versions(t::Template, versions::Vector) function collect_versions(t::Template, versions::Vector)
vs = [format_version(t.julia_version); map(format_version, versions)] vs = map(format_version, [t.julia_version, versions...])
return unique!(sort!(vs)) return unique(sort(vs))
end end
@with_kw struct TravisCI <: BasicPlugin @with_kw struct TravisCI <: BasicPlugin
@ -43,8 +42,8 @@ function view(p::TravisCI, t::Template, pkg::AbstractString)
x86 = Dict{String, String}[] x86 = Dict{String, String}[]
if p.x86 if p.x86
foreach(versions) do v foreach(versions) do v
p.linux && push!(x86, Dict("JULIA" => v, "OS" => "linux", "ARCH" => "x86")) p.linux && push!(x86, Dict("JULIA" => v, "OS" => "linux"))
p.windows && push!(x86, Dict("JULIA" => v, "OS" => "windows", "ARCH" => "x86")) p.windows && push!(x86, Dict("JULIA" => v, "OS" => "windows"))
end end
end end
@ -128,10 +127,10 @@ function view(p::CirrusCI, t::Template, ::AbstractString)
end end
@with_kw struct GitLabCI <: BasicPlugin @with_kw struct GitLabCI <: BasicPlugin
file::String file::String = default_file("gitlab-ci.yml")
documentation::Bool = true documentation::Bool = true
coverage::Bool = true coverage::Bool = true
extra_versions::Vector{VersionNumber} = [v"1.0"] extra_versions::VersionsOrStrings = ["1.0"] # Nightly has no Docker image.
end end
gitignore(p::GitLabCI) = p.coverage ? COVERAGE_GITIGNORE : String[] gitignore(p::GitLabCI) = p.coverage ? COVERAGE_GITIGNORE : String[]

View File

@ -93,7 +93,7 @@ function render_plugin(p::Gitignore, t::Template)
end end
function gen_plugin(p::Gitignore, t::Template, pkg_dir::AbstractString) function gen_plugin(p::Gitignore, t::Template, pkg_dir::AbstractString)
gen_file(joinpath(pkg_dir, ".gitignore"), render_plugin(p, t)) t.git && gen_file(joinpath(pkg_dir, ".gitignore"), render_plugin(p, t))
end end
@with_kw struct Tests <: BasicPlugin @with_kw struct Tests <: BasicPlugin
@ -121,7 +121,7 @@ function gen_plugin(p::Tests, t::Template, pkg_dir::AbstractString)
proj = current_project() proj = current_project()
try try
Pkg.activate(pkg_dir) Pkg.activate(pkg_dir)
Pkg.update() # Clean up both Manifest.toml and Project.toml. Pkg.update()
finally finally
proj === nothing ? Pkg.activate() : Pkg.activate(proj) proj === nothing ? Pkg.activate() : Pkg.activate(proj)
end end

View File

@ -1,13 +1,12 @@
const DEFAULT_USER = LibGit2.getconfig("github.user", "") default_plugins() = [Gitignore(), License(), Readme(), Tests()]
const DEFAULT_VERSION = VersionNumber(VERSION.major) default_user() = LibGit2.getconfig("github.user", "")
const DEFAULT_AUTHORS = let default_version() = VersionNumber(VERSION.major)
function default_authors()
name = LibGit2.getconfig("user.name", "") name = LibGit2.getconfig("user.name", "")
isempty(name) && return ""
email = LibGit2.getconfig("user.email", "") email = LibGit2.getconfig("user.email", "")
if isempty(name) return isempty(email) ? name : "$name <$email>"
""
else
isempty(email) ? name : "$name <$email>"
end
end end
""" """
@ -18,17 +17,17 @@ Records common information used to generate a package.
## Keyword Arguments ## Keyword Arguments
### User Options ### User Options
- `user::AbstractString="$DEFAULT_USER"`: GitHub (or other code hosting service) username. - `user::AbstractString="$(default_user())"`: GitHub (or other code hosting service) username.
The default value comes from the global Git config (`github.user`). The default value comes from the global Git config (`github.user`).
If no value is obtained, an `ArgumentError` is thrown. If no value is obtained, an `ArgumentError` is thrown.
- `authors::Union{AbstractString, Vector{<:AbstractString}}="$DEFAULT_AUTHORS"`: Package authors. - `authors::Union{AbstractString, Vector{<:AbstractString}}="$(default_authors())"`: Package authors.
Supply a string for one author or an array for multiple. Supply a string for one author or an array for multiple.
Like `user`, it takes its default value from the global Git config (`user.name` and `user.email`). Like `user`, it takes its default value from the global Git config (`user.name` and `user.email`).
### Package Options ### Package Options
- `host::AbstractString="github.com"`: URL to the code hosting service where packages will reside. - `host::AbstractString="github.com"`: URL to the code hosting service where packages will reside.
- `dir::AbstractString="$(contractuser(Pkg.devdir()))"`: Directory to place packages in. - `dir::AbstractString="$(contractuser(Pkg.devdir()))"`: Directory to place packages in.
- `julia_version::VersionNumber=$(repr(DEFAULT_VERSION))`: Minimum allowed Julia version. - `julia_version::VersionNumber=$(repr(default_version()))`: Minimum allowed Julia version.
- `develop::Bool=true`: Whether or not to `develop` new packages in the active environment. - `develop::Bool=true`: Whether or not to `develop` new packages in the active environment.
### Git Options ### Git Options
@ -39,13 +38,12 @@ Records common information used to generate a package.
### Template Plugins ### Template Plugins
- `plugins::Vector{<:Plugin}=Plugin[]`: A list of [`Plugin`](@ref)s used by the template. - `plugins::Vector{<:Plugin}=Plugin[]`: A list of [`Plugin`](@ref)s used by the template.
- `disabled_defaults::Vector{DataType}=DataType[]`: Default plugins to disable. - `disable_defaults::Vector{DataType}=DataType[]`: Default plugins to disable.
The default plugins are [`Readme`](@ref), [`License`](@ref), [`Tests`](@ref), and [`Gitignore`](@ref). The default plugins are [`Readme`](@ref), [`License`](@ref), [`Tests`](@ref), and [`Gitignore`](@ref).
To override a default plugin instead of disabling it altogether, supply it via `plugins`. To override a default plugin instead of disabling it altogether, supply it via `plugins`.
### Interactive Usage ### Interactive Usage
- `interactive::Bool=false`: When set, the template is created interactively, filling unset keywords with user input. - `interactive::Bool=false`: When set, the template is created interactively, filling unset keywords with user input.
- `fast::Bool=false`: Skips prompts for any unsupplied keywords except `user` and `plugins`, accepting default values.
""" """
struct Template struct Template
authors::Vector{String} authors::Vector{String}
@ -67,21 +65,19 @@ function Template(::Val{false}; kwargs...)
user = getkw(kwargs, :user) user = getkw(kwargs, :user)
isempty(user) && throw(ArgumentError("No user set, please pass user=username")) isempty(user) && throw(ArgumentError("No user set, please pass user=username"))
host = getkw(kwargs, :host)
host = URI(occursin("://", host) ? host : "https://$host").host
authors = getkw(kwargs, :authors) authors = getkw(kwargs, :authors)
authors isa Vector || (authors = map(strip, split(authors, ","))) authors isa Vector || (authors = map(strip, split(authors, ",")))
host = replace(getkw(kwargs, :host), r".*://" => "")
dir = abspath(expanduser(getkw(kwargs, :dir))) dir = abspath(expanduser(getkw(kwargs, :dir)))
disabled = getkw(kwargs, :disabled_defaults) disabled = getkw(kwargs, :disable_defaults)
defaults = [Readme, License, Tests, Gitignore] enabled = filter(p -> !(typeof(p) in disabled), default_plugins())
plugins = map(T -> T(), filter(T -> !(T in disabled), defaults)) append!(enabled, getkw(kwargs, :plugins))
append!(plugins, getkw(kwargs, :plugins))
# This comprehension resolves duplicate plugin types by overwriting, # This comprehension resolves duplicate plugin types by overwriting,
# which means that default plugins get replaced by user values. # which means that default plugins get replaced by user values.
plugin_dict = Dict(typeof(p) => p for p in plugins) plugins = Dict(typeof(p) => p for p in enabled)
return Template( return Template(
authors, authors,
@ -91,7 +87,7 @@ function Template(::Val{false}; kwargs...)
host, host,
getkw(kwargs, :julia_version), getkw(kwargs, :julia_version),
getkw(kwargs, :manifest), getkw(kwargs, :manifest),
plugin_dict, plugins,
getkw(kwargs, :ssh), getkw(kwargs, :ssh),
user, user,
) )
@ -106,14 +102,14 @@ getkw(kwargs, k) = get(() -> defaultkw(k), kwargs, k)
# Default Template keyword values. # Default Template keyword values.
defaultkw(s::Symbol) = defaultkw(Val(s)) defaultkw(s::Symbol) = defaultkw(Val(s))
defaultkw(::Val{:authors}) = DEFAULT_AUTHORS defaultkw(::Val{:authors}) = default_authors()
defaultkw(::Val{:develop}) = true defaultkw(::Val{:develop}) = true
defaultkw(::Val{:dir}) = Pkg.devdir() defaultkw(::Val{:dir}) = Pkg.devdir()
defaultkw(::Val{:disabled_defaults}) = DataType[] defaultkw(::Val{:disable_defaults}) = DataType[]
defaultkw(::Val{:git}) = true defaultkw(::Val{:git}) = true
defaultkw(::Val{:host}) = "github.com" defaultkw(::Val{:host}) = "github.com"
defaultkw(::Val{:julia_version}) = DEFAULT_VERSION defaultkw(::Val{:julia_version}) = default_version()
defaultkw(::Val{:manifest}) = false defaultkw(::Val{:manifest}) = false
defaultkw(::Val{:plugins}) = Plugin[] defaultkw(::Val{:plugins}) = Plugin[]
defaultkw(::Val{:ssh}) = false defaultkw(::Val{:ssh}) = false
defaultkw(::Val{:user}) = DEFAULT_USER defaultkw(::Val{:user}) = default_user()

3
test/fixtures/Basic/.gitignore.txt vendored Normal file
View File

@ -0,0 +1,3 @@
.DS_Store
/Manifest.toml
/dev/

19
test/fixtures/Basic/LICENSE.txt vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2019 tester
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

2
test/fixtures/Basic/Manifest.toml.txt vendored Normal file
View File

@ -0,0 +1,2 @@
# This file is machine-generated - editing it directly is not advised

13
test/fixtures/Basic/Project.toml.txt vendored Normal file
View File

@ -0,0 +1,13 @@
name = "Basic"
uuid = "5b7e9947-ddc0-4b3f-9b55-0d8042f74170"
authors = ["tester"]
version = "0.1.0"
[compat]
julia = "1"
[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
[targets]
test = ["Test"]

1
test/fixtures/Basic/README.md.txt vendored Normal file
View File

@ -0,0 +1 @@
# Basic

5
test/fixtures/Basic/src/Basic.jl.txt vendored Normal file
View File

@ -0,0 +1,5 @@
module Basic
greet() = print("Hello World!")
end # module

View File

@ -0,0 +1,6 @@
using Basic
using Test
@testset "Basic.jl" begin
# Write your tests here.
end

25
test/generate.jl Normal file
View File

@ -0,0 +1,25 @@
default_files(pkg::AbstractString) = [
".gitignore",
"LICENSE",
"Manifest.toml",
"Project.toml",
"README.md",
"src/$pkg.jl",
"test/runtests.jl",
]
function reference_test(pkg_dir::AbstractString, path::AbstractString)
pkg = basename(pkg_dir)
path = replace(path, "/" => path_separator)
# All fixture files are .txt because otherwise ReferenceTests/FileIO can't handle them.
reference = joinpath(@__DIR__, "fixtures", pkg, path * ".txt")
observed = read(joinpath(pkg_dir, path), String)
@test_reference reference observed
end
@testset "Default package" begin
pkg = "Basic"
t = tpl(; develop=false, authors=USER)
t(pkg)
foreach(f -> reference_test(joinpath(t.dir, pkg), f), default_files(pkg))
end

View File

@ -0,0 +1,32 @@
using Base.Filesystem: path_separator
using Pkg: Pkg
using Random: Random
using Test: @test, @testset, @test_throws
using ReferenceTests: @test_reference
using PkgTemplates
const PT = PkgTemplates
const PKG = "TestPkg"
const USER = "tester"
Random.seed!(1)
tpl(; kwargs...) = Template(; user=USER, kwargs...)
@testset "PkgTemplates.jl" begin
mktempdir() do dir
Pkg.activate(dir)
pushfirst!(DEPOT_PATH, dir)
try
include("template.jl")
include("generate.jl")
# include("plugin.jl")
# include("interactive.jl")
finally
popfirst!(DEPOT_PATH)
end
end
end

52
test/template.jl Normal file
View File

@ -0,0 +1,52 @@
@testset "Template constructor" begin
@testset "user" begin
if isempty(PT.default_user())
@test_throws ArgumentError Template()
haskey(ENV, "CI") && run(`git config --global github.user $USER`)
end
@test Template().user == PT.default_user()
end
@testset "authors" begin
@test tpl(; authors=["a"]).authors == ["a"]
@test tpl(; authors="a").authors == ["a"]
@test tpl(; authors="a,b").authors == ["a", "b"]
@test tpl(; authors="a, b").authors == ["a", "b"]
end
@testset "host" begin
@test tpl(; host="https://foo.com").host == "foo.com"
end
@testset "dir" begin
@test tpl(; dir="/foo/bar").dir == "/foo/bar"
@test tpl(; dir="foo").dir == abspath("foo")
@test tpl(; dir="~/foo").dir == abspath(expanduser("~/foo"))
end
@testset "plugins / disabled_defaults" begin
function test_plugins(plugins, expected, disabled=DataType[])
t = tpl(; plugins=plugins, disable_defaults=disabled)
@test issetequal(values(t.plugins), expected)
end
defaults = PT.default_plugins()
test_plugins([], defaults)
test_plugins([Citation()], union(defaults, [Citation()]))
# Overriding a default plugin.
gi = Gitignore(; dev=false)
test_plugins([gi], union(setdiff(defaults, [Gitignore()]), [gi]))
# Disabling a default plugin.
test_plugins([], setdiff(defaults, [Gitignore()]), [Gitignore])
end
end
@testset "hasplugin" begin
t = tpl(; plugins=[Documenter{TravisCI}()])
@test PT.hasplugin(t, typeof(first(PT.default_plugins())))
@test PT.hasplugin(t, Documenter)
@test PT.hasplugin(t, _ -> true)
@test !PT.hasplugin(t, _ -> false)
@test !PT.hasplugin(t, Citation)
@test !PT.hasplugin(t, PT.is_ci)
end