Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace minitest.cr in favor of std-lib spec #334

Merged
merged 27 commits into from
Mar 23, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 172 additions & 0 deletions spec/integration/spec_helper.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
ENV["PATH"] = "#{File.expand_path("../../bin", __DIR__)}:#{ENV["PATH"]}"
ENV["SHARDS_CACHE_PATH"] = ".shards"

require "spec"
require "../../src/config"
require "../../src/lock"
require "../../src/spec"
require "../support/factories"
require "../support/cli"

module Shards::Specs
@@created_repositories = false

def self.created_repositories?
@@created_repositories
end

def self.created_repositories!
@@created_repositories = true
end
end

Spec.before_each do
bcardiff marked this conversation as resolved.
Show resolved Hide resolved
unless Shards::Specs.created_repositories?
run "rm -rf #{tmp_path}/*"
setup_repositories
end
end

private def setup_repositories
# git dependencies for testing version resolution:
create_git_repository "web", "1.0.0", "1.1.0", "1.1.1", "1.1.2", "1.2.0", "2.0.0", "2.1.0"
create_git_repository "pg", "0.1.0", "0.2.0", "0.2.1", "0.3.0"
create_git_repository "optional", "0.2.0", "0.2.1", "0.2.2"
create_git_repository "shoulda", "0.1.0"
create_git_repository "minitest", "0.1.0", "0.1.1", "0.1.2", "0.1.3"

create_git_repository "mock"
create_git_release "mock", "0.1.0", "name: mock\nversion: 0.1.0\n" +
"dependencies:\n shoulda:\n git: #{git_path("shoulda")}\n version: < 0.3.0\n" +
"development_dependencies:\n minitest:\n git: #{git_path("minitest")}\n"

create_git_repository "orm", "0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.3.2", "0.4.0"
create_git_release "orm", "0.5.0", "name: orm\nversion: 0.5.0\ndependencies:\n pg:\n git: #{git_path("pg")}\n version: < 0.3.0\n"

create_git_repository "release", "0.2.0", "0.2.1", "0.2.2"
create_git_release "release", "0.3.0", "name: release\nversion: 0.3.0\ncustom_dependencies:\n pg:\n git: #{git_path("optional")}\n"

# git dependencies with prereleases:
create_git_repository "unstable", "0.1.0", "0.2.0", "0.3.0.alpha", "0.3.0.beta"
create_git_repository "preview", "0.1.0", "0.2.0", "0.3.0.a", "0.3.0.b", "0.3.0", "0.4.0.a"

# path dependency:
create_path_repository "foo", "0.1.0"

# dependency with neither a shard.yml and/or version tags:
# create_git_repository "empty"
# create_git_commit "empty", "initial release"

create_git_repository "missing"
create_shard "missing", "name: missing\nversion: 0.1.0\n"
create_git_commit "missing", "initial release"

# dependencies with postinstall scripts:
create_git_repository "post"
create_file "post", "Makefile", "all:\n\ttouch made.txt\n"
create_git_release "post", "0.1.0", "name: post\nversion: 0.1.0\nscripts:\n postinstall: make\n"

create_git_repository "fails"
create_file "fails", "Makefile", "all:\n\ttest -n ''\n"
create_git_release "fails", "0.1.0", "name: fails\nversion: 0.1.0\nscripts:\n postinstall: make\n"

# transitive dependencies in postinstall scripts:
create_git_repository "version"
create_file "version", "src/version.cr", %(module Version; STRING = "version @ 0.1.0"; end)
create_git_release "version", "0.1.0"

create_git_repository "transitive"
create_file "transitive", "src/version.cr", %(require "version"; puts Version::STRING)
create_git_release "transitive", "0.2.0", <<-YAML
name: transitive
version: 0.2.0
dependencies:
version:
git: #{git_path(:version)}
scripts:
postinstall: crystal build src/version.cr
YAML

