rspec-support 3.0.4 → 3.12.1

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 (41) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/Changelog.md +322 -0
  4. data/{LICENSE.txt → LICENSE.md} +3 -2
  5. data/README.md +29 -6
  6. data/lib/rspec/support/caller_filter.rb +35 -16
  7. data/lib/rspec/support/comparable_version.rb +46 -0
  8. data/lib/rspec/support/differ.rb +51 -41
  9. data/lib/rspec/support/directory_maker.rb +63 -0
  10. data/lib/rspec/support/encoded_string.rb +110 -15
  11. data/lib/rspec/support/fuzzy_matcher.rb +5 -6
  12. data/lib/rspec/support/hunk_generator.rb +0 -1
  13. data/lib/rspec/support/matcher_definition.rb +42 -0
  14. data/lib/rspec/support/method_signature_verifier.rb +287 -54
  15. data/lib/rspec/support/mutex.rb +73 -0
  16. data/lib/rspec/support/object_formatter.rb +275 -0
  17. data/lib/rspec/support/recursive_const_methods.rb +76 -0
  18. data/lib/rspec/support/reentrant_mutex.rb +78 -0
  19. data/lib/rspec/support/ruby_features.rb +177 -14
  20. data/lib/rspec/support/source/location.rb +21 -0
  21. data/lib/rspec/support/source/node.rb +110 -0
  22. data/lib/rspec/support/source/token.rb +94 -0
  23. data/lib/rspec/support/source.rb +85 -0
  24. data/lib/rspec/support/spec/deprecation_helpers.rb +19 -32
  25. data/lib/rspec/support/spec/diff_helpers.rb +31 -0
  26. data/lib/rspec/support/spec/in_sub_process.rb +43 -16
  27. data/lib/rspec/support/spec/library_wide_checks.rb +150 -0
  28. data/lib/rspec/support/spec/shell_out.rb +108 -0
  29. data/lib/rspec/support/spec/stderr_splitter.rb +31 -9
  30. data/lib/rspec/support/spec/string_matcher.rb +45 -0
  31. data/lib/rspec/support/spec/with_isolated_directory.rb +13 -0
  32. data/lib/rspec/support/spec/with_isolated_stderr.rb +0 -2
  33. data/lib/rspec/support/spec.rb +46 -26
  34. data/lib/rspec/support/version.rb +1 -1
  35. data/lib/rspec/support/warnings.rb +6 -6
  36. data/lib/rspec/support/with_keywords_when_needed.rb +33 -0
  37. data/lib/rspec/support.rb +87 -3
  38. data.tar.gz.sig +0 -0
  39. metadata +70 -52
  40. metadata.gz.sig +0 -0
  41. data/lib/rspec/support/version_checker.rb +0 -53
