mirror of
https://github.com/ethauvin/sdkman-for-fish.git
synced 2025-04-25 05:17:11 -07:00
Migrate completion tests to Cucumber.
Also retire Fish 2.x + macOS test; installation of custom brew didn't work out anymore.
This commit is contained in:
parent
ed6039e533
commit
0c15f199cd
12 changed files with 364 additions and 236 deletions
33
test/Dockerfile
Normal file
33
test/Dockerfile
Normal file
|
@ -0,0 +1,33 @@
|
|||
FROM ruby:2.5.8-slim-buster
|
||||
|
||||
# Install dependencies
|
||||
RUN apt-get update \
|
||||
&& apt-get -y install \
|
||||
fish \
|
||||
curl \
|
||||
unzip \
|
||||
zip \
|
||||
&& apt-get clean
|
||||
|
||||
WORKDIR app
|
||||
COPY test/Gemfile ./
|
||||
RUN bundle install \
|
||||
&& rm Gemfile
|
||||
|
||||
# Switch to non-root user for test context
|
||||
ARG TEST_HOME="/home/test"
|
||||
RUN groupadd -r test \
|
||||
&& useradd --no-log-init -r -g test -m -d $TEST_HOME test
|
||||
USER test
|
||||
RUN curl -s "https://get.sdkman.io" | bash
|
||||
|
||||
# "Install" sdkman-for-fish
|
||||
RUN mkdir -p $TEST_HOME/.config/fish/
|
||||
COPY completions $TEST_HOME/.config/fish/completions/
|
||||
COPY conf.d $TEST_HOME/.config/fish/conf.d/
|
||||
COPY completions $TEST_HOME/.config/fish/functions/
|
||||
RUN ls -R $TEST_HOME/.config/fish/
|
||||
|
||||
# Run tests
|
||||
COPY test ./
|
||||
CMD cucumber
|
6
test/Gemfile
Normal file
6
test/Gemfile
Normal file
|
@ -0,0 +1,6 @@
|
|||
source "https://rubygems.org"
|
||||
|
||||
group :test do
|
||||
gem 'cucumber', '~> 3.1.0'
|
||||
gem 'rspec', '~> 3.7.0'
|
||||
end
|
|
@ -1,60 +0,0 @@
|
|||
class HomebrewFormulaFish271 < Formula
|
||||
desc "User-friendly command-line shell for UNIX-like operating systems"
|
||||
homepage "https://fishshell.com"
|
||||
url "https://github.com/fish-shell/fish-shell/releases/download/2.7.1/fish-2.7.1.tar.gz"
|
||||
mirror "https://fishshell.com/files/2.7.1/fish-2.7.1.tar.gz"
|
||||
sha256 "e42bb19c7586356905a58578190be792df960fa81de35effb1ca5a5a981f0c5a"
|
||||
|
||||
bottle do
|
||||
sha256 "affac16a396410a500241266dbbbd8752562c9b800d9e4f2ef8de279c6fdb6aa" => :high_sierra
|
||||
sha256 "335538a7ea7f9613474f7321af0a1d519b61b0fc4be97a1744a7ea7bef7ff7e3" => :sierra
|
||||
sha256 "463cfa8edc0603761f25e0ba4e49524f69a1d856263d370d1de5fb8698dd5ccf" => :el_capitan
|
||||
end
|
||||
|
||||
head do
|
||||
url "https://github.com/fish-shell/fish-shell.git", :shallow => false
|
||||
|
||||
depends_on "autoconf" => :build
|
||||
depends_on "automake" => :build
|
||||
depends_on "doxygen" => :build
|
||||
end
|
||||
|
||||
depends_on "pcre2"
|
||||
|
||||
def install
|
||||
system "autoreconf", "--no-recursive" if build.head?
|
||||
|
||||
# In Homebrew's 'superenv' sed's path will be incompatible, so
|
||||
# the correct path is passed into configure here.
|
||||
args = %W[
|
||||
--prefix=#{prefix}
|
||||
--with-extra-functionsdir=#{HOMEBREW_PREFIX}/share/fish/vendor_functions.d
|
||||
--with-extra-completionsdir=#{HOMEBREW_PREFIX}/share/fish/vendor_completions.d
|
||||
--with-extra-confdir=#{HOMEBREW_PREFIX}/share/fish/vendor_conf.d
|
||||
SED=/usr/bin/sed
|
||||
]
|
||||
system "./configure", *args
|
||||
system "make", "install"
|
||||
end
|
||||
|
||||
def caveats; <<~EOS
|
||||
You will need to add:
|
||||
#{HOMEBREW_PREFIX}/bin/fish
|
||||
to /etc/shells.
|
||||
|
||||
Then run:
|
||||
chsh -s #{HOMEBREW_PREFIX}/bin/fish
|
||||
to make fish your default shell.
|
||||
EOS
|
||||
end
|
||||
|
||||
def post_install
|
||||
(pkgshare/"vendor_functions.d").mkpath
|
||||
(pkgshare/"vendor_completions.d").mkpath
|
||||
(pkgshare/"vendor_conf.d").mkpath
|
||||
end
|
||||
|
||||
test do
|
||||
system "#{bin}/fish", "-c", "echo"
|
||||
end
|
||||
end
|
|
@ -1,162 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
# Includes completion examples with the intent to cover
|
||||
# all sdk commands
|
||||
# Beware, pasta follows.
|
||||
|
||||
test_cases = {
|
||||
# Basic commands (in the order of `sdk help`):
|
||||
"i" => ["i", "install"],
|
||||
"u" => ["u", "ug", "uninstall", "update", "upgrade", "use"],
|
||||
"r" => ["rm"],
|
||||
"l" => ["list", "ls"],
|
||||
"d" => ["d", "default"],
|
||||
"c" => ["c", "current"],
|
||||
"v" => ["v", "version"],
|
||||
"b" => ["b", "broadcast"],
|
||||
"h" => ["h", "help"],
|
||||
"o" => ["offline"],
|
||||
"s" => ["selfupdate"],
|
||||
"f" => ["flush"],
|
||||
# Currently uncovered; include to catch new upstream commands:
|
||||
"a" => [],
|
||||
"e" => [],
|
||||
"g" => [],
|
||||
"j" => [],
|
||||
"k" => [],
|
||||
"m" => [],
|
||||
"n" => [],
|
||||
"p" => [],
|
||||
"q" => [],
|
||||
"t" => [],
|
||||
"w" => [],
|
||||
"x" => [],
|
||||
"y" => [],
|
||||
"z" => [],
|
||||
|
||||
# Second parameters complete correctly
|
||||
"install an" => ["ant"],
|
||||
"install xyz" => [],
|
||||
"install 1." => [],
|
||||
|
||||
"uninstall " => ["ant"],
|
||||
"uninstall a" => ["ant"],
|
||||
"uninstall j" => [],
|
||||
"uninstall 1." => [],
|
||||
|
||||
"list an" => ["ant"],
|
||||
"list xyz" => [],
|
||||
"list 1." => [],
|
||||
|
||||
"use " => ["ant"],
|
||||
"use an" => ["ant"],
|
||||
"use j" => [],
|
||||
"use 1." => [],
|
||||
|
||||
"default " => ["ant"],
|
||||
"default an" => ["ant"],
|
||||
"default j" => [],
|
||||
"default 1." => [],
|
||||
|
||||
"current an" => ["ant"],
|
||||
"current xyz" => [],
|
||||
"current 1." => [],
|
||||
|
||||
"upgrade " => ["ant"],
|
||||
"upgrade an" => ["ant"],
|
||||
"upgrade j" => [],
|
||||
"upgrade 1." => [],
|
||||
|
||||
"version " => [],
|
||||
"version a" => [],
|
||||
|
||||
"broadcast " => [],
|
||||
"broadcast a" => [],
|
||||
|
||||
"help " => [],
|
||||
"help a" => [],
|
||||
|
||||
"offline " => ["enable", "disable"],
|
||||
"offline a" => [],
|
||||
|
||||
"selfupdate " => ["force"],
|
||||
"selfupdate a" => [],
|
||||
|
||||
"update " => [],
|
||||
"update a" => [],
|
||||
|
||||
"flush " => ["broadcast", "archives", "temp"],
|
||||
"flush x" => [],
|
||||
|
||||
# Third parameters complete correctly
|
||||
#"install ant 1.10." => ["1.10.0", "1.10.1"], # TODO: issue #4
|
||||
"uninstall ant 1.10." => ["1.10.1"],
|
||||
"list ant " => [],
|
||||
"use ant " => ["1.9.9", "1.10.1"],
|
||||
"default ant " => ["1.9.9", "1.10.1"],
|
||||
"current ant " => [],
|
||||
"upgrade ant " => [],
|
||||
"offline ant " => [],
|
||||
"selfupdate ant " => [],
|
||||
"flush ant " => [],
|
||||
|
||||
# Fourth parameters complete correctly
|
||||
"install ant 1.10.2-mine /tm" => ["/tmp/"],
|
||||
"uninstall ant 1.10.1 " => [],
|
||||
"use ant 1.10.1 " => [],
|
||||
"default ant 1.10.1 " => [],
|
||||
|
||||
# Fifth parameters complete correctly
|
||||
"install ant 1.10.2-mine /tmp " => [],
|
||||
|
||||
# Lists of all candidates work
|
||||
"install gr" => ["gradle", "grails", "groovy", "groovyserv"],
|
||||
"install grad" => ["gradle"],
|
||||
"install gradk" => [],
|
||||
|
||||
# Lists of installed candidates work
|
||||
"uninstall an" => ["ant"],
|
||||
"uninstall gr" => [],
|
||||
"uninstall xyz" => [],
|
||||
|
||||
# List of all versions work
|
||||
# TODO
|
||||
|
||||
# List of installed versions work
|
||||
"uninstall ant 1" => ["1.9.9", "1.10.1"],
|
||||
"uninstall ant 2" => [],
|
||||
"uninstall vertx " => [],
|
||||
"uninstall an " => []
|
||||
}
|
||||
|
||||
def fish_command(prompt)
|
||||
# Fish errors out if we don't set terminal dimensions
|
||||
"fish -c 'stty rows 80 columns 80; complete -C\"sdk #{prompt}\"'"
|
||||
end
|
||||
|
||||
def extract(completion_output)
|
||||
completion_output.split("\n").map { |line|
|
||||
line.split(/\s+/)[0].strip
|
||||
}
|
||||
end
|
||||
|
||||
puts "Testing sdk completions"
|
||||
failures = 0
|
||||
test_cases.each { |prompt, results|
|
||||
results.sort!
|
||||
|
||||
print " Test: 'sdk #{prompt}'"
|
||||
completions = extract(`#{fish_command(prompt)}`).sort
|
||||
if completions != results
|
||||
puts " -- bad!"
|
||||
puts " - Expected: #{results}"
|
||||
puts " - Actual: #{completions}"
|
||||
failures += 1
|
||||
else
|
||||
puts " -- ok!"
|
||||
end
|
||||
}
|
||||
|
||||
puts "Ran #{test_cases.size} checks each."
|
||||
puts "#{failures}/#{test_cases.size} checks failed."
|
||||
exit failures > 0 ? 1 : 0
|
186
test/features/completions.feature
Normal file
186
test/features/completions.feature
Normal file
|
@ -0,0 +1,186 @@
|
|||
Feature: Shell Completion
|
||||
We want to get the correct completion on the CLI.
|
||||
|
||||
Background:
|
||||
Given SDKMAN! candidate list is up to date
|
||||
And candidate ant is installed at version 1.9.9
|
||||
And candidate ant is installed at version 1.10.1
|
||||
And candidate crash is installed
|
||||
|
||||
Scenario: Command list correct
|
||||
When the user enters " " into the prompt
|
||||
Then completion should propose "b, broadcast, c, current, d, default, flush, h, help, i, install, list, ls, offline, rm, selfupdate, u, ug, uninstall, update, upgrade, use, v, version"
|
||||
|
||||
Scenario Outline: Commands complete
|
||||
When the user enters "<cmd>" into the prompt
|
||||
Then completion should propose "<completions>"
|
||||
Examples:
|
||||
| cmd | completions |
|
||||
| b | b, broadcast |
|
||||
| c | c, current |
|
||||
| d | d, default |
|
||||
| f | flush |
|
||||
| h | h, help |
|
||||
| i | i, install |
|
||||
| in | install |
|
||||
| l | list, ls |
|
||||
| o | offline |
|
||||
| r | rm |
|
||||
| s | selfupdate |
|
||||
| u | u, ug, uninstall, update, upgrade, use |
|
||||
| un | uninstall |
|
||||
| up | update, upgrade |
|
||||
| us | use |
|
||||
| v | v, version |
|
||||
# Currently uncovered; include to catch new upstream commands:
|
||||
| a | |
|
||||
| e | |
|
||||
| g | |
|
||||
| j | |
|
||||
| k | |
|
||||
| m | |
|
||||
| n | |
|
||||
| p | |
|
||||
| q | |
|
||||
| t | |
|
||||
| w | |
|
||||
| x | |
|
||||
| y | |
|
||||
| z | |
|
||||
|
||||
Scenario Outline: Completion for 'install'
|
||||
When the user enters "install <cmd>" into the prompt
|
||||
Then completion should propose "<completions>"
|
||||
Examples:
|
||||
| cmd | completions |
|
||||
| an | ant |
|
||||
| xyz | |
|
||||
| 1. | |
|
||||
| gra | gradle, grails |
|
||||
| grad | gradle |
|
||||
| gradk | |
|
||||
# | ant 1.10. | 1.10.0, 1.10.1 | # TODO: list installable versions --> issue #4
|
||||
| ant 1.10.2-mine /tm | /tmp/ |
|
||||
| 'ant 1.10.2-mine /tmp ' | |
|
||||
|
||||
Scenario Outline: Completion for 'uninstall'
|
||||
When the user enters "uninstall <cmd>" into the prompt
|
||||
Then completion should propose "<completions>"
|
||||
Examples:
|
||||
| cmd | completions |
|
||||
| | ant, crash |
|
||||
| a | ant |
|
||||
| j | |
|
||||
| 1. | |
|
||||
| an | ant | # installed
|
||||
| gr | | # none installed
|
||||
| xyz | | # no such candidate
|
||||
| 'an ' | | # no such candidate installed
|
||||
| 'ant 1' | 1.10.1, 1.9.9 |
|
||||
| 'ant 1.10.' | 1.10.1 |
|
||||
| 'ant 2' | |
|
||||
| 'ant 1.10.1 ' | |
|
||||
|
||||
Scenario Outline: Completion for 'list'
|
||||
When the user enters "list <cmd>" into the prompt
|
||||
Then completion should propose "<completions>"
|
||||
Examples:
|
||||
| cmd | completions |
|
||||
| an | ant |
|
||||
| xyz | |
|
||||
| 1. | |
|
||||
| 'ant ' | |
|
||||
|
||||
Scenario Outline: Completion for 'use'
|
||||
When the user enters "use <cmd>" into the prompt
|
||||
Then completion should propose "<completions>"
|
||||
Examples:
|
||||
| cmd | completions |
|
||||
| | ant, crash |
|
||||
| an | ant |
|
||||
| j | |
|
||||
| 1. | |
|
||||
| 'ant ' | 1.10.1, 1.9.9 |
|
||||
| 'ant 1.10.1 ' | |
|
||||
|
||||
Scenario Outline: Completion for 'default'
|
||||
When the user enters "default <cmd>" into the prompt
|
||||
Then completion should propose "<completions>"
|
||||
Examples:
|
||||
| cmd | completions |
|
||||
| | ant, crash |
|
||||
| an | ant |
|
||||
| j | |
|
||||
| 1. | |
|
||||
| 'ant ' | 1.10.1, 1.9.9 |
|
||||
| 'ant 1.10.1 ' | |
|
||||
|
||||
Scenario Outline: Completion for 'current'
|
||||
When the user enters "current <cmd>" into the prompt
|
||||
Then completion should propose "<completions>"
|
||||
Examples:
|
||||
| cmd | completions |
|
||||
| an | ant | # --> installed version
|
||||
| ja | java | # --> not installed
|
||||
| xyz | |
|
||||
| 1. | |
|
||||
| 'ant ' | |
|
||||
|
||||
Scenario Outline: Completion for 'upgrade'
|
||||
When the user enters "upgrade <cmd>" into the prompt
|
||||
Then completion should propose "<completions>"
|
||||
Examples:
|
||||
| cmd | completions |
|
||||
| | ant, crash |
|
||||
| an | ant |
|
||||
| j | |
|
||||
| 1. | |
|
||||
| 'ant ' | |
|
||||
|
||||
Scenario Outline: Completion for 'offline'
|
||||
When the user enters "offline <cmd>" into the prompt
|
||||
Then completion should propose "<completions>"
|
||||
Examples:
|
||||
| cmd | completions |
|
||||
| | disable, enable |
|
||||
| e | enable |
|
||||
| d | disable |
|
||||
| a | |
|
||||
| 'enable ' | |
|
||||
|
||||
Scenario Outline: Completion for 'selfupdate'
|
||||
When the user enters "selfupdate <cmd>" into the prompt
|
||||
Then completion should propose "<completions>"
|
||||
Examples:
|
||||
| cmd | completions |
|
||||
| | force |
|
||||
| f | force |
|
||||
| a | |
|
||||
| 'force ' | |
|
||||
|
||||
Scenario Outline: Completion for 'flush'
|
||||
When the user enters "flush <cmd>" into the prompt
|
||||
Then completion should propose "<completions>"
|
||||
Examples:
|
||||
| cmd | completions |
|
||||
| | archives, broadcast, temp |
|
||||
| b | broadcast |
|
||||
| a | archives |
|
||||
| t | temp |
|
||||
| x | |
|
||||
| 'temp ' | |
|
||||
|
||||
|
||||
Scenario Outline: Completion for commands without parameters
|
||||
When the user enters "<cmd>" into the prompt
|
||||
Then completion should propose ""
|
||||
Examples:
|
||||
| cmd |
|
||||
| 'version ' |
|
||||
| 'version a' |
|
||||
| 'broadcast ' |
|
||||
| 'broadcast a' |
|
||||
| 'help ' |
|
||||
| 'help a' |
|
||||
| 'update ' |
|
||||
| 'update a' |
|
26
test/features/step_definitions/completion.rb
Normal file
26
test/features/step_definitions/completion.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'open3'
|
||||
|
||||
module CompletionHelper
|
||||
def complete(cmd)
|
||||
completions = run_fish_command("complete -C\"sdk #{cmd}\"")
|
||||
|
||||
completions.split("\n") \
|
||||
.map { |line| line.split(/\s+/)[0].strip } \
|
||||
.sort \
|
||||
.uniq \
|
||||
.join(', ')
|
||||
# TODO: Why do we get duplicates in the Docker container?
|
||||
# --> remove uniq
|
||||
end
|
||||
end
|
||||
World CompletionHelper
|
||||
|
||||
When('the user enters {string} into the prompt') do |cmd|
|
||||
@response = complete(cmd.gsub(/["']/, ''))
|
||||
end
|
||||
|
||||
Then(/^completion should propose "(.*)"$/) do |completions|
|
||||
expect(@response).to eq(completions)
|
||||
end
|
31
test/features/step_definitions/setup.rb
Normal file
31
test/features/step_definitions/setup.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
$index_updated = false # TODO: Hack since Cucumber doesn't have Feature-level hooks
|
||||
Given(/^SDKMAN! candidate list is up to date$/) do
|
||||
unless $index_updated
|
||||
run_bash_command('sdk update')
|
||||
$index_updated = true
|
||||
end
|
||||
end
|
||||
|
||||
Given(/^candidate (\w+) is installed at version (\d+(?:\.\d+)*)$/) do |candidate, version|
|
||||
run_bash_command("sdk install #{candidate} #{version}") unless installed?(candidate, version)
|
||||
end
|
||||
|
||||
Given(/^candidate (\w+) is installed$/) do |candidate|
|
||||
run_bash_command("sdk install #{candidate}") unless installed?(candidate)
|
||||
end
|
||||
|
||||
# Uninstall all SDKMAN! candidates
|
||||
# TODO: Run after every scenario, this makes tests very slow.
|
||||
# Currently, Cucumber doesn't have Feature-level hooks, so we have to work around:
|
||||
# --> install only if not already installed;
|
||||
# if the test needs a candidate to _not_ be there, make it explicit.
|
||||
# --> clean up after _all_ features at least
|
||||
at_exit do
|
||||
Dir["#{ENV['HOME']}/.sdkman/candidates/*/*"].each do |candidate_dir|
|
||||
%r{/([^/]+)/([^/]+)$}.match(candidate_dir) do |match|
|
||||
candidate = match[1]
|
||||
version = match[2]
|
||||
run_bash_command("sdk rm #{candidate} #{version}") unless version == 'current'
|
||||
end
|
||||
end
|
||||
end
|
42
test/features/support/helpers.rb
Normal file
42
test/features/support/helpers.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
def list_installed_candidates
|
||||
candidates = {}
|
||||
|
||||
Dir["#{ENV['HOME']}/.sdkman/candidates/*/*"].each do |candidate_dir|
|
||||
%r{/([^/]+)/([^/]+)$}.match(candidate_dir) do |match|
|
||||
candidate = match[1]
|
||||
version = match[2]
|
||||
candidates[candidate] = [] unless candidates.key?(candidate)
|
||||
candidates[candidate].push version unless version == 'current'
|
||||
end
|
||||
end
|
||||
|
||||
candidates
|
||||
end
|
||||
|
||||
def installed?(candidate, version = nil)
|
||||
candidates = list_installed_candidates
|
||||
candidates.key?(candidate) && (version.nil? || candidates[candidate].include?(version))
|
||||
end
|
||||
|
||||
def run_bash_command(cmd)
|
||||
stdout, stderr, status = Open3.capture3("bash -c 'source \"$HOME/.sdkman/bin/sdkman-init.sh\"; #{cmd}'")
|
||||
unless status.success?
|
||||
warn(stderr)
|
||||
raise "Bash command failed: #{stderr}"
|
||||
end
|
||||
|
||||
stdout
|
||||
end
|
||||
|
||||
def run_fish_command(cmd)
|
||||
# NB: Fish errors out if we don't set terminal dimensions
|
||||
stdout, stderr, status = Open3.capture3("fish -c 'stty rows 80 columns 80; #{cmd}'")
|
||||
unless status.success?
|
||||
warn(stderr)
|
||||
raise 'Fish command failed'
|
||||
end
|
||||
|
||||
stdout
|
||||
end
|
|
@ -3,8 +3,7 @@
|
|||
source "${HOME}"/.sdkman/bin/sdkman-init.sh
|
||||
|
||||
# Set up an SDK with two installed versions
|
||||
# --> test of `sdk use` in wrapper.fish
|
||||
# --> tests in completion.rb
|
||||
# --> test of `sdk use` in wrapper.fish
|
||||
sdk install ant 1.9.9
|
||||
echo "y" | sdk install ant 1.10.1
|
||||
sdk default ant 1.10.1
|
||||
sdk default ant 1.10.1
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue