File: rake_task_spec.rb

package info (click to toggle)
ruby-rspec 3.5.0c3e0m0s0-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 6,312 kB
  • ctags: 4,788
  • sloc: ruby: 62,572; sh: 785; makefile: 100
file content (430 lines) | stat: -rw-r--r-- 15,340 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
require "rspec/core/rake_task"
require 'tempfile'

module RSpec::Core
  RSpec.describe RakeTask do
    let(:task) { RakeTask.new }

    def ruby
      FileUtils::RUBY
    end

    def spec_command
      task.__send__(:spec_command)
    end

    context "with a name passed to the constructor" do
      let(:task) { RakeTask.new(:task_name) }

      it "correctly sets the name" do
        expect(task.name).to eq :task_name
      end
    end

    context "with args passed to the rake task" do
      it "correctly passes along task arguments" do
        the_task = RakeTask.new(:rake_task_args, :files) do |t, args|
          expect(args[:files]).to eq "first_spec.rb"
        end

        expect(the_task).to receive(:run_task) { true }
        expect(Rake.application.invoke_task("rake_task_args[first_spec.rb]")).to be_truthy
      end
    end

    context "default" do
      it "renders rspec" do
        expect(spec_command).to match(/^#{ruby} '?#{task.rspec_path}'?/)
      end
    end

    context "with space", :unless => RSpec::Support::OS.windows? do
      it "renders rspec with space escaped" do
        task.rspec_path = '/path with space/exe/rspec'
        expect(spec_command).to match(/^#{ruby} \/path\\ with\\ space\/exe\/rspec/)
      end
    end

    context "on windows, with a quote in the name", :if => RSpec::Support::OS.windows? do
      it "renders rspec quoted, with quote escaped" do
        task.rspec_path = "/foo'bar/exe/rspec"
        expect(spec_command).to include(%q|'/foo\'bar/exe/rspec'|)
      end
    end

    context "with ruby options" do
      it "renders them before the rspec path" do
        task.ruby_opts = "-w"
        expect(spec_command).to match(/^#{ruby} -w '?#{task.rspec_path}'?/)
      end
    end

    context "with rspec_opts" do
      it "adds the rspec_opts" do
        task.rspec_opts = "-Ifoo"
        expect(spec_command).to match(/#{task.rspec_path}.*-Ifoo/)
      end

      it 'correctly excludes the default pattern if rspec_opts includes --pattern' do
        task.rspec_opts = "--pattern some_specs"
        expect(spec_command).to include("--pattern some_specs").and(
          exclude(RSpec::Core::RakeTask::DEFAULT_PATTERN)
        )
      end
    end

    context "with pattern" do
      it "adds the pattern" do
        task.pattern = "complex_pattern"
        expect(spec_command).to match(/ --pattern '?complex_pattern'?/)
      end

      it "shellescapes the pattern as necessary", :unless => RSpec::Support::OS.windows? do
        task.pattern = "foo'bar"
        expect(spec_command).to include(" --pattern foo\\'bar")
      end
    end

    context "when `failure_message` is configured" do
      before do
        allow(task).to receive(:exit)
        task.failure_message = "Bad news"
      end

      it 'prints it if the RSpec run failed' do
        task.ruby_opts = '-e "exit(1);" ;#'
        expect { task.run_task false }.to output(/Bad news/).to_stdout
      end

      it 'does not print it if the RSpec run succeeded' do
        task.ruby_opts = '-e "exit(0);" ;#'
        expect { task.run_task false }.not_to output(/Bad/).to_stdout
      end
    end

    context 'with custom exit status' do
      def silence_output(&block)
        expect(&block).to output(anything).to_stdout.and output(anything).to_stderr
      end

      it 'returns the correct status on exit', :slow do
        expect(task).to receive(:exit).with(2)

        silence_output do
          task.ruby_opts = '-e "exit(2);" ;#'
          task.run_task true
        end
      end
    end

    context 'with verbose enabled' do
      it 'prints the command only to stdout for passing specs', :slow do
        expect {
          task.ruby_opts = '-e ""'
          task.run_task true
        }.to output(/-e ""/).to_stdout.and avoid_outputting.to_stderr
      end

      it 'prints an additional message to stderr for failures', :slow do
        allow(task).to receive(:exit)

        expect {
          task.ruby_opts = '-e "exit(1);" ;#'
          task.run_task true
        }.to output(/-e "exit\(1\);" ;#/).to_stdout.and output(/-e "exit\(1\);".* failed/).to_stderr
      end
    end

    context 'with verbose disabled' do
      it 'does not print to stdout or stderr', :slow do
        allow(task).to receive(:exit)

        expect {
          task.ruby_opts = '-e "exit(1);" ;#'
          task.run_task false
        }.to avoid_outputting.to_stdout.and avoid_outputting.to_stderr
      end
    end

    def loaded_files
      args = Shellwords.split(spec_command)
      args -= [task.class::RUBY, "-S", task.rspec_path]
      config = Configuration.new
      config_options = ConfigurationOptions.new(args)
      config_options.configure(config)
      config.files_to_run
    end

    def specify_consistent_ordering_of_files_to_run(pattern, file_searcher)
      orderings = [
        %w[ a/1.rb a/2.rb a/3.rb ],
        %w[ a/2.rb a/1.rb a/3.rb ],
        %w[ a/3.rb a/2.rb a/1.rb ]
      ].map do |files|
        allow(file_searcher).to receive(:[]).with(anything).and_call_original
        expect(file_searcher).to receive(:[]).with(a_string_including pattern) { files }
        loaded_files
      end

      expect(orderings.uniq.size).to eq(1)
    end

    context "with SPEC env var set" do
      it "sets files to run" do
        with_env_vars 'SPEC' => 'path/to/file' do
          expect(loaded_files).to contain_files("path/to/file")
        end
      end

      it "sets the files to run in a consistent order, regardless of the underlying FileList ordering" do
        with_env_vars 'SPEC' => 'a/*.rb' do
          specify_consistent_ordering_of_files_to_run('a/*.rb', FileList)
        end
      end
    end

    describe "load path manipulation" do
      def self.it_configures_rspec_load_path(description, path_template)
        context "when rspec is installed as #{description}" do
          # Matchers are lazily loaded via `autoload`, so we need to get the matcher before
          # the load path is manipulated, so we're using `let!` here to do that.
          let!(:include_expected_load_path_option) do
            match(/ -I'?#{path_template % "rspec-core"}'?#{File::PATH_SEPARATOR}'?#{path_template % "rspec-support"}'? /)
          end

          it "adds the current rspec-core and rspec-support dirs to the load path to ensure the current version is used" do
            $LOAD_PATH.replace([
              path_template % "rspec-core",
              path_template % "rspec-support",
              path_template % "rspec-expectations",
              path_template % "rspec-mocks",
              path_template % "rake"
            ])

            expect(spec_command).to include_expected_load_path_option
          end

          it "avoids adding the same load path entries twice" do
            $LOAD_PATH.replace([
              path_template % "rspec-core",
              path_template % "rspec-support",
              path_template % "rspec-core",
              path_template % "rspec-support"
            ])

            expect(spec_command).to include_expected_load_path_option
          end
        end
      end

      it_configures_rspec_load_path "bundler :git dependencies",
        "/Users/myron/code/some-gem/bundle/ruby/2.1.0/bundler/gems/%s-8d2e4e570994/lib"

      it_configures_rspec_load_path "bundler :path dependencies",
        "/Users/myron/code/rspec-dev/repos/%s/lib"

      it_configures_rspec_load_path "a rubygem",
        "/Users/myron/.gem/ruby/1.9.3/gems/%s-3.1.0.beta1/lib"

      it "does not include extra load path entries for other gems that have `rspec-core` in its path" do
        # matchers are lazily loaded with autoload, so we need to get the matcher before manipulating the load path.
        include_extra_load_path_entries = include("simplecov", "minitest", "rspec-core/spec")

        # these are items on my load path due to `bundle install --standalone`,
        # and my initial logic caused all these to be included in the `-I` option.
        $LOAD_PATH.replace([
           "/Users/user/code/rspec-dev/repos/rspec-core/spec",
           "/Users/user/code/rspec-dev/repos/rspec-core/bundle/ruby/1.9.1/gems/simplecov-0.8.2/lib",
           "/Users/user/code/rspec-dev/repos/rspec-core/bundle/ruby/1.9.1/gems/simplecov-html-0.8.0/lib",
           "/Users/user/code/rspec-dev/repos/rspec-core/bundle/ruby/1.9.1/gems/minitest-5.3.3/lib",
           "/Users/user/code/rspec-dev/repos/rspec/lib",
           "/Users/user/code/rspec-dev/repos/rspec-mocks/lib",
           "/Users/user/code/rspec-dev/repos/rspec-core/lib",
           "/Users/user/code/rspec-dev/repos/rspec-expectations/lib",
           "/Users/user/code/rspec-dev/repos/rspec-support/lib",
           "/Users/user/code/rspec-dev/repos/rspec-core/bundle",
        ])

        expect(spec_command).not_to include_extra_load_path_entries
      end
    end if false

    it "sets the files to run in a consistent order, regardless of the underlying FileList ordering" do
      task.pattern = 'a/*.rb'
      specify_consistent_ordering_of_files_to_run('a/*.rb', Dir)
    end

    context "with a pattern value" do
      context "that matches no files" do
        it "runs nothing" do
          task.pattern = 'a/*.no_match'
          expect(loaded_files).to eq([])
        end
      end

      context "that is an existing directory, not a file glob" do
        it "loads the spec files in that directory" do
          task.pattern = "./spec/rspec/core/resources/acceptance"
          expect(loaded_files).to contain_files("./spec/rspec/core/resources/acceptance/foo_spec.rb")
        end
      end

      context "that is an existing file, not a file glob" do
        it "loads the spec file" do
          task.pattern = "./spec/rspec/core/resources/acceptance/foo_spec.rb"
          expect(loaded_files).to contain_files("./spec/rspec/core/resources/acceptance/foo_spec.rb")
        end
      end

      context "that is an absolute path file glob" do
        it "loads the matching spec files", :failing_on_appveyor,
        :pending => false,
        :skip => (ENV['APPVEYOR'] ? "Failing on AppVeyor but :pending isn't working for some reason" : false) do
          dir = File.expand_path("../resources", __FILE__)
          task.pattern = File.join(dir, "**/*_spec.rb")

          expect(loaded_files).to contain_files(
            "./spec/rspec/core/resources/acceptance/foo_spec.rb",
            "./spec/rspec/core/resources/a_spec.rb"
          )
        end
      end

      context "that is a relative file glob, for a path not under the default spec dir (`spec`)" do
        it "loads the matching spec files" do
          Dir.chdir("./spec/rspec/core") do
            task.pattern = "resources/**/*_spec.rb"

            expect(loaded_files).to contain_files(
              "resources/acceptance/foo_spec.rb",
              "resources/a_spec.rb"
            )
          end
        end
      end

      context "that is an array of existing files or directories, not a file glob" do
        it "loads the specified spec files, and spec files from the specified directories" do
          task.pattern = ["./spec/rspec/core/resources/acceptance",
                          "./spec/rspec/core/resources/a_bar.rb"]

          expect(loaded_files).to contain_files(
            "./spec/rspec/core/resources/acceptance/foo_spec.rb",
            "./spec/rspec/core/resources/a_bar.rb"
          )
        end
      end

      # https://github.com/rspec/rspec-core/issues/1695
      context "that is a single glob that starts with ./" do
        it "loads the spec files that match the glob" do
          task.pattern = "./spec/rspec/core/resources/acceptance/**/*_spec.rb"
          expect(loaded_files).to contain_files("./spec/rspec/core/resources/acceptance/foo_spec.rb")
        end
      end

      context "that is an array of globs relative to the current working dir" do
        it "loads spec files that match any of the globs" do
          task.pattern = ["./spec/rspec/core/resources/acceptance/*_spec.rb",
                          "./spec/rspec/core/resources/*_bar.rb"]

          expect(loaded_files).to contain_files(
            "./spec/rspec/core/resources/acceptance/foo_spec.rb",
            "./spec/rspec/core/resources/a_bar.rb"
          )
        end
      end

      context "that is a mixture of file globs and individual files or dirs" do
        it "loads all specified or matching files" do
          task.pattern = ["./spec/rspec/core/resources/acceptance/*_spec.rb",
                          "./spec/rspec/core/resources/a_bar.rb"]

          expect(loaded_files).to contain_files(
            "./spec/rspec/core/resources/acceptance/foo_spec.rb",
            "./spec/rspec/core/resources/a_bar.rb"
          )
        end
      end

      context "that is a FileList" do
        it "loads the files from the FileList" do
          task.pattern = FileList["spec/rspec/core/resources/**/*_spec.rb"]

          expect(loaded_files).to contain_files(
            "spec/rspec/core/resources/a_spec.rb",
            "spec/rspec/core/resources/acceptance/foo_spec.rb"
          )
        end
      end
    end

    context "without an exclude_pattern" do
      it 'does not pass the --exclude-pattern option' do
        expect(spec_command).not_to include("exclude")
      end
    end

    context "with an exclude_pattern" do
      include_context "isolated directory"

      def make_file(dir, name)
        File.join("spec", dir, name).tap { |f| FileUtils.touch(f) }
      end

      def make_files_in_dir(dir)
        %w[ foo_spec.rb bar_spec.rb ].map do |file_name|
          make_file(dir, file_name)
        end
      end

      before do
        spec_dir = File.join(Dir.getwd, "spec")
        task.exclude_pattern = "spec/acceptance/*_spec.rb"

        FileUtils.mkdir_p(File.join(spec_dir, "acceptance"))
        FileUtils.mkdir_p(File.join(spec_dir, "unit"))

        make_files_in_dir "acceptance"
      end

      it "shellescapes the pattern as necessary", :unless => RSpec::Support::OS.windows? do
        task.exclude_pattern = "foo'bar"
        expect(spec_command).to include(" --exclude-pattern foo\\'bar")
      end

      it "it does not load matching files" do
        task.pattern = "spec/**/*_spec.rb"
        unit_files = make_files_in_dir "unit"

        expect(loaded_files).to contain_files(*unit_files)
      end

      it "excludes files when pattern and exclusion_pattern don't consistently start with ./" do
        task.exclude_pattern = "./spec/acceptance/*_spec.rb"
        task.pattern = "spec/**/*_spec.rb"
        unit_files = make_files_in_dir "unit"

        expect(loaded_files).to contain_files(*unit_files)
      end
    end

    context "with paths with quotes or spaces" do
      include_context "isolated directory"

      it "matches files with quotes and spaces", :failing_on_appveyor do
        spec_dir = File.join(Dir.getwd, "spec")
        task.pattern = "spec/*spec.rb"
        FileUtils.mkdir_p(spec_dir)

        files = ["first_spec.rb", "second_\"spec.rb", "third_\'spec.rb", "fourth spec.rb"].map do |file_name|
          File.join("spec", file_name).tap { |f| FileUtils.touch(f) }
        end

        expect(loaded_files).to contain_files(*files)
      end
    end

    it_behaves_like "handling symlinked directories when loading spec files"
  end
end