@@ -0,0 +1,108 @@
1
+ require 'open3'
2
+ require 'rake/file_utils'
3
+ require 'shellwords'
4
+
5
+ module RSpec
6
+ module Support
7
+ module ShellOut
8
+ def with_env(vars)
9
+ original = ENV.to_hash
10
+ vars.each { |k, v| ENV[k] = v }
11
+
12
+ begin
13
+ yield
14
+ ensure
15
+ ENV.replace(original)
16
+ end
17
+ end
18
+
19
+ if Open3.respond_to?(:capture3) # 1.9+
20
+ def shell_out(*command)
21
+ stdout, stderr, status = Open3.capture3(*command)
22
+ return stdout, filter(stderr), status
23
+ end
24
+ else # 1.8.7
25
+ # popen3 doesn't provide the exit status so we fake it out.
26
+ FakeProcessStatus = Struct.new(:exitstatus)
27
+
28
+ def shell_out(*command)
29
+ stdout = stderr = nil
30
+
31
+ Open3.popen3(*command) do |_in, out, err|
32
+ stdout = out.read
33
+ stderr = err.read
34
+ end
35
+
36
+ status = FakeProcessStatus.new(0)
37
+ return stdout, filter(stderr), status
38
+ end
39
+ end
40
+
41
+ def run_ruby_with_current_load_path(ruby_command, *flags)
42
+ command = [
43
+ FileUtils::RUBY,
44
+ "-I#{$LOAD_PATH.map(&:shellescape).join(File::PATH_SEPARATOR)}",
45
+ "-e", ruby_command, *flags
46
+ ]
47
+
48
+ # Unset these env vars because `ruby -w` will issue warnings whenever
49
+ # they are set to non-default values.
50
+ with_env 'RUBY_GC_HEAP_FREE_SLOTS' => nil, 'RUBY_GC_MALLOC_LIMIT' => nil,
51
+ 'RUBY_FREE_MIN' => nil do
52
+ shell_out(*command)
53
+ end
54
+ end
55
+
56
+ LINES_TO_IGNORE =
57
+ [
58
+ # Ignore bundler warning.
59
+ %r{bundler/source/rubygems},
60
+ # Ignore bundler + rubygems warning.
61
+ %r{site_ruby/\d\.\d\.\d/rubygems},
62
+ %r{jruby-\d\.\d\.\d+\.\d/lib/ruby/stdlib/rubygems},
63
+ # This is required for windows for some reason
64
+ %r{lib/bundler/rubygems},
65
+ # This is a JRuby file that generates warnings on 9.0.3.0
66
+ %r{lib/ruby/stdlib/jar},
67
+ # This is a JRuby file that generates warnings on 9.1.7.0
68
+ %r{org/jruby/RubyKernel\.java},
69
+ # This is a JRuby gem that generates warnings on 9.1.7.0
70
+ %r{ffi-1\.13\.\d+-java},
71
+ %r{uninitialized constant FFI},
72
+ # These are related to the above, there is a warning about io from FFI
73
+ %r{jruby-\d\.\d\.\d+\.\d/lib/ruby/stdlib/io},
74
+ %r{io/console on JRuby shells out to stty for most operations},
75
+ # This is a JRuby 9.1.17.0 error on Github Actions
76
+ %r{io/console not supported; tty will not be manipulated},
77
+ # This is a JRuby 9.2.1.x error
78
+ %r{jruby/kernel/gem_prelude},
79
+ %r{lib/jruby\.jar!/jruby/preludes},
80
+ # Ignore some JRuby errors for gems
81
+ %r{jruby/\d\.\d(\.\d)?/gems/aruba},
82
+ %r{jruby/\d\.\d(\.\d)?/gems/ffi},
83
+ ]
84
+
85
+ def strip_known_warnings(input)
86
+ input.split("\n").reject do |l|
87
+ LINES_TO_IGNORE.any? { |to_ignore| l =~ to_ignore } ||
88
+ # Remove blank lines
89
+ l == "" || l.nil?
90
+ end.join("\n")
91
+ end
92
+
93
+ private
94
+
95
+ if Ruby.jruby?
96
+ def filter(output)
97
+ output.each_line.reject do |line|
98
+ line.include?("lib/ruby/shared/rubygems")
99
+ end.join($/)
100
+ end
101
+ else
102
+ def filter(output)
103
+ output
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -6,15 +6,16 @@ module RSpec
6
6
  def initialize(original)
7
7
  @orig_stderr = original
8
8
  @output_tracker = ::StringIO.new
9
+ @last_line = nil
9
10
  end
10
11
 
11
12
  respond_to_name = (::RUBY_VERSION.to_f < 1.9) ? :respond_to? : :respond_to_missing?
12
13
  define_method respond_to_name do |*args|
13
- @orig_stderr.respond_to?(*args) || super
14
+ @orig_stderr.respond_to?(*args) || super(*args)
14
15
  end
15
16
 
16
17
  def method_missing(name, *args, &block)
17
- @output_tracker.__send__(name, *args, &block)
18
+ @output_tracker.__send__(name, *args, &block) if @output_tracker.respond_to?(name)
18
19
  @orig_stderr.__send__(name, *args, &block)
19
20
  end
20
21
 
@@ -22,13 +23,35 @@ module RSpec
22
23
  @orig_stderr == other
23
24
  end
24
25
 
26
+ def reopen(*args)
27
+ reset!
28
+ @orig_stderr.reopen(*args)
29
+ end
30
+
31
+ # To work around JRuby error:
32
+ # can't convert RSpec::Support::StdErrSplitter into String
33
+ def to_io
34
+ @orig_stderr.to_io
35
+ end
36
+
25
37
  # To work around JRuby error:
