audiothority 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|