loris 0.0.12

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 (69) hide show
  1. data/.gitignore +2 -0
  2. data/README.rdoc +9 -0
  3. data/Rakefile +41 -0
  4. data/TODO +21 -0
  5. data/VERSION +1 -0
  6. data/bin/loris +15 -0
  7. data/cucumber.yml +1 -0
  8. data/examples/self_test/jsl.conf +1 -0
  9. data/examples/self_test/spec/spec.rhino.js +6 -0
  10. data/features/javascript_lint.feature +34 -0
  11. data/features/run.feature +36 -0
  12. data/features/step_definitons/all.rb +69 -0
  13. data/features/support/env.rb +145 -0
  14. data/lib/always_continuer.rb +7 -0
  15. data/lib/browser_finder.rb +23 -0
  16. data/lib/file_actioner.rb +16 -0
  17. data/lib/file_finder.rb +31 -0
  18. data/lib/filters/ends_with_filter.rb +17 -0
  19. data/lib/filters/extension_filter.rb +20 -0
  20. data/lib/filters/file_filter.rb +14 -0
  21. data/lib/filters/modified_filter.rb +21 -0
  22. data/lib/icons/error.png +0 -0
  23. data/lib/icons/failure.png +0 -0
  24. data/lib/icons/info.png +0 -0
  25. data/lib/icons/success.png +0 -0
  26. data/lib/icons/warning.png +0 -0
  27. data/lib/js-test-driver/JsTestDriver-1.1.jar +0 -0
  28. data/lib/js-test-driver/plugins/coverage-1.1.jar +0 -0
  29. data/lib/loris.rb +170 -0
  30. data/lib/outputs/growl_output.rb +26 -0
  31. data/lib/outputs/output_collection.rb +23 -0
  32. data/lib/outputs/shell_output.rb +17 -0
  33. data/lib/outputs/unix_console_clearing_output.rb +13 -0
  34. data/lib/outputs/windows_console_clearing_output.rb +13 -0
  35. data/lib/pinger.rb +23 -0
  36. data/lib/poller.rb +16 -0
  37. data/lib/sleep_waiter.rb +11 -0
  38. data/lib/task_manager.rb +45 -0
  39. data/lib/tasks/command_line_task.rb +28 -0
  40. data/lib/tasks/javascript_lint/javascript_lint_parser.rb +45 -0
  41. data/lib/tasks/javascript_lint/javascript_lint_runner.rb +25 -0
  42. data/lib/tasks/js_test_driver/js_test_driver_config.rb +22 -0
  43. data/lib/tasks/js_test_driver/js_test_driver_parser.rb +28 -0
  44. data/lib/tasks/js_test_driver/js_test_driver_runner.rb +28 -0
  45. data/lib/tasks/js_test_driver/js_test_driver_server.rb +38 -0
  46. data/lib/tasks/jspec/jspec_parser.rb +30 -0
  47. data/lib/tasks/jspec/jspec_runner.rb +25 -0
  48. data/lib/tasks/list_task.rb +34 -0
  49. data/lib/tasks/rspec/rspec_parser.rb +25 -0
  50. data/lib/tasks/rspec/rspec_runner.rb +25 -0
  51. data/lib/unix_process.rb +7 -0
  52. data/lib/windows_process.rb +12 -0
  53. data/loris.gemspec +133 -0
  54. data/loris.tmproj +232 -0
  55. data/spec/file_actioner_spec.rb +41 -0
  56. data/spec/file_finder_spec.rb +73 -0
  57. data/spec/filters/ends_with_filter_spec.rb +26 -0
  58. data/spec/filters/file_filter_spec.rb +28 -0
  59. data/spec/filters/modified_filter_spec.rb +47 -0
  60. data/spec/growl_output_spec.rb +33 -0
  61. data/spec/list_task_spec.rb +58 -0
  62. data/spec/poller_spec.rb +28 -0
  63. data/spec/shell_output_spec.rb +25 -0
  64. data/spec/task_manager_spec.rb +64 -0
  65. data/spec/tasks/javascript_lint/javascript_lint_runner_spec.rb +90 -0
  66. data/spec/tasks/js_test_driver/js_test_driver_runner_spec.rb +92 -0
  67. data/spec/tasks/jspec/jspec_parser_spec.rb +28 -0
  68. data/spec/tasks/jspec/jspec_runner_spec.rb +78 -0
  69. metadata +174 -0