26
38
  # TypeError: $stderr must have write method, RSpec::StdErrSplitter given
27
39
  def write(line)
28
- if line !~ %r{^\S+/gems/\S+:\d+: warning:} # http://rubular.com/r/kqeUIZOfPG
29
- @orig_stderr.write(line)
30
- @output_tracker.write(line)
31
- end
40
+ return if line =~ %r{^\S+/gems/\S+:\d+: warning:} # http://rubular.com/r/kqeUIZOfPG
41
+
42
+ # Ruby 2.7.0 warnings from keyword arguments span multiple lines, extend check above
43
+ # to look for the next line.
44
+ return if @last_line =~ %r{^\S+/gems/\S+:\d+: warning:} &&
45
+ line =~ %r{warning: The called method .* is defined here}
46
+
47
+ # Ruby 2.7.0 complains about hashes used in place of keyword arguments
48
+ # Aruba 0.14.2 uses this internally triggering that here
49
+ return if line =~ %r{lib/ruby/2\.7\.0/fileutils\.rb:622: warning:}
50
+
51
+ @orig_stderr.write(line)
52
+ @output_tracker.write(line)
53
+ ensure
54
+ @last_line = line
32
55
  end
33
56
 
34
57
  def has_output?
@@ -39,15 +62,14 @@ module RSpec
39
62
  @output_tracker = ::StringIO.new
40
63
  end
41
64
 
42
- def verify_example!(example)
43
- example.send(:fail,"Warnings were generated: #{output}") if has_output?
65
+ def verify_no_warnings!
66
+ raise "Warnings were generated: #{output}" if has_output?
44
67
  reset!
45
68
  end
46
69
 
47
70
  def output
48
71
  @output_tracker.string
49
72
  end
50
-
51
73
  end
52
74
  end
53
75
  end
