ci-syntax-tool 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/LICENSE +27 -0
  4. data/README.md +109 -0
  5. data/Rakefile +43 -0
  6. data/bin/ci-syntax-tool +12 -0
  7. data/ci-syntax-tool.gemspec +27 -0
  8. data/lib/ci-syntax-tool.rb +6 -0
  9. data/lib/ci-syntax-tool/checker.rb +63 -0
  10. data/lib/ci-syntax-tool/command_line.rb +229 -0
  11. data/lib/ci-syntax-tool/format/base.rb +50 -0
  12. data/lib/ci-syntax-tool/format/junit.rb +54 -0
  13. data/lib/ci-syntax-tool/format/progress.rb +60 -0
  14. data/lib/ci-syntax-tool/format_factory.rb +55 -0
  15. data/lib/ci-syntax-tool/language/base.rb +56 -0
  16. data/lib/ci-syntax-tool/language/yaml.rb +41 -0
  17. data/lib/ci-syntax-tool/language_factory.rb +41 -0
  18. data/lib/ci-syntax-tool/result.rb +134 -0
  19. data/lib/ci-syntax-tool/version.rb +10 -0
  20. data/rubocop.yml +11 -0
  21. data/test/features/.keep +0 -0
  22. data/test/features/command-line-help.feature +15 -0
  23. data/test/features/format-junit.feature +29 -0
  24. data/test/features/language-yaml.feature +34 -0
  25. data/test/features/pluggable-formatters.feature +42 -0
  26. data/test/features/pluggable-languages.feature +15 -0
  27. data/test/features/require-ruby.feature +38 -0
  28. data/test/features/step_definitions/cli_steps.rb +46 -0
  29. data/test/features/step_definitions/format_steps.rb +63 -0
  30. data/test/features/step_definitions/junit_steps.rb +57 -0
  31. data/test/features/step_definitions/language_steps.rb +39 -0
  32. data/test/features/step_definitions/require_steps.rb +38 -0
  33. data/test/features/support/feature_helper.rb +142 -0
  34. data/test/fixtures/.keep +0 -0
  35. data/test/fixtures/files/clean/README.md +6 -0
  36. data/test/fixtures/files/clean/ansiblish.yaml +12 -0
  37. data/test/fixtures/files/clean/kitchenish.yml +17 -0
  38. data/test/fixtures/files/clean/rubocopish.yaml +11 -0
  39. data/test/fixtures/files/error/bad-indentation.yaml +5 -0
  40. data/test/fixtures/files/error/missing-array-element.yaml +5 -0
  41. data/test/fixtures/files/error/unquoted-jinja-template.yaml +3 -0
  42. data/test/fixtures/files/error/very-high-yaml-version.yaml +3 -0
  43. data/test/fixtures/require/invalid.rb +6 -0
  44. data/test/fixtures/require/mock_format.rb +10 -0
  45. data/test/fixtures/require/second.rb +10 -0
  46. data/test/fixtures/require/valid.rb +10 -0
  47. data/test/unit/.keep +0 -0
  48. data/test/unit/format_factory_spec.rb +46 -0
  49. data/test/unit/language_factory_spec.rb +46 -0
  50. data/test/unit/result_spec.rb +18 -0
  51. data/test/unit/spec_helper.rb +31 -0
  52. metadata +201 -0