data/lib/loris.rb ADDED
@@ -0,0 +1,170 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'rubygems'
5
+ require 'bind'
6
+ require 'rbconfig'
7
+ require 'find'
8
+ require 'Growl'
9
+ require 'yaml'
10
+ require 'uri'
11
+
12
+
13
+ require 'file_finder'
14
+ require 'poller'
15
+ require 'sleep_waiter'
16
+ require 'always_continuer'
17
+ require 'file_actioner'
18
+ require 'task_manager'
19
+ require 'pinger'
20
+
21
+ require 'filters/extension_filter'
22
+ require 'filters/modified_filter'
23
+ require 'filters/file_filter'
24
+ require 'filters/ends_with_filter'
25
+
26
+ require 'outputs/output_collection'
27
+ require 'outputs/shell_output'
28
+ require 'outputs/windows_console_clearing_output'
29
+ require 'outputs/unix_console_clearing_output'
30
+ require 'outputs/growl_output'
31
+
32
+ require 'tasks/list_task'
33
+ require 'tasks/command_line_task'
34
+ require 'tasks/jspec/jspec_runner'
35
+ require 'tasks/jspec/jspec_parser'
36
+ require 'tasks/javascript_lint/javascript_lint_runner'
37
+ require 'tasks/javascript_lint/javascript_lint_parser'
38
+ require 'tasks/rspec/rspec_runner'
39
+ require 'tasks/rspec/rspec_parser'
40
+
41
+
42
+ include Config
43
+
44
+ class DebugObserver
45
+ def update
46
+ puts '[Poll complete]'
47
+ end
48
+ end
49
+
50
+
51
+ module Loris
52
+ BINARY = File.expand_path(File.dirname(__FILE__) + '/../bin/loris')
53
+ LIBDIR = File.expand_path(File.dirname(__FILE__) + '/../lib')
54
+ RUBY_BINARY = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
55
+
56
+ module CLI
57
+
58
+ class Main
59
+
60
+ class DummyActioner
61
+
62
+ def initialize(actioner, stream)
63
+ @actioner = actioner
64
+ @stream = stream
65
+ end
66
+
67
+ def run
68
+ @actioner.run
69
+ @stream.puts '[Poll complete]'
70
+ @stream.flush
71
+ end
72
+
73
+ end
74
+
75
+ class << self
76
+ def execute(args)
77
+
78
+ # Get config variables
79
+ debug = args.length > 0
80
+ is_windows = RUBY_PLATFORM =~ /mswin32/
81
+ dir = Dir.pwd
82
+ sleep_duration = 1
83
+ browser = is_windows ? 'C:/Program Files/Internet Explorer/iexplore.exe' : 'open'
84
+
85
+ # Create object graph
86
+ w = SleepWaiter.new(sleep_duration)
87
+ c = AlwaysContinuer.new
88
+ ff = FileFinder.new(Find, dir)
89
+ ff.add_filter(FileFilter.new(File))
90
+ ff.add_filter(ModifiedFilter.new(File))
91
+
92
+ cco = is_windows ? WindowsConsoleClearingOutput.new : UnixConsoleClearingOutput.new
93
+
94
+ oc = OutputCollection.new
95
+ oc.add(ShellOutput.new($stdout))
96
+ oc.add(cco) unless debug
97
+ oc.add(GrowlOutput.new(Growl)) unless debug
98
+
99
+ tm = TaskManager.new(oc)
100
+ tm.add(ListTask.new) if debug
101
+ tm.add(CommandLineTask.new(JavascriptLintRunner.new(dir, ExtensionFilter.new(File, 'js')), JavascriptLintParser.new(dir)))
102
+ tm.add(CommandLineTask.new(JSpecRunner.new(dir, ExtensionFilter.new(File, 'js')), JSpecParser.new)) unless is_windows
103
+ tm.add(jsTestDriverTask(dir))
104
+ tm.add(CommandLineTask.new(RSpecRunner.new(dir, ExtensionFilter.new(File, 'rb'), EndsWithFilter.new('_spec.rb')), RSpecParser.new))
105
+
106
+ a = FileActioner.new(ff, tm)
107
+
108
+ da = DummyActioner.new(a, $stdout)
109
+
110
+ p = Poller.new(w, c, debug ? da : a)
111
+
112
+ # Start!
113
+ p.start
114
+
115
+ end
116
+
117
+ # Will need to be refactored into a factory
118
+ def jsTestDriverTask(dir)
119
+ require 'tasks/js_test_driver/js_test_driver_runner'
120
+ require 'tasks/js_test_driver/js_test_driver_parser'
121
+ require 'tasks/js_test_driver/js_test_driver_config'
122
+ require 'tasks/js_test_driver/js_test_driver_server'
123
+
124
+ jar = File.join(LIBDIR, 'js-test-driver/JsTestDriver-1.1.jar')
125
+ is_windows = RUBY_PLATFORM =~ /mswin32/
126
+
127
+ if is_windows
128
+ require 'browser_finder'
129
+ browser = BrowserFinder.new.getDefault
130
+ else
131
+ browser = 'open "%1"'
132
+ end
133
+
134
+ sleep_time = is_windows ? 3 : 2
135
+
136
+ if is_windows
137
+ require 'windows_process'
138
+ else
139
+ require 'unix_process'
140
+ end
141
+
142
+ return CommandLineTask.new(
143
+ JsTestDriverRunner.new(
144
+ dir,
145
+ jar,
146
+ ExtensionFilter.new(File, 'js'),
147
+ JsTestDriverServer.new(
148
+ JsTestDriverConfig.new(
149
+ dir,
150
+ YAML,
151
+ URI
152
+ ),
153
+ Pinger.new,
154
+ is_windows ? WindowsProcess.new : UnixProcess.new,
155
+ jar,
156
+ browser,
157
+ sleep_time
158
+ )
159
+ ),
160
+ JsTestDriverParser.new
161
+ )
162
+ end
163
+
164
+ end
165
+
166
+ end
167
+
168
+ end
169
+
170
+ end
@@ -0,0 +1,26 @@
1
+ class GrowlOutput
2
+
3
+ def initialize(growler)
4
+ @growler = growler
5
+ end
6
+
7
+ def start_run
8
+ end
9
+
10
+ def add_result(result)
11
+ icon = get_icon(result[:state])
12
+
13
+ if @growler.installed?
14
+ @growler.notify {
15
+ self.title = result[:title] + (result[:summary].nil? ? '' : ' - ' + result[:summary])
16
+ self.message = result[:first]
17
+ self.image = icon
18
+ }
19
+ end
20
+ end
21
+
22
+ def get_icon(state)
23
+ return File.join(File.expand_path(File.dirname(__FILE__)), '..', 'icons', "#{state.to_s}.png")
24
+ end
25
+
26
+ end
@@ -0,0 +1,23 @@
1
+ class OutputCollection
2
+
3
+ def initialize
4
+ @outputs = []
5
+ end
6
+
7
+ def start_run
8
+ @outputs.each do |output|
9
+ output.start_run
10
+ end
11
+ end
12
+
13
+ def add(output)
14
+ @outputs << output
15
+ end
16
+
17
+ def add_result(result)
18
+ @outputs.each do |output|
19
+ output.add_result(result)
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,17 @@
1
+ class ShellOutput
2
+
3
+ def initialize(output)
4
+ @output = output
5
+ end
6
+
7
+ def start_run
8
+ end
9
+
10
+ def add_result(result)
11
+ @output.puts result[:title]
12
+ @output.puts result[:state]
13
+ @output.puts result[:summary]
14
+ @output.puts result[:detail]
15
+ end
16
+
17
+ end
@@ -0,0 +1,13 @@
1
+ class UnixConsoleClearingOutput
2
+
3
+ def initialize
4
+ end
5
+
6
+ def start_run
7
+ system 'clear'
8
+ end
9
+
10
+ def add_result(result)
11
+ end
12
+
13
+ end
@@ -0,0 +1,13 @@
1
+ class WindowsConsoleClearingOutput
2
+
3
+ def initialize
4
+ end
5
+
6
+ def start_run
7
+ system 'cls'
8
+ end
9
+
10
+ def add_result(result)
11
+ end
12
+
13
+ end
data/lib/pinger.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'socket'
2
+ require 'timeout'
3
+
4
+ class Pinger
5
+
6
+ def is_port_open?(ip, port)
7
+ begin
8
+ Timeout::timeout(1) do
9
+ begin
10
+ s = TCPSocket.new(ip, port)
11
+ s.close
12
+ return true
13
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
14
+ return false
15
+ end
16
+ end
17
+ rescue Timeout::Error
18
+ end
19
+
20
+ return false
21
+ end
22
+
23
+ end
data/lib/poller.rb ADDED
@@ -0,0 +1,16 @@
1
+ class Poller
2
+
3
+ def initialize(waiter, continuer, action)
4
+ @waiter = waiter
5
+ @continuer = continuer
6
+ @action = action
7
+ end
8
+
9
+ def start
10
+ while @continuer.continue?
11
+ @waiter.wait
12
+ @action.run
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,11 @@
1
+ class SleepWaiter
2
+
3
+ def initialize(sleep_time = 1)
4
+ @sleep_time = sleep_time
5
+ end
6
+
7
+ def wait
8
+ sleep @sleep_time
9
+ end
10
+
11
+ end
@@ -0,0 +1,45 @@
1
+ class TaskManager
2
+
3
+ def initialize(output)
4
+ @output = output
5
+ @tasks = []
6
+ end
7
+
8
+ def add(task)
9
+ @tasks << task
10
+ end
11
+
12
+ def run(files)
13
+ @output.start_run;
14
+
15
+ @tasks.each do |task|
16
+
17
+ begin
18
+ break if !run_task(files, task)
19
+ rescue Exception => ex
20
+ output_exception(ex)
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
27
+ def run_task(files, task)
28
+ result = task.run(files)
29
+ return true if result.nil?
30
+
31
+ @output.add_result(result)
32
+ return !([:error, :failure].include? result[:state])
33
+ end
34
+
35
+ def output_exception(ex)
36
+ @output.add_result({
37
+ :state => :error,
38
+ :title => 'Task',
39
+ :summary => 'Exception',
40
+ :first => ex.message,
41
+ :detail => ex.backtrace
42
+ })
43
+ end
44
+
45
+ end
@@ -0,0 +1,28 @@
1
+ class CommandLineTask
2
+
3
+ def initialize(runner, parser)
4
+ @runner = runner
5
+ @parser = parser
6
+ end
7
+
8
+ def run(files)
9
+ all_files = files[:all]
10
+ modified_files = files[:filtered]
11
+
12
+ return nil if (!@runner.is_configured? all_files)
13
+ return nil if (!@runner.should_run? modified_files)
14
+
15
+ detail = @runner.execute
16
+
17
+ state, summary, first = @parser.parse_result(detail)
18
+
19
+ return {
20
+ :state => state,
21
+ :title => @runner.name,
22
+ :summary => summary,
23
+ :first => first,
24
+ :detail => detail
25
+ }
26
+ end
27
+
28
+ end
@@ -0,0 +1,45 @@
1
+ class JavascriptLintParser
2
+
3
+ def initialize(dir)
4
+ @dir = dir
5
+
6
+ # TODO: Tidy!
7
+ if (RUBY_PLATFORM =~ /mswin32/)
8
+ @dir = @dir.gsub('/', '\\')
9
+ end
10
+ end
11
+
12
+ def parse_result(detail)
13
+ summary_line = detail.grep( /\d+\s+error.*,\s+\d+\s+warning.*/ )[0]
14
+
15
+ if summary_line.nil?
16
+ # error
17
+ error_info = (detail + "\nUnknown Error!").to_a[0].strip
18
+ return :error, 'Error', error_info
19
+ end
20
+
21
+ if summary_line =~ /([1-9]+)\d*\s+error/
22
+ num_failures = $1
23
+ error_info = detail.grep(/\([0-9]+\):([^:]*)Error:/)[0].strip
24
+ return :failure, num_failures + ' Errors', strip_dir(error_info)
25
+ end
26
+
27
+ if summary_line =~ /([1-9]+)\d*\s+warning/
28
+ num_failures = $1
29
+ error_info = detail.grep(/\([0-9]+\)/)[0].strip
30
+ return :warning, num_failures + ' Warnings', strip_dir(error_info)
31
+ end
32
+
33
+ return :success, 'All files are clean', ''
34
+ end
35
+
36
+ def strip_dir(text)
37
+
38
+ # Move to function/class w/ win32 related code
39
+ if (text[0, @dir.length] == @dir)
40
+ text = text[(@dir.length + 1)..-1]
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,25 @@
1
+ class JavascriptLintRunner
2
+
3
+ def initialize(dir, filter)
4
+ @config = dir + '/jsl.conf'
5
+ @dir = dir
6
+ @filter = filter
7
+ end
8
+
9
+ def name
10
+ return 'Javascript Lint'
11
+ end
12
+
13
+ def execute
14
+ return `jsl -conf "#{@config}" -nologo -nofilelisting 2>&1`
15
+ end
16
+
17
+ def is_configured?(all_files)
18
+ return all_files.include?(@config)
19
+ end
20
+
21
+ def should_run?(modified_files)
22
+ return !(modified_files.detect { |file| @filter.filter(file) }).nil? || modified_files.include?(@config)
23
+ end
24
+
25
+ end
@@ -0,0 +1,22 @@
1
+ class JsTestDriverConfig
2
+
3
+ def initialize(dir, yaml, uri)
4
+ @config_file = dir + '/jsTestDriver.conf'
5
+ @yaml = yaml
6
+ @uri = uri
7
+ end
8
+
9
+ def reload
10
+ @conf = @yaml.load_file(@config_file)
11
+ @server = @uri.parse(@conf['server'])
12
+ end
13
+
14
+ def host
15
+ return @server.host
16
+ end
17
+
18
+ def port
19
+ return @server.port
20
+ end
21
+
22
+ end
@@ -0,0 +1,28 @@
1
+ class JsTestDriverParser
2
+
3
+ def parse_result(detail)
4
+ summary_line = detail.grep( /Total \d+ tests/ )[0]
5
+
6
+ if summary_line.nil?
7
+ # error
8
+ error_info = (detail + "\nUnknown Error!").to_a[0].strip
9
+ return :error, 'Error', error_info
10
+ end
11
+
12
+ if summary_line =~ /Errors: ([1-9]+)/
13
+ num_errors = $1
14
+ error_info = detail.grep(/error \([0-9]+.[0-9]+ ms\)/)[0].strip
15
+ return :failure, num_errors + ' Errors', error_info
16
+ end
17
+
18
+ if summary_line =~ /Fails: ([1-9]+)/
19
+ num_failures = $1
20
+ error_info = detail.grep(/failed \([0-9]+.[0-9]+ ms\)/)[0].strip
21
+ return :failure, num_failures + ' Failures', error_info
22
+ end
23
+
24
+ return :success, 'All tests pass', ''
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,28 @@
1
+ class JsTestDriverRunner
2
+
3
+ def initialize(dir, jar, filter, server)
4
+ @config = dir + '/jsTestDriver.conf'
5
+ @dir = dir
6
+ @jar = jar
7
+ @filter = filter
8
+ @server = server
9
+ end
10
+
11
+ def name
12
+ return 'JS Test Driver'
13
+ end
14
+
15
+ def execute
16
+ @server.start_if_required
17
+ return `java -jar "#{@jar}" --config "#{@config}" --tests all --verbose 2>&1`
18
+ end
19
+
20
+ def is_configured?(all_files)
21
+ return all_files.include?(@dir + '/jsTestDriver.conf')
22
+ end
23
+
24
+ def should_run?(modified_files)
25
+ return !(modified_files.detect { |file| @filter.filter(file) }).nil? || modified_files.include?(@config)
26
+ end
27
+
28
+ end
@@ -0,0 +1,38 @@
1
+ class JsTestDriverServer
2
+
3
+ def initialize(config, pinger, process, jar, browser, sleep_time)
4
+ @config = config
5
+ @pinger = pinger
6
+ @process = process
7
+ @jar = jar
8
+ @browser = browser
9
+ @sleep_time = sleep_time
10
+ end
11
+
12
+ def start_if_required
13
+ @config.reload
14
+
15
+ if @config.host == 'localhost' && server_not_running
16
+ start_server(@config.port)
17
+ start_and_capture_browser(@config.port)
18
+ end
19
+ end
20
+
21
+ def server_not_running
22
+ return !@pinger.is_port_open?('127.0.0.1', @config.port)
23
+ end
24
+
25
+ def start_server(port)
26
+ command = "java -jar \"#{@jar}\" --port #{port}" #" --browser \"#{@browser}\" "
27
+ @process.create(command)
28
+ sleep @sleep_time
29
+ end
30
+
31
+ def start_and_capture_browser(port)
32
+ capture_url = "http://localhost:#{port}/capture"
33
+ command = @browser.gsub('%1', capture_url)
34
+ @process.create(command)
35
+ sleep @sleep_time
36
+ end
37
+
38
+ end
@@ -0,0 +1,30 @@
1
+ class JSpecParser
2
+
3
+ def parse_result(detail)
4
+ summary_line = detail.grep( /Passes:/ )[0]
5
+
6
+ if summary_line.nil?
7
+ # error
8
+ error_info = (detail + "\nUnknown Error!").to_a[0]
9
+ return :error, 'Error', error_info
10
+ end
11
+
12
+ # remove console colour information and trim start and end white space
13
+ summary_line = remove_colour(summary_line).strip
14
+
15
+ if summary_line =~ /Failures:\s+([1-9]+)\d*/
16
+ num_failures = $1
17
+ error_line = detail.grep(/\[31m/)[1] || ''
18
+ error_info = remove_colour(error_line).strip
19
+ return :failure, num_failures + ' Failures', error_info
20
+ end
21
+
22
+
23
+ return :success, 'All Passed', summary_line
24
+ end
25
+
26
+ def remove_colour(string)
27
+ return string.gsub(/\e\[[0-9]+m?/, '')
28
+ end
29
+
30
+ end
@@ -0,0 +1,25 @@
1
+ class JSpecRunner
2
+
3
+ def initialize(dir, filter)
4
+ @config = dir + '/spec/spec.rhino.js'
5
+ @dir = dir
6
+ @filter = filter
7
+ end
8
+
9
+ def name
10
+ return 'JSpec'
11
+ end
12
+
13
+ def execute
14
+ return `jspec run --rhino --trace 2>&1`
15
+ end
16
+
17
+ def is_configured?(all_files)
18
+ return all_files.include?(@config)
19
+ end
20
+
21
+ def should_run?(modified_files)
22
+ return !(modified_files.detect { |file| @filter.filter(file) }).nil?
23
+ end
24
+
25
+ end
@@ -0,0 +1,34 @@
1
+ class ListTask
2
+
3
+ def initialize(format_string = "%s")
4
+ @format_string = format_string
5
+ end
6
+
7
+ def run(files)
8
+ all_files = files[:all]
9
+ mofified_files = files[:filtered]
10
+
11
+ return {
12
+ :state => :success,
13
+ :title => 'List',
14
+ :first => mofified_files.length == 1 ? mofified_files[0] : '%s files.' % mofified_files.length,
15
+ :detail => get_detail(mofified_files)
16
+ }
17
+ end
18
+
19
+ def get_detail(paths)
20
+ detail = ""
21
+ limit = [paths.length - 1, 14].min
22
+ (0..limit).each do |i|
23
+ path = paths[i]
24
+ detail += (@format_string % path)
25
+ detail += "\n"
26
+ end
27
+ if limit < paths.length - 1
28
+ detail += " - Plus #{(paths.length - 1) - limit} more files."
29
+ end
30
+
31
+ return detail
32
+ end
33
+
34
+ end