Skip to content
Newer
Older
100644 256 lines (222 sloc) 10.2 KB
4073e9d @gs moved files into folder, added keybindings for osx
gs authored
1 import os
2 import re
3 import thread
4 import subprocess
5 import functools
6 import time
7 import sublime
8 import sublime_plugin
9
10 class AsyncProcess(object):
11 def __init__(self, cmd, listener):
12 self.cmd = cmd
13 self.listener = listener
ba3ad8d @maltize support for stderr lol :)
authored
14 print "DEBUG_EXEC: " + self.cmd
15 self.proc = subprocess.Popen([self.cmd], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
16 if self.proc.stdout:
17 thread.start_new_thread(self.read_stdout, ())
18 if self.proc.stderr:
19 thread.start_new_thread(self.read_stderr, ())
4073e9d @gs moved files into folder, added keybindings for osx
gs authored
20
21 def read_stdout(self):
33fccbb @gs removed try block
gs authored
22 while True:
23 data = os.read(self.proc.stdout.fileno(), 2**15)
24 if data != "":
25 sublime.set_timeout(functools.partial(self.listener.append_data, self.proc, data), 0)
26 else:
27 self.proc.stdout.close()
28 self.listener.is_running = False
29 break
4073e9d @gs moved files into folder, added keybindings for osx
gs authored
30
ba3ad8d @maltize support for stderr lol :)
authored
31 def read_stderr(self):
32 while True:
33 data = os.read(self.proc.stderr.fileno(), 2**15)
34 if data != "":
35 sublime.set_timeout(functools.partial(self.listener.append_data, self.proc, data), 0)
36 else:
37 self.proc.stderr.close()
38 self.listener.is_running = False
39 break
40
4073e9d @gs moved files into folder, added keybindings for osx
gs authored
41 class StatusProcess(object):
42 def __init__(self, msg, listener):
43 self.msg = msg
44 self.listener = listener
45 thread.start_new_thread(self.run_thread, ())
46
47 def run_thread(self):
48 progress = ""
49 while True:
50 if self.listener.is_running:
1299526 @maltize no more smileys on status bar, use dots instead :)
authored
51 if len(progress) >= 10:
4073e9d @gs moved files into folder, added keybindings for osx
gs authored
52 progress = ""
1299526 @maltize no more smileys on status bar, use dots instead :)
authored
53 progress += "."
4073e9d @gs moved files into folder, added keybindings for osx
gs authored
54 sublime.set_timeout(functools.partial(self.listener.update_status, self.msg, progress), 0)
55 time.sleep(1)
56 else:
57 break
58
f40ecb4 @gja Moved all methods to a base ruby class
gja authored
59 class BaseRubyTask(sublime_plugin.WindowCommand):
82e97a1 @maltize make ruby/cucumber/rspec binary configurable
authored
60 def load_config(self):
61 s = sublime.load_settings("RubyTest.sublime-settings")
62 global RUBY_UNIT; RUBY_UNIT = s.get("ruby_unit_exec")
63 global CUCUMBER_UNIT; CUCUMBER_UNIT = s.get("ruby_cucumber_exec")
64 global RSPEC_UNIT; RSPEC_UNIT = s.get("ruby_rspec_exec")
aeeb4ac @maltize manually merged changes from @bradleypriest - Perform the tests from …
authored
65 global RUBY_UNIT_FOLDER; RUBY_UNIT_FOLDER = s.get("ruby_unit_folder")
66 global CUCUMBER_UNIT_FOLDER; CUCUMBER_UNIT_FOLDER = s.get("ruby_cucumber_folder")
67 global RSPEC_UNIT_FOLDER; RSPEC_UNIT_FOLDER = s.get("ruby_rspec_folder")
4073e9d @gs moved files into folder, added keybindings for osx
gs authored
68
1c4abd4 @joneath Added RunLastRubyTest class to run_ruby_test.py. Added menu items and…
joneath authored
69 def save_test_run(self, ex, file_name):
70 s = sublime.load_settings("RubyTest.last-run")
71 s.set("last_test_run", ex)
72 s.set("last_test_file", file_name)
73
74 sublime.save_settings("RubyTest.last-run")
75
4073e9d @gs moved files into folder, added keybindings for osx
gs authored
76 def show_tests_panel(self):
77 if not hasattr(self, 'output_view'):
78 self.output_view = self.window.get_output_panel("tests")
79 self.clear_test_view()
80 self.window.run_command("show_panel", {"panel": "output.tests"})
81
82 def clear_test_view(self):
83 self.output_view.set_read_only(False)
84 edit = self.output_view.begin_edit()
85 self.output_view.erase(edit, sublime.Region(0, self.output_view.size()))
86 self.output_view.end_edit(edit)
87 self.output_view.set_read_only(True)
88
89 def append_data(self, proc, data):
90 str = data.decode("utf-8")
91 str = str.replace('\r\n', '\n').replace('\r', '\n')
92
93 selection_was_at_end = (len(self.output_view.sel()) == 1
94 and self.output_view.sel()[0]
95 == sublime.Region(self.output_view.size()))
96 self.output_view.set_read_only(False)
97 edit = self.output_view.begin_edit()
98 self.output_view.insert(edit, self.output_view.size(), str)
99 if selection_was_at_end:
100 self.output_view.show(self.output_view.size())
101 self.output_view.end_edit(edit)
102 self.output_view.set_read_only(True)
103
56e05cf @gja Verify Syntax uses the polymorphic relationship
gja authored
104 def start_async(self, caption, executable):
105 self.is_running = True
106 self.proc = AsyncProcess(executable, self)
107 StatusProcess(caption, self)
108
4073e9d @gs moved files into folder, added keybindings for osx
gs authored
109 def update_status(self, msg, progress):
110 sublime.status_message(msg + " " + progress)
111
112 def project_path(self, path, command):
836397d support for RSPEC and color syntax
sfistak authored
113 return "cd " + path + " && " + command
114
9e324d8 @gja Replaced if checks with a polymorphic relationship
gja authored
115 class BaseFile:
7a6aace @gja Wrap in CD handled by the file itself
gja authored
116 def __init__(self, file_name): self.folder_name, self.file_name = os.path.split(file_name)
56e05cf @gja Verify Syntax uses the polymorphic relationship
gja authored
117 def verify_syntax_command(self): return None
7a6aace @gja Wrap in CD handled by the file itself
gja authored
118 def wrap_in_cd(self, path, command): return "cd " + path + " && " + command
3ee8b5d @gja Massive performance boost by ignoring the .git folder
gja authored
119 def possible_alternate_files(self): return []
b3b1b2a @gja Cleaning up the run all specs
gja authored
120 def run_all_tests_command(self): return None
65f11a0 @gja Test unit code also pushed deep inside
gja authored
121 def run_from_project_root(self, partition_folder, command, options = ""):
b3b1b2a @gja Cleaning up the run all specs
gja authored
122 folder_name, test_folder, file_name = os.path.join(self.folder_name, self.file_name).partition(partition_folder)
65f11a0 @gja Test unit code also pushed deep inside
gja authored
123 return self.wrap_in_cd(folder_name, command + " " + test_folder + file_name + options)
6e189b5 @gja Cucumber also just passes the line number
gja authored
124 def get_current_line_number(self, view):
125 char_under_cursor = view.sel()[0].a
126 return view.rowcol(char_under_cursor)[0] + 1
9e324d8 @gja Replaced if checks with a polymorphic relationship
gja authored
127
128 class RubyFile(BaseFile):
7a6aace @gja Wrap in CD handled by the file itself
gja authored
129 def verify_syntax_command(self): return self.wrap_in_cd(self.folder_name, "ruby -c " + self.file_name)
3ee8b5d @gja Massive performance boost by ignoring the .git folder
gja authored
130 def possible_alternate_files(self): return [self.file_name.replace(".rb", "_spec.rb"), self.file_name.replace(".rb", "_test.rb"), self.file_name.replace(".rb", ".feature")]
9e324d8 @gja Replaced if checks with a polymorphic relationship
gja authored
131
132 class UnitFile(RubyFile):
3ee8b5d @gja Massive performance boost by ignoring the .git folder
gja authored
133 def possible_alternate_files(self): return [self.file_name.replace("_test.rb", ".rb")]
b3b1b2a @gja Cleaning up the run all specs
gja authored
134 def run_all_tests_command(self): return self.run_from_project_root(RUBY_UNIT_FOLDER, RUBY_UNIT)
65f11a0 @gja Test unit code also pushed deep inside
gja authored
135 def run_single_test_command(self, view):
136 region = view.sel()[0]
137 line_region = view.line(region)
138 text_string = view.substr(sublime.Region(region.begin() - 2000, line_region.end()))
139 text_string = text_string.replace("\n", "\\N")
140 text_string = text_string[::-1]
141 match_obj = re.search('\s?([a-zA-Z_\d]+tset)\s+fed', text_string) # 1st search for 'def test_name'
142 if not match_obj:
143 match_obj = re.search('\s?(\"[a-zA-Z_\s\d]+\"\s+tset)', text_string) # 2nd search for 'test "name"'
144 if not match_obj:
145 sublime.error_message("No test name!")
146 return
147 test_name = match_obj.group(1)[::-1]
148 test_name = test_name.replace("\"", "").replace(" ", "_") # if test name in 2nd format
149 return self.run_from_project_root(RUBY_UNIT_FOLDER, RUBY_UNIT, " -n " + test_name)
b3b1b2a @gja Cleaning up the run all specs
gja authored
150
9e324d8 @gja Replaced if checks with a polymorphic relationship
gja authored
151 class CucumberFile(BaseFile):
3ee8b5d @gja Massive performance boost by ignoring the .git folder
gja authored
152 def possible_alternate_files(self): return [self.file_name.replace(".feature", ".rb")]
b3b1b2a @gja Cleaning up the run all specs
gja authored
153 def run_all_tests_command(self): return self.run_from_project_root(CUCUMBER_UNIT_FOLDER, CUCUMBER_UNIT)
65f11a0 @gja Test unit code also pushed deep inside
gja authored
154 def run_single_test_command(self, view): return self.run_from_project_root(CUCUMBER_UNIT_FOLDER, CUCUMBER_UNIT, " -l " + str(self.get_current_line_number(view)))
9e324d8 @gja Replaced if checks with a polymorphic relationship
gja authored
155
156 class RSpecFile(RubyFile):
3ee8b5d @gja Massive performance boost by ignoring the .git folder
gja authored
157 def possible_alternate_files(self): return [self.file_name.replace("_spec.rb", ".rb")]
b3b1b2a @gja Cleaning up the run all specs
gja authored
158 def run_all_tests_command(self): return self.run_from_project_root(RSPEC_UNIT_FOLDER, RSPEC_UNIT)
65f11a0 @gja Test unit code also pushed deep inside
gja authored
159 def run_single_test_command(self, view): return self.run_from_project_root(RSPEC_UNIT_FOLDER, RSPEC_UNIT, " -l " + str(self.get_current_line_number(view)))
9e324d8 @gja Replaced if checks with a polymorphic relationship
gja authored
160
161 class ErbFile(BaseFile):
7a6aace @gja Wrap in CD handled by the file itself
gja authored
162 def verify_syntax_command(self): return self.wrap_in_cd(self.folder_name, "erb -xT - " + self.file_name + " | ruby -c")
9e324d8 @gja Replaced if checks with a polymorphic relationship
gja authored
163
164 def file_type(self, file_name):
165 if re.search('\w+\_test.rb', file_name):
166 return BaseRubyTask.UnitFile(file_name)
167 elif re.search('\w+\_spec.rb', file_name):
168 return BaseRubyTask.RSpecFile(file_name)
169 elif re.search('\w+\.feature', file_name):
170 return BaseRubyTask.CucumberFile(file_name)
171 elif re.search('\w+\.rb', file_name):
172 return BaseRubyTask.RubyFile(file_name)
173 elif re.search('\w+\.erb', file_name):
174 return BaseRubyTask.ErbFile(file_name)
175 else:
e289178 @gja fixed another typo
gja authored
176 return BaseRubyTask.BaseFile(file_name)
df3ff4b @maltize small refactoring
authored
177
f40ecb4 @gja Moved all methods to a base ruby class
gja authored
178 class RunSingleRubyTest(BaseRubyTask):
836397d support for RSPEC and color syntax
sfistak authored
179
4073e9d @gs moved files into folder, added keybindings for osx
gs authored
180 def run(self):
82e97a1 @maltize make ruby/cucumber/rspec binary configurable
authored
181 self.load_config()
4073e9d @gs moved files into folder, added keybindings for osx
gs authored
182 view = self.window.active_view()
65f11a0 @gja Test unit code also pushed deep inside
gja authored
183 file = self.file_type(view.file_name())
184 command = file.run_single_test_command(view)
185 if command:
186 self.save_test_run(command, file.file_name)
86e8d1a @gs support for running Cucumber test, wip for running single Cucumber test
gs authored
187 self.show_tests_panel()
4073e9d @gs moved files into folder, added keybindings for osx
gs authored
188 self.is_running = True
65f11a0 @gja Test unit code also pushed deep inside
gja authored
189 self.proc = AsyncProcess(command, self)
6100f32 @maltize fixed broken file_name
authored
190 StatusProcess("Starting tests from file " + file.file_name, self)
4073e9d @gs moved files into folder, added keybindings for osx
gs authored
191
f40ecb4 @gja Moved all methods to a base ruby class
gja authored
192 class RunAllRubyTest(BaseRubyTask):
4073e9d @gs moved files into folder, added keybindings for osx
gs authored
193 def run(self):
82e97a1 @maltize make ruby/cucumber/rspec binary configurable
authored
194 self.load_config()
4073e9d @gs moved files into folder, added keybindings for osx
gs authored
195 view = self.window.active_view()
196 folder_name, file_name = os.path.split(view.file_name())
b3b1b2a @gja Cleaning up the run all specs
gja authored
197 file = self.file_type(view.file_name())
198 command = file.run_all_tests_command()
199 if command:
200 self.show_tests_panel()
201 self.save_test_run(command, file_name)
202 self.is_running = True
203 self.proc = AsyncProcess(command, self)
6100f32 @maltize fixed broken file_name
authored
204 StatusProcess("Starting tests from file " + file.file_name, self)
df3ff4b @maltize small refactoring
authored
205 else:
206 sublime.error_message("Only *_test.rb, *_spec.rb, *.feature files supported!")
4073e9d @gs moved files into folder, added keybindings for osx
gs authored
207
208
f40ecb4 @gja Moved all methods to a base ruby class
gja authored
209 class RunLastRubyTest(BaseRubyTask):
1c4abd4 @joneath Added RunLastRubyTest class to run_ruby_test.py. Added menu items and…
joneath authored
210 def load_last_run(self):
211 s = sublime.load_settings("RubyTest.last-run")
212 global LAST_TEST_RUN; LAST_TEST_RUN = s.get("last_test_run")
213 global LAST_TEST_FILE; LAST_TEST_FILE = s.get("last_test_file")
214
215 def run(self):
216 self.load_last_run()
217 self.show_tests_panel()
218 self.is_running = True
219 self.proc = AsyncProcess(LAST_TEST_RUN, self)
220 StatusProcess("Starting tests from file " + LAST_TEST_FILE, self)
221
f40ecb4 @gja Moved all methods to a base ruby class
gja authored
222 class ShowTestPanel(BaseRubyTask):
4073e9d @gs moved files into folder, added keybindings for osx
gs authored
223 def run(self):
224 self.window.run_command("show_panel", {"panel": "output.tests"})
a86077d @gs adding syntax checking for rb and erb files
gs authored
225
f40ecb4 @gja Moved all methods to a base ruby class
gja authored
226 class VerifyRubyFile(BaseRubyTask):
a86077d @gs adding syntax checking for rb and erb files
gs authored
227 def run(self):
228 view = self.window.active_view()
7a6aace @gja Wrap in CD handled by the file itself
gja authored
229 file = self.file_type(view.file_name())
230 command = file.verify_syntax_command()
56e05cf @gja Verify Syntax uses the polymorphic relationship
gja authored
231 if command:
232 self.show_tests_panel()
7a6aace @gja Wrap in CD handled by the file itself
gja authored
233 self.start_async("Checking syntax of : " + file.file_name, command)
a86077d @gs adding syntax checking for rb and erb files
gs authored
234 else:
df3ff4b @maltize small refactoring
authored
235 sublime.error_message("Only .rb or .erb files supported!")
8d34679 @gja Added support for switching between code and spec
gja authored
236
f843f68 @gja fixing typo
gja authored
237 class SwitchBetweenCodeAndTest(BaseRubyTask):
8d34679 @gja Added support for switching between code and spec
gja authored
238 def run(self):
239 _, file_name = os.path.split(self.window.active_view().file_name())
3ee8b5d @gja Massive performance boost by ignoring the .git folder
gja authored
240 possible_alternates = self.file_type(file_name).possible_alternate_files()
241 alternates = self.project_files(lambda(file): file in possible_alternates)
8d34679 @gja Added support for switching between code and spec
gja authored
242 if alternates:
243 self.window.open_file(alternates.pop())
244 else:
245 sublime.error_message("could not find " + str(possible_alternates))
246
31ab725 @gja Ignoring vendor as well
gja authored
247 def walk(self, directory, ignored_directories = []):
248 ignored_directories = ['.git', 'vendor'] # Move this into config
3ee8b5d @gja Massive performance boost by ignoring the .git folder
gja authored
249 for dir, dirnames, files in os.walk(directory):
31ab725 @gja Ignoring vendor as well
gja authored
250 dirnames[:] = [dirname for dirname in dirnames if dirname not in ignored_directories]
3ee8b5d @gja Massive performance boost by ignoring the .git folder
gja authored
251 yield dir, dirnames, files
8d34679 @gja Added support for switching between code and spec
gja authored
252
3ee8b5d @gja Massive performance boost by ignoring the .git folder
gja authored
253 def project_files(self, file_matcher):
8d34679 @gja Added support for switching between code and spec
gja authored
254 directories = self.window.folders()
3ee8b5d @gja Massive performance boost by ignoring the .git folder
gja authored
255 return [os.path.join(dirname, file) for directory in directories for dirname, _, files in self.walk(directory) for file in filter(file_matcher, files)]
Something went wrong with that request. Please try again.