@@ -0,0 +1,45 @@
1
+ require 'rspec/matchers'
2
+ # Special matcher for comparing encoded strings so that
3
+ # we don't run any expectation failures through the Differ,
4
+ # which also relies on EncodedString. Instead, confirm the
5
+ # strings have the same bytes.
6
+ RSpec::Matchers.define :be_identical_string do |expected|
7
+ if String.method_defined?(:encoding)
8
+ match do
9
+ expected_encoding? &&
10
+ actual.bytes.to_a == expected.bytes.to_a
11
+ end
12
+
13
+ failure_message do
14
+ "expected\n#{actual.inspect} (#{actual.encoding.name}) to be identical to\n"\
15
+ "#{expected.inspect} (#{expected.encoding.name})\n"\
16
+ "The exact bytes are printed below for more detail:\n"\
17
+ "#{actual.bytes.to_a}\n"\
18
+ "#{expected.bytes.to_a}\n"\
19
+ end
20
+
21
+ # Depends on chaining :with_same_encoding for it to
22
+ # check for string encoding.
23
+ def expected_encoding?
24
+ if defined?(@expect_same_encoding) && @expect_same_encoding
25
+ actual.encoding == expected.encoding
26
+ else
27
+ true
28
+ end
29
+ end
30
+ else
31
+ match do
32
+ actual.split(//) == expected.split(//)
33
+ end
34
+
35
+ failure_message do
36
+ "expected\n#{actual.inspect} to be identical to\n#{expected.inspect}\n"
37
+ end
38
+ end
39
+
40
+ chain :with_same_encoding do
41
+ @expect_same_encoding ||= true
42
+ end
43
+ end
44
+ RSpec::Matchers.alias_matcher :a_string_identical_to, :be_identical_string
45
+ RSpec::Matchers.alias_matcher :be_diffed_as, :be_identical_string
@@ -0,0 +1,13 @@
1
+ require 'tmpdir'
2
+
3
+ RSpec.shared_context "isolated directory" do
4
+ around do |ex|
5
+ Dir.mktmpdir do |tmp_dir|
6
+ Dir.chdir(tmp_dir, &ex)
7
+ end
8
+ end
9
+ end
10
+
11
+ RSpec.configure do |c|
12
+ c.include_context "isolated directory", :isolated_directory => true
13
+ end
@@ -1,7 +1,6 @@
1
1
  module RSpec
2
2
  module Support
3
3
  module WithIsolatedStdErr
4
-
5
4
  def with_isolated_stderr
6
5
  original = $stderr
7
6
  $stderr = StringIO.new
@@ -9,7 +8,6 @@ module RSpec
9
8
  ensure
10
9
  $stderr = original
11
10
  end
12
-
13
11
  end
14
12
  end
15
13
  end
@@ -1,8 +1,13 @@
1
1
  require 'rspec/support'
2
+ require 'rspec/support/spec/in_sub_process'
3
+
2
4
  RSpec::Support.require_rspec_support "spec/deprecation_helpers"
5
+ RSpec::Support.require_rspec_support "spec/diff_helpers"
3
6
  RSpec::Support.require_rspec_support "spec/with_isolated_stderr"
4
7
  RSpec::Support.require_rspec_support "spec/stderr_splitter"
5
8
  RSpec::Support.require_rspec_support "spec/formatting_support"
9
+ RSpec::Support.require_rspec_support "spec/with_isolated_directory"
10
+ RSpec::Support.require_rspec_support "ruby_features"
6
11
 
7
12
  warning_preventer = $stderr = RSpec::Support::StdErrSplitter.new($stderr)
8
13
 
@@ -10,14 +15,15 @@ RSpec.configure do |c|
10
15
  c.include RSpecHelpers
11
16
  c.include RSpec::Support::WithIsolatedStdErr
12
17
  c.include RSpec::Support::FormattingSupport
18
+ c.include RSpec::Support::InSubProcess
13
19
 
14
20
  unless defined?(Debugger) # debugger causes warnings when used
15
21
  c.before do
16
22
  warning_preventer.reset!
17
23
  end
18
24
 
19
- c.after do |example|
20
- warning_preventer.verify_example!(example)
25
+ c.after do
26
+ warning_preventer.verify_no_warnings!
21
27
  end
22
28
  end
23
29
 
@@ -26,37 +32,51 @@ RSpec.configure do |c|
26
32
  c.default_formatter = 'doc'
27
33
  end
28
34
 
29
- c.filter_run :focus
30
- c.run_all_when_everything_filtered = true
31
- end
35
+ c.filter_run_when_matching :focus
32
36
 
33
- module RSpec::Support::Spec
34
- def self.setup_simplecov(&block)
35
- # Simplecov emits some ruby warnings when loaded, so silence them.
36
- old_verbose, $VERBOSE = $VERBOSE, false
37
+ c.example_status_persistence_file_path = "./spec/examples.txt"
38
+
39
+ c.define_derived_metadata :failing_on_windows_ci do |meta|
40
+ meta[:pending] ||= "This spec fails on Windows CI and needs someone to fix it."
41
+ end if RSpec::Support::OS.windows? && ENV['CI']
42
+ end
37
43
 
38
- return if ENV['NO_COVERAGE'] || RUBY_VERSION < '1.9.3' || RUBY_ENGINE != 'ruby'
44
+ module RSpec
45
+ module Support
46
+ module Spec
47
+ def self.setup_simplecov(&block)
48
+ # Simplecov emits some ruby warnings when loaded, so silence them.
49
+ old_verbose, $VERBOSE = $VERBOSE, false
39
50
 
40
- # Don't load it when we're running a single isolated
41
- # test file rather than the whole suite.
42
- return if RSpec.configuration.files_to_run.one?
51
+ return if ENV['NO_COVERAGE'] || RUBY_VERSION < '1.9.3'
52
+ return if RUBY_ENGINE != 'ruby' || RSpec::Support::OS.windows?
43
53
 
44
- require 'simplecov'
54
+ # Don't load it when we're running a single isolated
55
+ # test file rather than the whole suite.
56
+ return if RSpec.configuration.files_to_run.one?
45
57
 
46
- SimpleCov.start do
47
- add_filter "./bundle/"
48
- add_filter "./tmp/"
49
- add_filter do |source_file|
50
- # Filter out `spec` directory except when it is under `lib`
51
- # (as is the case in rspec-support)
52
- source_file.filename.include?('/spec/') && !source_file.filename.include?('/lib/')
58
+ require 'simplecov'
59
+ start_simplecov(&block)
60
+ rescue LoadError
61
+ warn "Simplecov could not be loaded"
62
+ ensure
63
+ $VERBOSE = old_verbose
53
64
  end
54
65
 
55
- instance_eval(&block) if block
66
+ def self.start_simplecov(&block)
67
+ SimpleCov.start do
68
+ add_filter "bundle/"
69
+ add_filter "tmp/"
70
+ add_filter do |source_file|
71
+ # Filter out `spec` directory except when it is under `lib`
72
+ # (as is the case in rspec-support)
73
+ source_file.filename.include?('/spec/') && !source_file.filename.include?('/lib/')
74
+ end
75
+
76
+ instance_eval(&block) if block
77
+ end
78
+ end
79
+ private_class_method :start_simplecov
56
80
  end
57
- rescue LoadError
58
- ensure
59
- $VERBOSE = old_verbose
60
81
  end
61
82
  end
62
-
@@ -1,7 +1,7 @@
1
1
  module RSpec
2
2
  module Support
3
3
  module Version
4
- STRING = '3.0.4'
4
+ STRING = '3.12.1'
5
5
  end
6
6
  end
7
7
  end
@@ -4,7 +4,7 @@ RSpec::Support.require_rspec_support "caller_filter"
4
4
  module RSpec
5
5
  module Support
6
6
  module Warnings
7
- def deprecate(deprecated, options = {})
7
+ def deprecate(deprecated, options={})
8
8
  warn_with "DEPRECATION: #{deprecated} is deprecated.", options
9
9
  end
10
10
 
@@ -12,7 +12,7 @@ module RSpec
12
12
  #
13
13
  # Used internally to print deprecation warnings
14
14
  # when rspec-core isn't loaded
15
- def warn_deprecation(message, options = {})
15
+ def warn_deprecation(message, options={})
16
16
  warn_with "DEPRECATION: \n #{message}", options
17
17
  end
18
18
 
@@ -26,11 +26,11 @@ module RSpec
26
26
  # @private
27
27
  #
28
28
  # Used internally to print longer warnings
29
- def warn_with(message, options = {})
29
+ def warn_with(message, options={})
30
30
  call_site = options.fetch(:call_site) { CallerFilter.first_non_rspec_line }
31
- message << " Use #{options[:replacement]} instead." if options[:replacement]
32
- message << " Called from #{call_site}." if call_site
33
- ::Kernel.warn message
31
+ message += " Use #{options[:replacement]} instead." if options[:replacement]
32
+ message += " Called from #{call_site}." if call_site
33
+ Support.warning_notifier.call message
34
34
  end
35
35
  end
36
36
  end
@@ -0,0 +1,33 @@
1
+ RSpec::Support.require_rspec_support("method_signature_verifier")
2
+
3
+ module RSpec
4
+ module Support
5
+ module WithKeywordsWhenNeeded
6
+ # This module adds keyword sensitive support for core ruby methods
7
+ # where we cannot use `ruby2_keywords` directly.
8
+
9
+ module_function
10
+
11
+ if RSpec::Support::RubyFeatures.kw_args_supported?
12
+ # Remove this in RSpec 4 in favour of explicitly passed in kwargs where
13
+ # this is used. Works around a warning in Ruby 2.7
14
+
15
+ def class_exec(klass, *args, &block)
16
+ if MethodSignature.new(block).has_kw_args_in?(args)
17
+ binding.eval(<<-CODE, __FILE__, __LINE__)
18
+ kwargs = args.pop
19
+ klass.class_exec(*args, **kwargs, &block)
20
+ CODE
21
+ else
22
+ klass.class_exec(*args, &block)
23
+ end
24
+ end
25
+ ruby2_keywords :class_exec if respond_to?(:ruby2_keywords, true)
26
+ else
27
+ def class_exec(klass, *args, &block)
28
+ klass.class_exec(*args, &block)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
data/lib/rspec/support.rb CHANGED
@@ -14,7 +14,12 @@ module RSpec
14
14
  def self.define_optimized_require_for_rspec(lib, &require_relative)
15
15
  name = "require_rspec_#{lib}"
16
16
 
17
- if Kernel.respond_to?(:require_relative)
17
+ if RUBY_PLATFORM == 'java' && !Kernel.respond_to?(:require)
18
+ # JRuby 9.1.17.0 has developed a regression for require
19
+ (class << self; self; end).__send__(:define_method, name) do |f|
20
+ Kernel.send(:require, "rspec/#{lib}/#{f}")
21
+ end
22
+ elsif Kernel.respond_to?(:require_relative)
18
23
  (class << self; self; end).__send__(:define_method, name) do |f|
19
24
  require_relative.call("#{lib}/#{f}")
20
25
  end
@@ -51,7 +56,7 @@ module RSpec
51
56
  handle = object.method(method_name)
52
57
  raise original unless handle.is_a? Method
53
58
  handle
54
- rescue Exception
59
+ rescue Support::AllExceptionsExceptOnesWeMustNotRescue
55
60
  raise original
56
61
  end
57
62
  end
@@ -67,10 +72,89 @@ module RSpec
67
72
  handle = object.method(method_name)
68
73
  raise original unless handle.is_a? Method
69
74
  handle
70
- rescue Exception
75
+ rescue Support::AllExceptionsExceptOnesWeMustNotRescue
71
76
  raise original
72
77
  end
73
78
  end
74
79
  end
80
+
81
+ # @api private
82
+ #
83
+ # Used internally to get a class of a given object, even if it does not respond to #class.
84
+ def self.class_of(object)
85
+ object.class
86
+ rescue NoMethodError
87
+ singleton_class = class << object; self; end
88
+ singleton_class.ancestors.find { |ancestor| !ancestor.equal?(singleton_class) }
89
+ end
90
+
91
+ # A single thread local variable so we don't excessively pollute that namespace.
92
+ if RUBY_VERSION.to_f >= 2
93
+ def self.thread_local_data
94
+ Thread.current.thread_variable_get(:__rspec) || Thread.current.thread_variable_set(:__rspec, {})
95
+ end
96
+ else
97
+ def self.thread_local_data
98
+ Thread.current[:__rspec] ||= {}
99
+ end
100
+ end
101
+
102
+ # @api private
103
+ def self.failure_notifier=(callable)
104
+ thread_local_data[:failure_notifier] = callable
105
+ end
106
+
107
+ # @private
108
+ DEFAULT_FAILURE_NOTIFIER = lambda { |failure, _opts| raise failure }
109
+
110
+ # @api private
111
+ def self.failure_notifier
112
+ thread_local_data[:failure_notifier] || DEFAULT_FAILURE_NOTIFIER
113
+ end
114
+
115
+ # @api private
116
+ def self.notify_failure(failure, options={})
117
+ failure_notifier.call(failure, options)
118
+ end
119
+
120
+ # @api private
121
+ def self.with_failure_notifier(callable)
122
+ orig_notifier = failure_notifier
123
+ self.failure_notifier = callable
124
+ yield
125
+ ensure
126
+ self.failure_notifier = orig_notifier
127
+ end
128
+
129
+ class << self
130
+ # @api private
131
+ attr_writer :warning_notifier
132
+ end
133
+
134
+ # @private
135
+ DEFAULT_WARNING_NOTIFIER = lambda { |warning| ::Kernel.warn warning }
136
+
137
+ # @api private
138
+ def self.warning_notifier
139
+ @warning_notifier ||= DEFAULT_WARNING_NOTIFIER
140
+ end
141
+
142
+ # @private
143
+ module AllExceptionsExceptOnesWeMustNotRescue
144
+ # These exceptions are dangerous to rescue as rescuing them
145
+ # would interfere with things we should not interfere with.
146
+ AVOID_RESCUING = [NoMemoryError, SignalException, Interrupt, SystemExit]
147
+
148
+ def self.===(exception)
149
+ AVOID_RESCUING.none? { |ar| ar === exception }
150
+ end
151
+ end
152
+
153
+ # The Differ is only needed when a spec fails with a diffable failure.
154
+ # In the more common case of all specs passing or the only failures being
155
+ # non-diffable, we can avoid the extra cost of loading the differ, diff-lcs,
156
+ # pp, etc by avoiding an unnecessary require. Instead, autoload will take
157
+ # care of loading the differ on first use.
158
+ autoload :Differ, "rspec/support/differ"
75
159
  end
76
160
  end
data.tar.gz.sig CHANGED
Binary file