audiothority 0.1.0
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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +30 -0
- data/bin/audiothorian +8 -0
- data/lib/audiothority.rb +18 -0
- data/lib/audiothority/change.rb +68 -0
- data/lib/audiothority/cli.rb +82 -0
- data/lib/audiothority/crawler.rb +30 -0
- data/lib/audiothority/custodian.rb +26 -0
- data/lib/audiothority/enforcer.rb +60 -0
- data/lib/audiothority/extract.rb +30 -0
- data/lib/audiothority/inspector.rb +31 -0
- data/lib/audiothority/society.rb +18 -0
- data/lib/audiothority/summary.rb +32 -0
- data/lib/audiothority/tracker.rb +17 -0
- data/lib/audiothority/validation.rb +33 -0
- data/lib/audiothority/validators.rb +15 -0
- data/lib/audiothority/validators/album.rb +11 -0
- data/lib/audiothority/validators/artist.rb +11 -0
- data/lib/audiothority/validators/track.rb +16 -0
- data/lib/audiothority/validators/unique.rb +22 -0
- data/lib/audiothority/validators/year.rb +11 -0
- data/lib/audiothority/version.rb +5 -0
- data/spec/audiothority/crawler_spec.rb +62 -0
- data/spec/audiothority/custodian_spec.rb +62 -0
- data/spec/audiothority/extract_spec.rb +126 -0
- data/spec/audiothority/inspector_spec.rb +72 -0
- data/spec/audiothority/society_spec.rb +27 -0
- data/spec/audiothority/tracker_spec.rb +34 -0
- data/spec/audiothority/validators_spec.rb +134 -0
- data/spec/integration/enforce_integration_spec.rb +91 -0
- data/spec/integration/scan_integration_spec.rb +95 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/cli_setup.rb +47 -0
- data/spec/support/interactive.rb +30 -0
- metadata +119 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
|
6
|
+
module Audiothority
|
7
|
+
describe Society do
|
8
|
+
let :society do
|
9
|
+
described_class.new(location, fileutils)
|
10
|
+
end
|
11
|
+
|
12
|
+
let :location do
|
13
|
+
'society'
|
14
|
+
end
|
15
|
+
|
16
|
+
let :fileutils do
|
17
|
+
double(:fileutils, move: 0)
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#transfer' do
|
21
|
+
it 'moves the enforced entity to society location' do
|
22
|
+
society.transfer('enforced')
|
23
|
+
expect(fileutils).to have_received(:move).with('enforced', Pathname.new('society'))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
|
6
|
+
module Audiothority
|
7
|
+
describe Tracker do
|
8
|
+
let :tracker do
|
9
|
+
described_class.new
|
10
|
+
end
|
11
|
+
|
12
|
+
let :violations do
|
13
|
+
[double(:violation)]
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#mark' do
|
17
|
+
it 'adds path and violations' do
|
18
|
+
tracker.mark('path', violations)
|
19
|
+
expect(tracker.suspects).to eq({'path' => violations})
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#suspects' do
|
24
|
+
it 'returns marked paths with violations' do
|
25
|
+
tracker.mark('path', violations)
|
26
|
+
expect(tracker.suspects).to eq({'path' => violations})
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'freezes the returned suspects' do
|
30
|
+
expect(tracker.suspects).to be_frozen
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
|
6
|
+
module Audiothority
|
7
|
+
module Validators
|
8
|
+
shared_examples_for 'an uniqueness validator' do
|
9
|
+
let :validator do
|
10
|
+
described_class.new
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when `field` is unique among given tags' do
|
14
|
+
let :tags do
|
15
|
+
3.times.map { |i| OpenStruct.new(field => 'same') }
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'returns a `valid` validation' do
|
19
|
+
expect(validator.validate(tags)).to be_valid
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when `field` is not unique among given tags' do
|
24
|
+
let :tags do
|
25
|
+
3.times.map { |i| OpenStruct.new(field => i) }
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'returns an `invalid` validation' do
|
29
|
+
expect(validator.validate(tags)).to be_invalid
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'indicates which field it is concerned about' do
|
33
|
+
violation = validator.validate(tags)
|
34
|
+
expect(violation.field).to eq(field)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'reports that there are multiple values for field' do
|
38
|
+
violation = validator.validate(tags)
|
39
|
+
expect(violation.reason).to eq(:multiple)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when `field` is not present among given tags' do
|
44
|
+
let :tags do
|
45
|
+
3.times.map { |i| OpenStruct.new(field => nil) }
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'returns an `invalid` validation' do
|
49
|
+
expect(validator.validate(tags)).to be_invalid
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'indicates which field it is concerned about' do
|
53
|
+
violation = validator.validate(tags)
|
54
|
+
expect(violation.field).to eq(field)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'reports that field is missing' do
|
58
|
+
violation = validator.validate(tags)
|
59
|
+
expect(violation.reason).to eq(:missing)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'reports an applicable violation' do
|
63
|
+
violation = validator.validate(tags)
|
64
|
+
expect(violation).to be_applicable
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe Artist do
|
70
|
+
it_behaves_like 'an uniqueness validator' do
|
71
|
+
let :field do
|
72
|
+
:artist
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe Album do
|
78
|
+
it_behaves_like 'an uniqueness validator' do
|
79
|
+
let :field do
|
80
|
+
:album
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe Year do
|
86
|
+
it_behaves_like 'an uniqueness validator' do
|
87
|
+
let :field do
|
88
|
+
:year
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe TrackNumber do
|
94
|
+
let :validator do
|
95
|
+
described_class.new
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'when `track` is present among all tags' do
|
99
|
+
let :tags do
|
100
|
+
3.times.map { |i| OpenStruct.new(track: i + 1) }
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'returns a `valid` validation' do
|
104
|
+
expect(validator.validate(tags)).to be_valid
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'when `track` of any tag is zero (0)' do
|
109
|
+
let :tags do
|
110
|
+
3.times.map { |i| OpenStruct.new(track: i) }
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'returns an `invalid` validation' do
|
114
|
+
expect(validator.validate(tags)).to be_invalid
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'indicates which field it is concerned about' do
|
118
|
+
violation = validator.validate(tags)
|
119
|
+
expect(violation.field).to eq(:track)
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'reports one or more tags are missing track numbers' do
|
123
|
+
violation = validator.validate(tags)
|
124
|
+
expect(violation.reason).to eq(:missing)
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'reports a non-applicable violation' do
|
128
|
+
violation = validator.validate(tags)
|
129
|
+
expect(violation).to_not be_applicable
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
|
6
|
+
describe 'bin/audiothorian enforce <PATH>' do
|
7
|
+
include_context 'cli setup'
|
8
|
+
|
9
|
+
let :argv do
|
10
|
+
['enforce', music_dir]
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when invoked without any paths' do
|
14
|
+
let :argv do
|
15
|
+
['enforce']
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'prints help for the `enforce` command' do
|
19
|
+
expect { run }.to output(/^Usage:\n.+ enforce/).to_stdout
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'presents a list of inconsistent albums' do
|
24
|
+
expect do
|
25
|
+
interactive(%w[n]) { run }
|
26
|
+
end.to output(/the-album is inconsistent due to:/).to_stdout
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'when the user answers `y` / `yes` to enforcement' do
|
30
|
+
context ', and selects to perform presented changes' do
|
31
|
+
it 'corrects inconsistencies' do
|
32
|
+
interactive(%w[y P]) { run }
|
33
|
+
tags_from(%(#{music_dir}/the-album)) do |tags|
|
34
|
+
expect(tags.map(&:artist).uniq).to eq(['the artist'])
|
35
|
+
expect(tags.map(&:album).uniq).to eq(['the album'])
|
36
|
+
expect(tags.map(&:year).uniq).to eq([2001])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'and -S / --society option is given' do
|
41
|
+
let :society_dir do
|
42
|
+
File.join(music_dir, 'society')
|
43
|
+
end
|
44
|
+
|
45
|
+
let :argv do
|
46
|
+
['enforce', music_dir, '-S', society_dir]
|
47
|
+
end
|
48
|
+
|
49
|
+
before do
|
50
|
+
Dir.mkdir(society_dir)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'moves the enforced entity to society' do
|
54
|
+
interactive(%w[y P]) { run }
|
55
|
+
expect(File.exists?(File.join(society_dir, 'the-album'))).to be true
|
56
|
+
tags_from(%(#{society_dir}/the-album)) do |tags|
|
57
|
+
expect(tags.map(&:artist).uniq).to eq(['the artist'])
|
58
|
+
expect(tags.map(&:album).uniq).to eq(['the album'])
|
59
|
+
expect(tags.map(&:year).uniq).to eq([2001])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context ', and decides to skip performing changes' do
|
66
|
+
it 'does not apply changes' do
|
67
|
+
interactive(%w[y S]) do
|
68
|
+
expect { run }.to output.to_stdout
|
69
|
+
end
|
70
|
+
tags_from(%(#{music_dir}/the-album)) do |tags|
|
71
|
+
expect(tags.map(&:artist).uniq.size).to be > 1
|
72
|
+
expect(tags.map(&:album).uniq.size).to be > 1
|
73
|
+
expect(tags.map(&:year).uniq.size).to be > 1
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'when the user answer anything else' do
|
80
|
+
it 'terminates and does not change any files' do
|
81
|
+
interactive(%w[n]) do
|
82
|
+
expect { run }.to output.to_stdout
|
83
|
+
end
|
84
|
+
tags_from(%(#{music_dir}/the-album)) do |tags|
|
85
|
+
expect(tags.map(&:artist).uniq.size).to be > 1
|
86
|
+
expect(tags.map(&:album).uniq.size).to be > 1
|
87
|
+
expect(tags.map(&:year).uniq.size).to be > 1
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
|
6
|
+
describe 'bin/audiothorian scan <PATH>' do
|
7
|
+
include_context 'cli setup'
|
8
|
+
|
9
|
+
context 'when invoked without any paths' do
|
10
|
+
let :argv do
|
11
|
+
['scan']
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'prints help for the `scan` command' do
|
15
|
+
expect { run }.to output(/^Usage:\n.+ scan/).to_stdout
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'when a directory does not contain inconsistencies' do
|
20
|
+
let :argv do
|
21
|
+
['scan', empty_dir]
|
22
|
+
end
|
23
|
+
|
24
|
+
let :empty_dir do
|
25
|
+
path = File.join(music_dir, 'empty-dir')
|
26
|
+
Dir.mkdir(path)
|
27
|
+
path
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'prints an `all is good` message' do
|
31
|
+
expect { run }.to output(/All is good/).to_stdout
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when a directory contains directories with inconsistencies' do
|
36
|
+
context 'without any options' do
|
37
|
+
let :argv do
|
38
|
+
['scan', music_dir]
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'scans a given directory and reports inconsistencies' do
|
42
|
+
expect { run }.to output(/the-album is inconsistent due to:/).to_stdout
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'reports inconsistencies in `artist` field' do
|
46
|
+
expect { run }.to output(/multiple artists:/).to_stdout
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'reports inconsistencies in `album` field' do
|
50
|
+
expect { run }.to output(/multiple albums:/).to_stdout
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'reports inconsistencies in `year` field' do
|
54
|
+
expect { run }.to output(/multiple years:/).to_stdout
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'with --paths-only' do
|
59
|
+
let :argv do
|
60
|
+
['scan', music_dir, '--paths-only']
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'prints paths to inconsistent albums' do
|
64
|
+
expect { run }.to output(/\/the-album$/).to_stdout
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'with -C / --custody' do
|
69
|
+
let :custody do
|
70
|
+
File.join(music_dir, 'custody')
|
71
|
+
end
|
72
|
+
|
73
|
+
context ', and custody exists' do
|
74
|
+
before do
|
75
|
+
Dir.mkdir(custody)
|
76
|
+
end
|
77
|
+
|
78
|
+
before do
|
79
|
+
run_audiothorian(['scan', music_dir, '-C', custody])
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'moves suspects into custody' do
|
83
|
+
expect(File.exists?(File.join(music_dir, 'custody', 'the-album'))).to be true
|
84
|
+
expect(Dir[%(#{custody}/**/*.mp3)].size).to eq(3)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context ', and custody does not exist' do
|
89
|
+
it 'raises an error' do
|
90
|
+
expect { run_audiothorian(['scan', music_dir, '-C', custody]) }.to raise_error(Audiothority::CustodyTorchedError)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'tmpdir'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'support/cli_setup'
|
6
|
+
require 'support/interactive'
|
7
|
+
require 'coveralls'
|
8
|
+
require 'simplecov'
|
9
|
+
|
10
|
+
if ENV.include?('TRAVIS')
|
11
|
+
Coveralls.wear!
|
12
|
+
SimpleCov.formatter = Coveralls::SimpleCov::Formatter
|
13
|
+
end
|
14
|
+
|
15
|
+
SimpleCov.start do
|
16
|
+
add_group 'Source', 'lib'
|
17
|
+
add_group 'Unit tests', 'spec/audiothority'
|
18
|
+
add_group 'Integration tests', 'spec/integration'
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'audiothority'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
|
6
|
+
shared_context 'cli setup' do
|
7
|
+
let :run do
|
8
|
+
run_audiothorian(argv)
|
9
|
+
end
|
10
|
+
|
11
|
+
def run_audiothorian(*argv)
|
12
|
+
Audiothority::Cli.start(*argv)
|
13
|
+
end
|
14
|
+
|
15
|
+
def copy_resources(dir)
|
16
|
+
Dir[resources_glob].each do |path|
|
17
|
+
path = Pathname.new(path)
|
18
|
+
FileUtils.copy_entry(path.to_s, %(#{dir}/#{path.basename}))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def resources_glob
|
23
|
+
%(#{resources_dir}/the-album)
|
24
|
+
end
|
25
|
+
|
26
|
+
def resources_dir
|
27
|
+
@resources_dir ||= File.expand_path('../../resources', __FILE__)
|
28
|
+
end
|
29
|
+
|
30
|
+
def music_dir
|
31
|
+
@music_dir
|
32
|
+
end
|
33
|
+
|
34
|
+
def set_music_dir(dir)
|
35
|
+
@music_dir = dir
|
36
|
+
end
|
37
|
+
|
38
|
+
around do |example|
|
39
|
+
Dir.mktmpdir do |dir|
|
40
|
+
Dir.chdir(dir) do
|
41
|
+
copy_resources(dir)
|
42
|
+
set_music_dir(dir)
|
43
|
+
example.call
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|