# dependencies with executables:
create_git_repository "binary"
create_file "binary", "bin/foobar", "#! /usr/bin/env sh\necho 'OK'", perm: 0o755
create_file "binary", "bin/baz", "#! /usr/bin/env sh\necho 'KO'", perm: 0o755
create_git_release "binary", "0.1.0", "name: binary\nversion: 0.1.0\nexecutables:\n - foobar\n - baz\n"
create_file "binary", "bin/foo", "echo 'FOO'", perm: 0o755
create_git_release "binary", "0.2.0", "name: binary\nversion: 0.2.0\nexecutables:\n - foobar\n - baz\n - foo"

Shards::Specs.created_repositories!
end

private def assert(value, message, file, line)
straight-shoota marked this conversation as resolved.
Show resolved Hide resolved
fail(message, file, line) unless value
end

private def refute(value, message, file, line)
fail(message, file, line) if value
end

def assert_installed(name, version = nil, file = __FILE__, line = __LINE__)
assert Dir.exists?(install_path(name)), "expected #{name} dependency to have been installed", file, line

if version
assert File.exists?(install_path(name, "shard.yml")), "expected shard.yml for installed #{name} dependency was not found", file, line
spec = Shards::Spec.from_file(install_path(name, "shard.yml"))

if spec.version == "0" && File.exists?(install_path("#{name}.sha1"))
File.read(install_path("#{name}.sha1")).should eq(version), file, line
else
spec.version.should eq(version), file, line
end
end
end

def refute_installed(name, version = nil, file = __FILE__, line = __LINE__)
if version
if Dir.exists?(install_path(name))
assert File.exists?(install_path(name, "shard.yml")), "expected shard.yml for installed #{name} dependency was not found", file, line
spec = Shards::Spec.from_file(install_path(name, "shard.yml"))
spec.version.should_not eq(version), file, line
end
else
refute Dir.exists?(install_path(name)), "expected #{name} dependency to not have been installed", file, line
end
end

def assert_installed_file(path, file = __FILE__, line = __LINE__)
assert File.exists?(File.join(install_path(name), path)), "Expected #{path} to have been installed", file, line
end

def assert_locked(name, version = nil, file = __FILE__, line = __LINE__)
path = File.join(application_path, "shard.lock")
assert File.exists?(path), "expected shard.lock to have been generated", file, line
locks = Shards::Lock.from_file(path)
assert lock = locks.find { |d| d.name == name }, "expected #{name} dependency to have been locked", file, line

if lock && version
if version =~ Shards::VERSION_REFERENCE
assert version == lock.version, "expected #{name} dependency to have been locked at version #{version}", file, line
else
assert version == lock.refs, "expected #{name} dependency to have been locked at commit #{version}", file, line
end
end
end

def refute_locked(name, version = nil, file = __FILE__, line = __LINE__)
path = File.join(application_path, "shard.lock")
assert File.exists?(path), "expected shard.lock to have been generated", file, line
locks = Shards::Lock.from_file(path)
refute locks.find { |d| d.name == name }, "expected #{name} dependency to not have been locked", file, line
end

def install_path(project, *path_names)
File.join(application_path, "lib", project, *path_names)
end

def debug(command)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this used for?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it's a helper method to debug failure on commands. It was there I didn't want to disrupt anybody's workflow.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Useful when an integration test starts failing: add a debug run instead of trying to reproduce the test case or printing the output manually.

run "#{command} --verbose"
rescue ex : FailedCommand
puts
puts ex.stdout
puts ex.stderr
end
107 changes: 107 additions & 0 deletions spec/support/cli.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
Spec.before_each do
application_path.tap do |path|
bcardiff marked this conversation as resolved.
Show resolved Hide resolved
if File.exists?(path)
run("rm -rf #{path}/*", capture: false)
run("rm -rf #{path}/.shards", capture: false)
else
Dir.mkdir_p(path)
end
end
end

def with_shard(metadata, lock = nil)
Dir.cd(application_path) do
File.write "shard.yml", to_shard_yaml(metadata)
File.write "shard.lock", to_lock_yaml(lock) if lock
yield
end
end

def to_shard_yaml(metadata)
String.build do |yml|
yml << "name: " << (metadata[:name]? || "test").inspect << '\n'
yml << "version: " << (metadata[:version]? || "0.0.0").inspect << '\n'

metadata.each do |key, value|
if key.to_s.ends_with?("dependencies")
yml << key << ':'

if value.responds_to?(:each)
yml << '\n'
value.each do |name, version|
yml << " " << name << ":\n"

case version
when String
yml << " git: " << git_url(name).inspect << '\n'
yml << " version: " << version.inspect << '\n'
# when Hash
# version.each do |k, v|
# yml << " " << k << ": " << v.inspect << '\n'
# end
when NamedTuple
version.each do |k, v|
yml << " " << k.to_s << ": " << v.inspect << '\n'
end
else
yml << " git: " << git_url(name).inspect << '\n'
end
end
else
yml << value
end
elsif key.to_s == "targets"
yml << "targets:\n"
if value.responds_to?(:each)
value.each do |target, info|
yml << " " << target.to_s << ":\n"
if info.responds_to?(:each)
info.each do |main, src|
yml << " main: " << src.inspect << '\n'
end
end
end
end
end
end
end
end

def to_lock_yaml(lock)
return unless lock

String.build do |yml|
yml << "version: 1.0\n"
yml << "shards:\n"

lock.each do |name, version|
yml << " " << name << ":\n"
yml << " git: " << git_url(name).inspect << '\n'

if version =~ /^[\d\.]+$/
yml << " version: " << version.inspect << '\n'
else
yml << " commit: " << version.inspect << '\n'
end
end
end
end

module Shards::Specs
@@application_path : String?

def self.application_path
@@application_path ||= File.expand_path("../../tmp/integration", __DIR__).tap do |path|
if File.exists?(path)
run("rm -rf #{path}/*", capture: false)
run("rm -rf #{path}/.shards", capture: false)
else
Dir.mkdir_p(path)
end
end
end
end

def application_path
Shards::Specs.application_path
end
4 changes: 4 additions & 0 deletions spec/support/factories.cr
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,7 @@ def run(command, capture = false)
raise FailedCommand.new("command failed: #{command}", output.to_s, error.to_s)
end
end

def run!(command)
run(command, capture: true).not_nil!
end
73 changes: 73 additions & 0 deletions spec/support/mock_resolver.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
require "../../src/versions"

module Shards
class MockResolver < Resolver
def self.key
"mock"
end

def read_spec(version = nil)
specs = @@specs[dependency.name].not_nil!
unless version
version = Versions.sort(specs.keys).first?
end
specs[version.to_s]
end

def available_versions
if specs = @@specs[dependency.name]?
specs.keys
else
[] of String
end
end

def spec?(version)
raise "not implemented error"
end

@@specs = {} of String => Hash(String, String)

def self.register_spec(name, version = "0.0.0", dependencies = nil, development = nil)
spec = "name: #{name.inspect}\n"
spec += "version: #{version.inspect}\n"

if dependencies
spec += "dependencies:\n#{to_yaml(dependencies)}\n"
end

if development
spec += "development_dependencies:\n#{to_yaml(development)}\n"
end

specs = @@specs[name] ||= {} of String => String
specs[version.to_s] = spec

nil
end

def self.clear_specs
@@specs.clear
end

def installed_commit_hash
raise ArgumentError.new("abstract")
end

private def self.to_yaml(dependencies)
yaml = dependencies.map do |dep|
ary = dep.split(':', 2)

if ary.size == 2
" #{ary[0]}:\n mock: \"test\"\n version: #{ary[1].inspect}"
else
" #{ary[0]}:\n mock: \"test\""
end
end

yaml.join('\n')
end
end

register_resolver MockResolver
end