@@ -0,0 +1,10 @@
1
+ module CI
2
+ module Syntax
3
+ # CI::Syntax::Tool module
4
+ # Simple container for namespacing the project. Here, just the
5
+ # version string.
6
+ module Tool
7
+ VERSION = '0.1.0'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ Style/FileName:
2
+ Enabled: False
3
+
4
+ Metrics/MethodLength:
5
+ Max: 25
6
+
7
+ Style/TrailingWhitespace:
8
+ Enabled: False
9
+
10
+ Style/TrailingComma:
11
+ Enabled: False
File without changes
@@ -0,0 +1,15 @@
1
+ Feature: Command line help
2
+
3
+ In order to be able to learn about the options available for syntax checking
4
+ As a developer
5
+ I want to be able to interactively get help on the options from the command line
6
+
7
+ Scenario: Command help
8
+ Given I have installed the tool
9
+ When I run it on the command line with the help option
10
+ Then the simple usage text should be displayed along with a zero exit code
11
+
12
+ Scenario: Display version
13
+ Given I have installed the tool
14
+ When I run it on the command line with the version option
15
+ Then the current version should be displayed
@@ -0,0 +1,29 @@
1
+ Feature: jUnit / SureFire format
2
+
3
+ In order to be able to use the tool with many popular CI tools
4
+ As a developer
5
+ I want to be able to have output formatted as jUnit-style XML reports
6
+
7
+ Scenario: List available core formats
8
+ Given I have installed the tool
9
+ When I run it on the command line with the list-formats option
10
+ Then JUnit should be included in the listed formats
11
+
12
+ Scenario: Use JUnit format on clean files
13
+ Given I have installed the tool
14
+ When I run it on the command line with the JUnit format on clean files
15
+ Then I should get a 0 exit code
16
+ And the generated files should be valid XML
17
+ And the JUnit files should have the correct structure
18
+ And the JUnit files should have 0 errors
19
+ And the JUnit files should have 0 warnings
20
+
21
+ Scenario: Use JUnit format on error files
22
+ Given I have installed the tool
23
+ When I run it on the command line with the JUnit format on error files
24
+ Then I should get a 1 exit code
25
+ And the generated files should be valid XML
26
+ And the JUnit files should have the correct structure
27
+ And the JUnit files should have 3 errors
28
+ And the JUnit files should have 0 warnings
29
+
@@ -0,0 +1,34 @@
1
+ Feature: Pluggable formatters
2
+
3
+ In order to be able to check YAML files for errors
4
+ As a developer
5
+ I want to be able to run the tool on yaml files
6
+
7
+ Scenario: Use YAML explicitly on clean files
8
+ Given I have installed the tool
9
+ When I run it on the command line specifying the YAML language and clean files
10
+ Then I should get a 0 exit code
11
+ And the output should show only files for YAML
12
+ And the output should have 0 warnings
13
+ And the output should have 0 errors
14
+
15
+
16
+ Scenario: Run all lamnguages on clean files
17
+ Given I have installed the tool
18
+ When I run it on the command line specifying all languages and clean files
19
+ Then I should get a 0 exit code
20
+ And the output should include files for YAML
21
+ And the output should have 0 warnings
22
+ And the output should have 0 errors
23
+
24
+
25
+ Scenario: Use YAML explicitly on error files
26
+ Given I have installed the tool
27
+ When I run it on the command line specifying the YAML language and error files
28
+ Then I should get a 1 exit code
29
+ And the output should show only files for YAML
30
+ And the output should have 0 warnings
31
+ And the output should have 3 errors
32
+
33
+
34
+
@@ -0,0 +1,42 @@
1
+ Feature: Pluggable formatters
2
+
3
+ In order to be able to use the tool with whatever CI engine I need
4
+ As a developer
5
+ I want to be able to output the results of language checks in various formats
6
+
7
+ Scenario: List available core formats
8
+ Given I have installed the tool
9
+ When I run it on the command line with the list-formats option
10
+ Then I should get a list of the core formats along with a zero exit code
11
+
12
+ Scenario: Reject an invalid format
13
+ Given I have installed the tool
14
+ When I run it on the command line with the format option and the argument foo
15
+ Then I should get an error message and the exit code 4
16
+
17
+ Scenario: Reject multiple formats without destination
18
+ Given I have installed the tool
19
+ When I run it on the command line with two format options and 0 destinations
20
+ Then I should get an error message and the exit code 4
21
+
22
+ Scenario: Reject multiple formats with ambiguous destination count
23
+ Given I have installed the tool
24
+ When I run it on the command line with two format options and 1 destinations
25
+ Then I should get an error message and the exit code 4
26
+
27
+ Scenario: Use multiple formatters with correct number of destinations
28
+ Given I have installed the tool
29
+ When I run it on the command line with two format options and 2 destinations
30
+ Then I should get a 0 exit code
31
+
32
+ Scenario: Create destination files automatically
33
+ Given I have installed the tool
34
+ When I run it on the command line with a destination in the current directory
35
+ Then the file should be created
36
+
37
+ Scenario: Create destination directories automatically
38
+ Given I have installed the tool
39
+ When I run it on the command line with a destination in a directory that does not exist
40
+ Then the file should be created
41
+
42
+
@@ -0,0 +1,15 @@
1
+ Feature: Pluggable languages
2
+
3
+ In order to be able to use the tool on whatever language I need
4
+ As a developer
5
+ I want to be able to extend the tool by adding support for new languages
6
+
7
+ Scenario: List available core languages
8
+ Given I have installed the tool
9
+ When I run it on the command line with the list-languages option
10
+ Then I should get a list of the core languages along with a zero exit code
11
+
12
+ Scenario: Reject an invalid language
13
+ Given I have installed the tool
14
+ When I run it on the command line with the lang option and the argument foo
15
+ Then I should get an error message and the exit code 3
@@ -0,0 +1,38 @@
1
+ Feature: Load extra Ruby files
2
+
3
+ In order to be able to use the tool with custom plugins
4
+ As a developer
5
+ I want to be able to load additional ruby files
6
+
7
+ Scenario: Load a valid ruby file
8
+ Given I have installed the tool
9
+ When I run it on the command line with the require option and a valid ruby file
10
+ Then the valid ruby file should have been loaded
11
+ And I should get a 0 exit code
12
+
13
+ Scenario: Reject a missing file with a nice message
14
+ Given I have installed the tool
15
+ When I run it on the command line with the require option and a missing ruby file
16
+ Then I should get an error message about the missing require file
17
+ And I should get a 5 exit code
18
+
19
+ Scenario: Reject an invalid file with a nice message
20
+ Given I have installed the tool
21
+ When I run it on the command line with the require option and a invalid ruby file
22
+ Then I should get an error message about the invalid require file
23
+ And I should get a 5 exit code
24
+
25
+ Scenario: Reject an invalid file with a stacktrace when debug flag is present
26
+ Given I have installed the tool
27
+ When I run it on the command line with the require option and the debug option and a invalid ruby file
28
+ Then I should get a stack trace about the invalid require file
29
+ And I should get a 5 exit code
30
+
31
+ Scenario: Accept multiple requires
32
+ Given I have installed the tool
33
+ When I run it on the command line with two requires
34
+ Then the first ruby file should have been loaded
35
+ And the second ruby file should have been loaded
36
+ And I should get a 0 exit code
37
+
38
+
@@ -0,0 +1,46 @@
1
+ Given(/^I have installed the tool$/) do
2
+ end
3
+
4
+ # TODO: Add code to target fixture directory by default
5
+ When(/^I run it on the command line with the ([^ ]+) option(?: and the argument )?([^ ]+)?$/) do |option, arg|
6
+ options = []
7
+ if option.match(/\-\w$/)
8
+ options << option
9
+ else
10
+ options << "--#{option}"
11
+ end
12
+ if arg
13
+ options << arg
14
+ end
15
+ @run_result = run_check(options)
16
+ end
17
+
18
+ Then(/^the simple usage text should be displayed along with a non\-zero exit code$/) do
19
+ refute_empty(@run_result[:stderr], 'Expected to see an error message on STDERR')
20
+ refute_equal(@run_result[:exit_status], 0)
21
+ assert_usage_message
22
+ end
23
+
24
+ Then(/^the simple usage text should be displayed along with a zero exit code$/) do
25
+ assert_empty(@run_result[:stderr])
26
+ assert_equal(@run_result[:exit_status], 0)
27
+ assert_usage_message
28
+ end
29
+
30
+ Then(/^the current version should be displayed$/) do
31
+ assert_match(/^ci-syntax-tool/, @run_result[:stdout], 'Version string should include tool name')
32
+ assert_match(Regexp.new(Regexp.escape(CI::Syntax::Tool::VERSION)), @run_result[:stdout], 'Version string should include the version')
33
+ assert_equal(@run_result[:stdout].split("\n").length, 1, 'Version output should be exactly one line long')
34
+ assert_empty(@run_result[:stderr])
35
+ assert_equal(@run_result[:exit_status], 0)
36
+ end
37
+
38
+ Then(/^I should get an error message and the exit code (\d+)$/) do |expected_exit_status|
39
+ refute_empty(@run_result[:stderr], 'Expected to see an error message on STDERR')
40
+ assert_equal(expected_exit_status.to_i, @run_result[:exit_status])
41
+ end
42
+
43
+ Then(/^I should get a (\d+) exit code$/) do |expected_exit_status|
44
+ assert_equal(expected_exit_status.to_i, @run_result[:exit_status].to_i)
45
+ end
46
+
@@ -0,0 +1,63 @@
1
+ require 'fileutils'
2
+
3
+ When(/^I run it on the command line with two format options and (\d) destination(?:s)$/) do |dest_count|
4
+ options = [
5
+ '--require', Dir.pwd + '/' + 'test/fixtures/require/mock_format.rb',
6
+ '--format', 'MockFormat',
7
+ '--format', 'MockFormat',
8
+ ]
9
+ (0..(dest_count.to_i-1)).to_a.each do |i|
10
+ options << '--output'
11
+ options << 'test/tmp/dest-' + i.to_s
12
+ end
13
+ options << 'test/fixtures/files/clean'
14
+ @run_result = run_check(options)
15
+ end
16
+
17
+ When(/^I run it on the command line with a destination in (the current directory|a directory that does not exist)$/) do |mode|
18
+ path = 'output-create-test-1'
19
+ options = [
20
+ '--output'
21
+ ]
22
+ if mode == 'the current directory'
23
+ FileUtils.rm_f path
24
+ options << path
25
+ else
26
+ dir = 'test/tmp/output-create-dir'
27
+ FileUtils.rm_f dir
28
+ options << dir + '/' + path
29
+ end
30
+ options << 'test/fixtures/files/clean'
31
+ @expected_output_paths = [ path ]
32
+ @run_result = run_check(options)
33
+ end
34
+
35
+ When(/^I run it on the command line with the ([^ ]+) format on ([^ ]+) files$/) do |fmt_name, fixture|
36
+ path = "test/tmp/#{fmt_name}.out"
37
+ FileUtils.rm_f path
38
+ options = [
39
+ '--format', fmt_name,
40
+ '--output', path,
41
+ '--lang', 'YAML',
42
+ "test/fixtures/files/#{fixture}",
43
+ ]
44
+ @expected_output_paths = [ path ]
45
+ @run_result = run_check(options)
46
+ end
47
+
48
+ Then(/^I should get a list of the core formats along with a zero exit code$/) do
49
+ assert_equal(0, @run_result[:exit_status])
50
+ assert_format_list
51
+ end
52
+
53
+ Then(/^([^ ]+) should be included in the listed formats$/) do |fmt_name|
54
+ actual = @run_result[:stdout].split("\n")
55
+ assert_includes(actual, fmt_name)
56
+ end
57
+
58
+ Then(/^the file(?:s)? should be created$/) do
59
+ @expected_output_paths.each do |path|
60
+ assert(File.exist?(path), "#{path} should exist")
61
+ end
62
+ end
63
+
@@ -0,0 +1,57 @@
1
+ require 'nokogiri'
2
+
3
+ Then(/^the generated files should be valid XML$/) do
4
+ @expected_output_paths.each do |path|
5
+ begin
6
+ doc = File.open(path) { |f| Nokogiri::XML(f) }
7
+ pass
8
+ rescue
9
+ flunk
10
+ end
11
+ end
12
+ end
13
+
14
+ Then(/^the JUnit files should have the correct structure$/) do
15
+ @expected_output_paths.each do |path|
16
+
17
+ # puts "POINT A:" + path
18
+
19
+ doc = File.open(path) { |f| Nokogiri::XML(f) }
20
+
21
+ # puts "POINT B:" + doc.to_s
22
+ assert_equal(1, doc.xpath('/testsuites').length, "It should have one root testsuites element")
23
+
24
+ # It should have nonzero testsuite elements with a name attribute reflecting the language
25
+ match = doc.xpath('/testsuites/testsuite')
26
+ assert(match.length > 0, "It should have nonzero testsuite elements")
27
+ langs = CI::Syntax::Tool::LanguageFactory.all_language_names
28
+ match.each do |node|
29
+ assert_includes(langs, node.at_xpath('@name').to_s)
30
+ end
31
+
32
+ # It should have nonzero testcase elements, one per file, with name = sanitized filename
33
+ match = doc.xpath('/testsuites/testsuite/testcase')
34
+ assert(match.length > 0, "It should have nonzero testcase elements")
35
+ match.each do |node|
36
+ file = node.at_xpath('@name').to_s
37
+ refute_empty(file, "testcase should have a name attribute")
38
+ end
39
+
40
+ # It may have failure elements which must have a type=warning or error
41
+ match = doc.xpath('/testsuites/testsuite/testcase/failure')
42
+ match.each do |node|
43
+ type = node.at_xpath('@type').to_s
44
+ assert_includes(['warning', 'error'], type, "Each failure type should be either a warning or an error")
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+ Then(/^the JUnit files should have (\d+) (error|warning)s$/) do |count, level|
51
+ actual_count = 0
52
+ @expected_output_paths.each do |path|
53
+ doc = File.open(path) { |f| Nokogiri::XML(f) }
54
+ actual_count += doc.xpath("//failure[@type=\"#{level}\"]").length
55
+ end
56
+ assert_equal(count.to_i, actual_count)
57
+ end
@@ -0,0 +1,39 @@
1
+ When(/^I run it on the command line specifying(?: the)? ([^ ]+) language(?:s)? and ([^ ]+) files$/) do |lang_name, fixture_group|
2
+ options = [
3
+ '--require', Dir.pwd + '/' + 'test/fixtures/require/mock_format.rb',
4
+ '--format', 'MockFormat',
5
+ ]
6
+ unless lang_name == 'all'
7
+ options << '--lang'
8
+ options << lang_name
9
+ end
10
+ options << 'test/fixtures/files/' + fixture_group
11
+
12
+ @run_result = run_check(options)
13
+ end
14
+
15
+ Then(/^I should get a list of the core languages along with a zero exit code$/) do
16
+ assert_equal(0, @run_result[:exit_status].to_i)
17
+ assert_language_list
18
+ end
19
+
20
+ Then(/^the output should have (\d+) (error|warning)s$/) do |count, level|
21
+ actual = @run_result[:overall_result].send((level +'_count').to_sym)
22
+ assert_equal(count.to_i, actual)
23
+ end
24
+
25
+ Then(/^the output should show only files for ([^ ]+)$/) do |lang_name|
26
+ touched = @run_result[:overall_result].file_paths
27
+ matched = files_matching_language(touched, lang_name)
28
+ extra = touched - matched
29
+ assert_empty(extra, "The touched files should ONLY include files for #{lang_name}")
30
+ refute_empty(matched, "The touched files should include files for #{lang_name}")
31
+ end
32
+
33
+
34
+ Then(/^the output should include files for ([^ ]+)$/) do | lang_name|
35
+ touched = @run_result[:overall_result].file_paths
36
+ matched = files_matching_language(touched, lang_name)
37
+ refute_empty(matched, "The touched files should include files for #{lang_name}")
38
+ end
39
+
@@ -0,0 +1,38 @@
1
+ When(/^I run it on the command line with the require option (and the debug option )?and a (valid|invalid|missing) ruby file$/) do |need_debug, require_file|
2
+ options = [
3
+ '--require', Dir.pwd + '/' + 'test/fixtures/require/' + require_file + '.rb',
4
+ ]
5
+ if need_debug then
6
+ options << '--debug'
7
+ end
8
+ options << 'test/fixtures/files/clean'
9
+ @run_result = run_check(options)
10
+ end
11
+
12
+ When(/^I run it on the command line with two requires$/) do
13
+ options = [
14
+ '--require', Dir.pwd + '/' + 'test/fixtures/require/valid.rb',
15
+ '--require', Dir.pwd + '/' + 'test/fixtures/require/second.rb',
16
+ ]
17
+ options << 'test/fixtures/files/clean'
18
+ @run_result = run_check(options)
19
+ end
20
+
21
+ Then(/^I should get an error message about the (missing|invalid) require file$/) do |which_require|
22
+ assert_match(Regexp.new("Could not load .+ because it appears to be #{which_require}."), @run_result[:stderr])
23
+ assert_equal(1, @run_result[:stderr].split("\n").length, 'Stderr should be a single line, not a nasty stacktrace')
24
+ assert_empty(@run_result[:stdout], 'Stdout should be silent')
25
+ end
26
+
27
+ Then(/^I should get a stack trace about the invalid require file$/) do
28
+ assert_match(Regexp.new("Could not load .+ because it appears to be invalid."), @run_result[:stderr])
29
+ assert_match(Regexp.new('(Error|Exception)'),@run_result[:stderr], 'Stderr should include a stack trace')
30
+ assert_empty(@run_result[:stdout], 'Stdout should be silent')
31
+ end
32
+
33
+ Then(/^the (valid|first|second) ruby file should have been loaded$/) do |which_require|
34
+ klass_name = which_require == 'first' ? 'valid' : which_require
35
+ klass_name.capitalize!
36
+ klass_name = 'CI::Syntax::Tool::Test::' + klass_name + 'Require'
37
+ assert_class_loaded(klass_name)
38
+ end