simplecov 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -7
- data/.rspec +3 -1
- data/.rubocop.yml +6 -5
- data/CHANGELOG.md +16 -2
- data/Gemfile +14 -14
- data/README.md +28 -12
- data/Rakefile +5 -8
- data/features/config_formatters.feature +1 -1
- data/features/config_tracked_files.feature +29 -0
- data/features/step_definitions/transformers.rb +1 -1
- data/features/support/env.rb +6 -3
- data/lib/simplecov.rb +19 -2
- data/lib/simplecov/configuration.rb +24 -4
- data/lib/simplecov/defaults.rb +28 -5
- data/lib/simplecov/file_list.rb +11 -0
- data/lib/simplecov/filter.rb +1 -1
- data/lib/simplecov/formatter/multi_formatter.rb +17 -13
- data/lib/simplecov/result.rb +1 -1
- data/lib/simplecov/source_file.rb +6 -3
- data/lib/simplecov/version.rb +1 -1
- data/spec/1_8_fallbacks_spec.rb +29 -0
- data/spec/command_guesser_spec.rb +46 -0
- data/spec/deleted_source_spec.rb +12 -0
- data/{test → spec}/faked_project/Gemfile +0 -0
- data/{test → spec}/faked_project/Rakefile +0 -0
- data/{test → spec}/faked_project/cucumber.yml +0 -0
- data/{test → spec}/faked_project/features/step_definitions/my_steps.rb +0 -0
- data/{test → spec}/faked_project/features/support/env.rb +0 -0
- data/{test → spec}/faked_project/features/test_stuff.feature +0 -0
- data/{test → spec}/faked_project/lib/faked_project.rb +1 -1
- data/{test → spec}/faked_project/lib/faked_project/framework_specific.rb +0 -0
- data/{test → spec}/faked_project/lib/faked_project/meta_magic.rb +0 -0
- data/{test → spec}/faked_project/lib/faked_project/some_class.rb +0 -0
- data/spec/faked_project/lib/faked_project/untested_class.rb +11 -0
- data/{test → spec}/faked_project/spec/faked_spec.rb +0 -0
- data/{test → spec}/faked_project/spec/forking_spec.rb +0 -0
- data/{test → spec}/faked_project/spec/meta_magic_spec.rb +0 -0
- data/{test → spec}/faked_project/spec/some_class_spec.rb +0 -0
- data/{test → spec}/faked_project/spec/spec_helper.rb +0 -0
- data/{test → spec}/faked_project/test/faked_test.rb +0 -0
- data/{test → spec}/faked_project/test/meta_magic_test.rb +0 -0
- data/{test → spec}/faked_project/test/some_class_test.rb +0 -0
- data/{test → spec}/faked_project/test/test_helper.rb +0 -0
- data/spec/file_list_spec.rb +48 -0
- data/spec/filters_spec.rb +96 -0
- data/{test → spec}/fixtures/app/controllers/sample_controller.rb +0 -0
- data/{test → spec}/fixtures/app/models/user.rb +0 -0
- data/{test → spec}/fixtures/deleted_source_sample.rb +0 -0
- data/{test → spec}/fixtures/frameworks/rspec_bad.rb +0 -0
- data/{test → spec}/fixtures/frameworks/rspec_good.rb +0 -0
- data/{test → spec}/fixtures/frameworks/testunit_bad.rb +0 -0
- data/{test → spec}/fixtures/frameworks/testunit_good.rb +0 -0
- data/{test → spec}/fixtures/iso-8859.rb +0 -0
- data/{test → spec}/fixtures/resultset1.rb +0 -0
- data/{test → spec}/fixtures/resultset2.rb +0 -0
- data/{test → spec}/fixtures/sample.rb +0 -0
- data/{test → spec}/fixtures/utf-8.rb +0 -0
- data/{test → spec}/helper.rb +3 -7
- data/spec/merge_helpers_spec.rb +108 -0
- data/spec/result_spec.rb +207 -0
- data/spec/return_codes_spec.rb +37 -0
- data/spec/source_file_line_spec.rb +153 -0
- data/spec/source_file_spec.rb +75 -0
- metadata +163 -149
- data/test/shoulda_macros.rb +0 -19
- data/test/test_1_8_fallbacks.rb +0 -31
- data/test/test_command_guesser.rb +0 -19
- data/test/test_deleted_source.rb +0 -14
- data/test/test_file_list.rb +0 -23
- data/test/test_filters.rb +0 -94
- data/test/test_merge_helpers.rb +0 -112
- data/test/test_result.rb +0 -175
- data/test/test_return_codes.rb +0 -41
- data/test/test_source_file.rb +0 -73
- data/test/test_source_file_line.rb +0 -106
data/lib/simplecov/file_list.rb
CHANGED
@@ -26,6 +26,17 @@ module SimpleCov
|
|
26
26
|
map { |f| f.skipped_lines.count }.inject(&:+)
|
27
27
|
end
|
28
28
|
|
29
|
+
# Computes the coverage based upon lines covered and lines missed for each file
|
30
|
+
# Returns an array with all coverage percentages
|
31
|
+
def covered_percentages
|
32
|
+
map(&:covered_percent)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Finds the least covered file and returns that file's name
|
36
|
+
def least_covered_file
|
37
|
+
sort_by(&:covered_percent).first.filename
|
38
|
+
end
|
39
|
+
|
29
40
|
# Returns the overall amount of relevant lines of code across all files in this list
|
30
41
|
def lines_of_code
|
31
42
|
covered_lines + missed_lines
|
data/lib/simplecov/filter.rb
CHANGED
@@ -21,7 +21,7 @@ module SimpleCov
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def passes?(source_file)
|
24
|
-
warn "
|
24
|
+
warn "#{Kernel.caller.first}: [DEPRECATION] #passes? is deprecated. Use #matches? instead."
|
25
25
|
matches?(source_file)
|
26
26
|
end
|
27
27
|
end
|
@@ -1,27 +1,31 @@
|
|
1
1
|
module SimpleCov
|
2
2
|
module Formatter
|
3
3
|
class MultiFormatter
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
module InstanceMethods
|
5
|
+
def format(result)
|
6
|
+
formatters.map do |formatter|
|
7
|
+
begin
|
8
|
+
formatter.new.format(result)
|
9
|
+
rescue => e
|
10
|
+
STDERR.puts("Formatter #{formatter} failed with #{e.class}: #{e.message} (#{e.backtrace.first})")
|
11
|
+
nil
|
12
|
+
end
|
8
13
|
end
|
9
14
|
end
|
10
15
|
end
|
11
16
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
rescue => e
|
17
|
-
STDERR.puts("Formatter #{formatter} failed with #{e.class}: #{e.message} (#{e.backtrace.first})")
|
18
|
-
nil
|
17
|
+
def self.new(formatters = nil)
|
18
|
+
Class.new do
|
19
|
+
define_method :formatters do
|
20
|
+
@formatters ||= Array(formatters)
|
19
21
|
end
|
22
|
+
include InstanceMethods
|
20
23
|
end
|
21
24
|
end
|
22
25
|
|
23
|
-
def
|
24
|
-
|
26
|
+
def self.[](*args)
|
27
|
+
warn "#{Kernel.caller.first}: [DEPRECATION] ::[] is deprecated. Use ::new instead."
|
28
|
+
new(*args)
|
25
29
|
end
|
26
30
|
end
|
27
31
|
end
|
data/lib/simplecov/result.rb
CHANGED
@@ -18,7 +18,7 @@ module SimpleCov
|
|
18
18
|
# Explicitly set the command name that was used for this coverage result. Defaults to SimpleCov.command_name
|
19
19
|
attr_writer :command_name
|
20
20
|
|
21
|
-
def_delegators :files, :covered_percent, :covered_strength, :covered_lines, :missed_lines
|
21
|
+
def_delegators :files, :covered_percent, :covered_percentages, :least_covered_file, :covered_strength, :covered_lines, :missed_lines
|
22
22
|
def_delegator :files, :lines_of_code, :total_lines
|
23
23
|
|
24
24
|
# Initialize a new SimpleCov::Result from given Coverage.result (a Hash of filenames each containing an array of
|
@@ -28,8 +28,10 @@ module SimpleCov
|
|
28
28
|
fail ArgumentError, "Only String accepted for source" unless src.is_a?(String)
|
29
29
|
fail ArgumentError, "Only Fixnum accepted for line_number" unless line_number.is_a?(Fixnum)
|
30
30
|
fail ArgumentError, "Only Fixnum and nil accepted for coverage" unless coverage.is_a?(Fixnum) || coverage.nil?
|
31
|
-
@src
|
32
|
-
@
|
31
|
+
@src = src
|
32
|
+
@line_number = line_number
|
33
|
+
@coverage = coverage
|
34
|
+
@skipped = false
|
33
35
|
end
|
34
36
|
|
35
37
|
# Returns true if this is a line that should have been covered, but was not
|
@@ -77,7 +79,8 @@ module SimpleCov
|
|
77
79
|
alias_method :source, :src
|
78
80
|
|
79
81
|
def initialize(filename, coverage)
|
80
|
-
@filename
|
82
|
+
@filename = filename
|
83
|
+
@coverage = coverage
|
81
84
|
File.open(filename, "rb") { |f| @src = f.readlines }
|
82
85
|
end
|
83
86
|
|
data/lib/simplecov/version.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
# Tests that verify that on 1.8 versions of ruby, simplecov simply
|
4
|
+
# does not launch and does not cause errors on the way
|
5
|
+
#
|
6
|
+
# TODO: This should be expanded upon all methods that could potentially
|
7
|
+
# be called in a test/spec-helper simplecov config block
|
8
|
+
#
|
9
|
+
describe "Ruby 1.8 fallback" do
|
10
|
+
it "return false when calling SimpleCov.start" do
|
11
|
+
expect(SimpleCov.start).to be false
|
12
|
+
end
|
13
|
+
|
14
|
+
it "return false when calling SimpleCov.start with a block" do
|
15
|
+
expect(SimpleCov.start { fail "Shouldn't reach this!" }).to be false
|
16
|
+
end
|
17
|
+
|
18
|
+
it "return false when calling SimpleCov.configure with a block" do
|
19
|
+
expect(SimpleCov.configure { fail "Shouldn't reach this!" }).to be false
|
20
|
+
end
|
21
|
+
|
22
|
+
it "allow to define a profile" do
|
23
|
+
expect do
|
24
|
+
SimpleCov.profiles.define "testprofile" do
|
25
|
+
add_filter "/config/"
|
26
|
+
end
|
27
|
+
end.not_to raise_error
|
28
|
+
end
|
29
|
+
end if RUBY_VERSION.start_with? "1.8"
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
describe SimpleCov::CommandGuesser do
|
4
|
+
subject { SimpleCov::CommandGuesser }
|
5
|
+
it 'correctly guesses "Unit Tests" for unit tests' do
|
6
|
+
subject.original_run_command = "/some/path/test/units/foo_bar_test.rb"
|
7
|
+
expect(subject.guess).to eq("Unit Tests")
|
8
|
+
subject.original_run_command = "test/units/foo.rb"
|
9
|
+
expect(subject.guess).to eq("Unit Tests")
|
10
|
+
subject.original_run_command = "test/foo.rb"
|
11
|
+
expect(subject.guess).to eq("Unit Tests")
|
12
|
+
subject.original_run_command = "test/{models,helpers,unit}/**/*_test.rb"
|
13
|
+
expect(subject.guess).to eq("Unit Tests")
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'correctly guesses "Functional Tests" for functional tests' do
|
17
|
+
subject.original_run_command = "/some/path/test/functional/foo_bar_controller_test.rb"
|
18
|
+
expect(subject.guess).to eq("Functional Tests")
|
19
|
+
subject.original_run_command = "test/{controllers,mailers,functional}/**/*_test.rb"
|
20
|
+
expect(subject.guess).to eq("Functional Tests")
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'correctly guesses "Integration Tests" for integration tests' do
|
24
|
+
subject.original_run_command = "/some/path/test/integration/foo_bar_controller_test.rb"
|
25
|
+
expect(subject.guess).to eq("Integration Tests")
|
26
|
+
subject.original_run_command = "test/integration/**/*_test.rb"
|
27
|
+
expect(subject.guess).to eq("Integration Tests")
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'correctly guesses "Cucumber Features" for cucumber features' do
|
31
|
+
subject.original_run_command = "features"
|
32
|
+
expect(subject.guess).to eq("Cucumber Features")
|
33
|
+
subject.original_run_command = "cucumber"
|
34
|
+
expect(subject.guess).to eq("Cucumber Features")
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'correctly guesses "RSpec" for RSpec' do
|
38
|
+
subject.original_run_command = "/some/path/spec/foo.rb"
|
39
|
+
expect(subject.guess).to eq("RSpec")
|
40
|
+
end
|
41
|
+
|
42
|
+
it "defaults to RSpec because RSpec constant is defined" do
|
43
|
+
subject.original_run_command = "some_arbitrary_command with arguments"
|
44
|
+
expect(subject.guess).to eq("RSpec")
|
45
|
+
end
|
46
|
+
end if SimpleCov.usable?
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
# Test to verify correct handling of deleted files
|
4
|
+
# See https://github.com/colszowka/simplecov/issues/9
|
5
|
+
describe "A source file which is subsequently deleted" do
|
6
|
+
it "does not cause an error" do
|
7
|
+
Dir.chdir(File.join(File.dirname(__FILE__), "fixtures")) do
|
8
|
+
`ruby deleted_source_sample.rb`
|
9
|
+
expect($?.exitstatus).to be_zero
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -4,7 +4,7 @@ class FakedProject
|
|
4
4
|
end
|
5
5
|
end
|
6
6
|
|
7
|
-
Dir[File.join(File.dirname(__FILE__), "faked_project/*.rb")].each do |file|
|
7
|
+
Dir[File.join(File.dirname(__FILE__), "faked_project/*.rb")].reject { |f| /untested/.match(f) }.each do |file|
|
8
8
|
require file # Require all source files in project dynamically so we can inject some stuff depending on test situation
|
9
9
|
end
|
10
10
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
describe SimpleCov::Result do
|
4
|
+
subject do
|
5
|
+
original_result = {
|
6
|
+
source_fixture("sample.rb") => [nil, 1, 1, 1, nil, nil, 1, 1, nil, nil],
|
7
|
+
source_fixture("app/models/user.rb") => [nil, 1, 1, 1, nil, nil, 1, 0, nil, nil],
|
8
|
+
source_fixture("app/controllers/sample_controller.rb") => [nil, 2, 2, 0, nil, nil, 0, nil, nil, nil],
|
9
|
+
}
|
10
|
+
SimpleCov::Result.new(original_result).files
|
11
|
+
end
|
12
|
+
|
13
|
+
it "has 11 covered lines" do
|
14
|
+
expect(subject.covered_lines).to eq(11)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "has 3 missed lines" do
|
18
|
+
expect(subject.missed_lines).to eq(3)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "has 19 never lines" do
|
22
|
+
expect(subject.never_lines).to eq(19)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "has 14 lines of code" do
|
26
|
+
expect(subject.lines_of_code).to eq(14)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "has 3 skipped lines" do
|
30
|
+
expect(subject.skipped_lines).to eq(3)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "has the correct covered percent" do
|
34
|
+
expect(subject.covered_percent).to eq(78.57142857142857)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "has the correct covered percentages" do
|
38
|
+
expect(subject.covered_percentages).to eq([50.0, 80.0, 100.0])
|
39
|
+
end
|
40
|
+
|
41
|
+
it "has the correct least covered file" do
|
42
|
+
expect(subject.least_covered_file).to match(/sample_controller.rb/)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "has the correct covered strength" do
|
46
|
+
expect(subject.covered_strength).to eq(0.9285714285714286)
|
47
|
+
end
|
48
|
+
end if SimpleCov.usable?
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
describe SimpleCov::SourceFile do
|
4
|
+
subject do
|
5
|
+
SimpleCov::SourceFile.new(source_fixture("sample.rb"), [nil, 1, 1, 1, nil, nil, 1, 0, nil, nil])
|
6
|
+
end
|
7
|
+
|
8
|
+
it "doesn't match a new SimpleCov::StringFilter 'foobar'" do
|
9
|
+
expect(SimpleCov::StringFilter.new("foobar")).not_to be_matches subject
|
10
|
+
end
|
11
|
+
|
12
|
+
it "doesn't match a new SimpleCov::StringFilter 'some/path'" do
|
13
|
+
expect(SimpleCov::StringFilter.new("some/path")).not_to be_matches subject
|
14
|
+
end
|
15
|
+
|
16
|
+
it "matches a new SimpleCov::StringFilter 'spec/fixtures'" do
|
17
|
+
expect(SimpleCov::StringFilter.new("spec/fixtures")).to be_matches subject
|
18
|
+
end
|
19
|
+
|
20
|
+
it "matches a new SimpleCov::StringFilter 'spec/fixtures/sample.rb'" do
|
21
|
+
expect(SimpleCov::StringFilter.new("spec/fixtures/sample.rb")).to be_matches subject
|
22
|
+
end
|
23
|
+
|
24
|
+
it "matches a new SimpleCov::StringFilter 'sample.rb'" do
|
25
|
+
expect(SimpleCov::StringFilter.new("sample.rb")).to be_matches subject
|
26
|
+
end
|
27
|
+
|
28
|
+
it "doesn't match a new SimpleCov::BlockFilter that is not applicable" do
|
29
|
+
expect(SimpleCov::BlockFilter.new(proc { |s| File.basename(s.filename) == "foo.rb" })).not_to be_matches subject
|
30
|
+
end
|
31
|
+
|
32
|
+
it "matches a new SimpleCov::BlockFilter that is applicable" do
|
33
|
+
expect(SimpleCov::BlockFilter.new(proc { |s| File.basename(s.filename) == "sample.rb" })).to be_matches subject
|
34
|
+
end
|
35
|
+
|
36
|
+
it "matches a new SimpleCov::ArrayFilter when 'sample.rb' is passed as array" do
|
37
|
+
expect(SimpleCov::ArrayFilter.new(["sample.rb"])).to be_matches subject
|
38
|
+
end
|
39
|
+
|
40
|
+
it "doesn't match a new SimpleCov::ArrayFilter when a file path different than 'sample.rb' is passed as array" do
|
41
|
+
expect(SimpleCov::ArrayFilter.new(["other_file.rb"])).not_to be_matches subject
|
42
|
+
end
|
43
|
+
|
44
|
+
it "matches a new SimpleCov::ArrayFilter when two file paths including 'sample.rb' are passed as array" do
|
45
|
+
expect(SimpleCov::ArrayFilter.new(["sample.rb", "other_file.rb"])).to be_matches subject
|
46
|
+
end
|
47
|
+
|
48
|
+
context "with no filters set up and a basic source file in an array" do
|
49
|
+
before do
|
50
|
+
@prev_filters = SimpleCov.filters
|
51
|
+
SimpleCov.filters = []
|
52
|
+
end
|
53
|
+
|
54
|
+
subject do
|
55
|
+
[SimpleCov::SourceFile.new(source_fixture("sample.rb"), [nil, 1, 1, 1, nil, nil, 1, 0, nil, nil])]
|
56
|
+
end
|
57
|
+
|
58
|
+
after do
|
59
|
+
SimpleCov.filters = @prev_filters
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'returns 0 items after executing SimpleCov.filtered on files when using a "sample" string filter' do
|
63
|
+
SimpleCov.add_filter "sample"
|
64
|
+
expect(SimpleCov.filtered(subject).count).to be_zero
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'returns 0 items after executing SimpleCov.filtered on files when using a "spec/fixtures" string filter' do
|
68
|
+
SimpleCov.add_filter "spec/fixtures"
|
69
|
+
expect(SimpleCov.filtered(subject).count).to be_zero
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'returns 1 item after executing SimpleCov.filtered on files when using a "fooo" string filter' do
|
73
|
+
SimpleCov.add_filter "fooo"
|
74
|
+
expect(SimpleCov.filtered(subject).count).to eq(1)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "returns 0 items after executing SimpleCov.filtered on files when using a block filter that returns true" do
|
78
|
+
SimpleCov.add_filter do
|
79
|
+
true
|
80
|
+
end
|
81
|
+
expect(SimpleCov.filtered(subject).count).to be_zero
|
82
|
+
end
|
83
|
+
|
84
|
+
it "returns 1 item after executing SimpleCov.filtered on files when using an always-false block filter" do
|
85
|
+
SimpleCov.add_filter do
|
86
|
+
false
|
87
|
+
end
|
88
|
+
expect(SimpleCov.filtered(subject).count).to eq(1)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "returns a FileList after filtering" do
|
92
|
+
SimpleCov.add_filter "fooo"
|
93
|
+
expect(SimpleCov.filtered(subject)).to be_a SimpleCov::FileList
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end if SimpleCov.usable?
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/{test → spec}/helper.rb
RENAMED
@@ -1,7 +1,5 @@
|
|
1
|
-
require "bundler/setup"
|
2
1
|
require "simplecov"
|
3
|
-
require "
|
4
|
-
require "shoulda"
|
2
|
+
require "rspec"
|
5
3
|
|
6
4
|
SimpleCov.coverage_dir("tmp/coverage")
|
7
5
|
|
@@ -9,9 +7,6 @@ def source_fixture(filename)
|
|
9
7
|
File.expand_path(File.join(File.dirname(__FILE__), "fixtures", filename))
|
10
8
|
end
|
11
9
|
|
12
|
-
require "shoulda_macros"
|
13
|
-
Minitest::Test.send :extend, ShouldaMacros
|
14
|
-
|
15
10
|
# Taken from http://stackoverflow.com/questions/4459330/how-do-i-temporarily-redirect-stderr-in-ruby
|
16
11
|
require "stringio"
|
17
12
|
|
@@ -19,7 +14,8 @@ def capture_stderr
|
|
19
14
|
# The output stream must be an IO-like object. In this case we capture it in
|
20
15
|
# an in-memory IO object so we can return the string value. You can assign any
|
21
16
|
# IO object here.
|
22
|
-
previous_stderr
|
17
|
+
previous_stderr = $stderr
|
18
|
+
$stderr = StringIO.new
|
23
19
|
yield
|
24
20
|
$stderr.string
|
25
21
|
ensure
|