Skip to content

Commit ed69ad5

Browse files
author
Kevin Paulisse
committed
Move compilation directory logic into a filter
1 parent b31d566 commit ed69ad5

8 files changed

Lines changed: 217 additions & 174 deletions

File tree

lib/octocatalog-diff/catalog-diff/differ.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ class Differ
6060
# @param catalog1_in [OctocatalogDiff::Catalog] First catalog to compare
6161
# @param catalog2_in [OctocatalogDiff::Catalog] Second catalog to compare
6262
def initialize(opts, catalog1_in, catalog2_in)
63+
@catalog1_raw = catalog1_in
64+
@catalog2_raw = catalog2_in
6365
@catalog1 = catalog_resources(catalog1_in, 'First catalog')
6466
@catalog2 = catalog_resources(catalog2_in, 'Second catalog')
6567
@logger = opts.fetch(:logger, Logger.new(StringIO.new))
@@ -149,12 +151,18 @@ def catdiff
149151

150152
# Legacy options which are now filters
151153
@opts[:filters] ||= []
152-
if @opts[:suppress_absent_file_details] && !@opts[:filters].include?('absent_file')
154+
if @opts[:suppress_absent_file_details] && !@opts[:filters].include?('AbsentFile')
153155
@opts[:filters] << 'AbsentFile'
154156
end
157+
@opts[:filters] << 'CompilationDir' unless @opts[:filters].include?('CompilationDir')
155158

156159
# Apply any additional pluggable filters.
157-
OctocatalogDiff::CatalogDiff::Filter.apply_filters(result, @opts[:filters]) if @opts[:filters].any?
160+
filter_opts = {
161+
logger: @logger,
162+
from_compilation_dir: @catalog1_raw.compilation_dir,
163+
to_compilation_dir: @catalog2_raw.compilation_dir
164+
}
165+
OctocatalogDiff::CatalogDiff::Filter.apply_filters(result, @opts[:filters], filter_opts) if @opts[:filters].any?
158166

159167
# That's it!
160168
@logger.debug "Exiting catdiff; change count: #{result.size}"

lib/octocatalog-diff/catalog-diff/filter.rb

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
require_relative 'filter/absent_file'
2+
require_relative 'filter/compilation_dir'
23
require_relative 'filter/yaml'
34

5+
require 'stringio'
6+
47
module OctocatalogDiff
58
module CatalogDiff
69
# Filtering of diffs, and parent class for inheritance.
710
class Filter
11+
attr_accessor :logger
12+
813
# Public: Apply multiple filters by repeatedly calling the `filter` method for each
914
# filter in an array. This method returns nothing.
1015
#
1116
# @param result [Array] Difference array (mutated)
1217
# @param filter_names [Array] Filters to run
13-
# @param options [Hash] Options for each filter (hashed by name)
18+
# @param options [Hash] Options for each filter
1419
def self.apply_filters(result, filter_names, options = {})
1520
return unless filter_names.is_a?(Array)
16-
filter_names.each { |x| filter(result, x, options[x] || {}) }
21+
filter_names.each { |x| filter(result, x, options || {}) }
1722
end
1823

1924
# Public: Perform a filter on `result` using the specified filter class.
@@ -25,13 +30,15 @@ def self.apply_filters(result, filter_names, options = {})
2530
# @param options [Hash] Additional options (optional) to pass to filtered? method
2631
def self.filter(result, filter_class_name, options = {})
2732
filter_class_name = [name.to_s, filter_class_name].join('::')
28-
obj = Kernel.const_get(filter_class_name).new(result)
33+
obj = Kernel.const_get(filter_class_name).new(result, options[:logger])
2934
result.reject! { |item| obj.filtered?(item, options) }
3035
end
3136

32-
# Inherited: Constructor that does nothing. Some filters require working on the entire
33-
# data set and will override this method to perform some pre-processing for efficiency.
34-
def initialize(_diff_array = [])
37+
# Inherited: Constructor. Some filters require working on the entire data set and
38+
# will override this method to perform some pre-processing for efficiency. This also
39+
# sets up the logger object.
40+
def initialize(_diff_array = [], logger = Logger.new(StringIO.new))
41+
@logger = logger
3542
end
3643

3744
# Inherited: Construct a default `filtered?` method for the subclass via inheritance.

lib/octocatalog-diff/catalog-diff/filter/absent_file.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class Filter
99
class AbsentFile < OctocatalogDiff::CatalogDiff::Filter
1010
# Constructor: Since this filter requires knowledge of the entire array of diffs,
1111
# override the inherited method to store those diffs in an instance variable.
12-
def initialize(diffs)
12+
def initialize(diffs, _logger = nil)
1313
@diffs = diffs
1414
@results = nil
1515
end
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# frozen_string_literal: true
2+
3+
require_relative '../../api/v1/diff'
4+
5+
module OctocatalogDiff
6+
module CatalogDiff
7+
class Filter
8+
# Filter out changes that are due to the catalog compilation directory.
9+
class CompilationDir < OctocatalogDiff::CatalogDiff::Filter
10+
# Public: Filter the diff if the change is due to the catalog compilation directory.
11+
# Determine this by obtaining the compiilation directory from each of the catalogs
12+
# (supplied via options) and checking the differences. If the only thing different
13+
# is the compilation directory, filter it out with a warning.
14+
#
15+
# @param diff_in [Array (Internal Diff Format)] Difference
16+
# @param options [Hash] Additional options:
17+
# :from_compilation_dir [String] Compilation directory for the "from" catalog
18+
# :to_compilation_dir [String] Compilation directory for the "to" catalog
19+
# @return [Boolean] true if this difference is a YAML file with identical objects, false otherwise
20+
def filtered?(diff_in, options = {})
21+
return false unless options[:from_compilation_dir] && options[:to_compilation_dir]
22+
dir1 = options[:to_compilation_dir]
23+
dir1_rexp = Regexp.escape(dir1)
24+
dir2 = options[:from_compilation_dir]
25+
dir2_rexp = Regexp.escape(dir2)
26+
dir = Regexp.new("(?:#{dir1_rexp}|#{dir2_rexp})")
27+
diff = OctocatalogDiff::API::V1::Diff.new(diff_in)
28+
# raise "Called on #{diff.inspect} with #{options.inspect}"
29+
30+
# Check for added/removed resources where the title of the resource includes the compilation directory
31+
if (diff.addition? || diff.removal?) && diff.title.match(dir)
32+
message = "Resource #{diff.type}[#{diff.title}]"
33+
message += ' appears to depend on catalog compilation directory. Suppressed from results.'
34+
logger.warn message
35+
return true
36+
end
37+
38+
# Check for a change where the difference in a parameter exactly corresponds to the difference in the
39+
# compilation directory.
40+
if diff.change? && (diff.old_value.is_a?(String) || diff.new_value.is_a?(String))
41+
from_before = nil
42+
from_after = nil
43+
from_match = false
44+
to_before = nil
45+
to_after = nil
46+
to_match = false
47+
48+
if diff.old_value =~ /^(.*)#{dir2}(.*)$/m
49+
from_before = Regexp.last_match(1) || ''
50+
from_after = Regexp.last_match(2) || ''
51+
from_match = true
52+
end
53+
54+
if diff.new_value =~ /^(.*)#{dir1}(.*)$/m
55+
to_before = Regexp.last_match(1) || ''
56+
to_after = Regexp.last_match(2) || ''
57+
to_match = true
58+
end
59+
60+
if from_match && to_match && to_before == from_before && to_after == from_after
61+
message = "Resource key #{diff.type}[#{diff.title}] #{diff.structure.join(' => ')}"
62+
message += ' appears to depend on catalog compilation directory. Suppressed from results.'
63+
@logger.warn message
64+
return true
65+
end
66+
67+
if from_match || to_match
68+
message = "Resource key #{diff.type}[#{diff.title}] #{diff.structure.join(' => ')}"
69+
message += ' may depend on catalog compilation directory, but there may be differences.'
70+
message += ' This is included in results for now, but please verify.'
71+
@logger.warn message
72+
end
73+
end
74+
75+
false
76+
end
77+
end
78+
end
79+
end
80+
end

lib/octocatalog-diff/cli/diffs.rb

Lines changed: 0 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -55,76 +55,10 @@ def diffs(catalogs)
5555

5656
# Actually perform the diff
5757
diff_result = differ.diff
58-
diff_result.delete_if { |element| change_due_to_compilation_dir?(element, catalogs) }
5958
@logger.debug 'Success compute diffs between catalogs'
6059
diff_result
6160
end
6261

63-
# Catch anything that explictly changed as a result of different compilation directories and
64-
# warn about it. These are probably things that should be refactored. For now we're going to pull
65-
# these out after the fact so we can warn about them if they do show up.
66-
# @param change [Array(diff format)] Change in diff format
67-
# @param catalogs [Hash] { :to => OctocatalogDiff::Catalog, :from => OctocatalogDiff::Catalog }
68-
# @return [Boolean] True if change includes compilation directory, false otherwise
69-
def change_due_to_compilation_dir?(change, catalogs)
70-
dir1 = catalogs.fetch(:to).compilation_dir
71-
dir2 = catalogs.fetch(:from).compilation_dir
72-
return false if dir1.nil? || dir2.nil?
73-
74-
dir1_rexp = Regexp.escape(dir1)
75-
dir2_rexp = Regexp.escape(dir2)
76-
dir = "(?:#{dir1_rexp}|#{dir2_rexp})"
77-
78-
# Check for added/removed resources where the title of the resource includes the compilation directory
79-
if change[0] == '+' || change[0] == '-'
80-
if change[1] =~ /^([^\f]+)\f([^\f]*#{dir}[^\f]*)/
81-
message = "Resource #{Regexp.last_match(1)}[#{Regexp.last_match(2)}]"
82-
message += ' appears to depend on catalog compilation directory. Suppressed from results.'
83-
@logger.warn message
84-
return true
85-
end
86-
end
87-
88-
# Check for a change where the difference in a parameter exactly corresponds to the difference in the
89-
# compilation directory.
90-
if change[0] == '~' || change[0] == '!'
91-
from_before = nil
92-
from_after = nil
93-
from_match = false
94-
to_before = nil
95-
to_after = nil
96-
to_match = false
97-
98-
if change[2] =~ /^(.*)#{dir2}(.*)$/m
99-
from_before = Regexp.last_match(1) || ''
100-
from_after = Regexp.last_match(2) || ''
101-
from_match = true
102-
end
103-
104-
if change[3] =~ /^(.*)#{dir1}(.*)$/m
105-
to_before = Regexp.last_match(1) || ''
106-
to_after = Regexp.last_match(2) || ''
107-
to_match = true
108-
end
109-
110-
if from_match && to_match && to_before == from_before && to_after == from_after
111-
message = "Resource key #{change[1].gsub(/\f/, ' => ')}"
112-
message += ' appears to depend on catalog compilation directory. Suppressed from results.'
113-
@logger.warn message
114-
return true
115-
end
116-
117-
if from_match || to_match
118-
message = "Resource key #{change[1].gsub(/\f/, ' => ')}"
119-
message += ' may depend on catalog compilation directory, but there may be differences.'
120-
message += ' This is included in results for now, but please verify.'
121-
@logger.warn message
122-
end
123-
end
124-
125-
false
126-
end
127-
12862
private
12963

13064
# Determine if a resource is tagged with any ignore-tag.
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# frozen_string_literal: true
2+
3+
require_relative '../../spec_helper'
4+
require OctocatalogDiff::Spec.require_path('/catalog')
5+
require OctocatalogDiff::Spec.require_path('/catalog-diff/filter/compilation_dir')
6+
require OctocatalogDiff::Spec.require_path('/cli/diffs')
7+
8+
describe OctocatalogDiff::CatalogDiff::Filter::CompilationDir do
9+
before(:all) do
10+
@cat_compilation_dir_1 = OctocatalogDiff::Catalog.new(
11+
node: 'my.rspec.node',
12+
basedir: '/path/to/catalog1',
13+
json: File.read(OctocatalogDiff::Spec.fixture_path('catalogs/compilation-dir-1.json'))
14+
)
15+
@cat_compilation_dir_2 = OctocatalogDiff::Catalog.new(
16+
node: 'my.rspec.node',
17+
basedir: '/path/to/catalog2',
18+
json: File.read(OctocatalogDiff::Spec.fixture_path('catalogs/compilation-dir-2.json'))
19+
)
20+
end
21+
22+
before(:each) do
23+
@logger, @logger_str = OctocatalogDiff::Spec.setup_logger
24+
end
25+
26+
it 'should remove +/- full title changes due to compilation dirs' do
27+
opts = {
28+
ignore: [
29+
{ type: 'Varies_Due_To_Compilation_Dir_2' },
30+
{ type: 'Varies_Due_To_Compilation_Dir_3' },
31+
{ type: 'Varies_Due_To_Compilation_Dir_4' }
32+
]
33+
}
34+
testobj = OctocatalogDiff::Cli::Diffs.new(opts, @logger)
35+
result = testobj.diffs(from: @cat_compilation_dir_1, to: @cat_compilation_dir_2)
36+
expect(result).to eq([])
37+
expect(@logger_str.string).to match(%r{Varies_Due_To_Compilation_Dir_1\[/path/to/catalog2\] .*Suppressed})
38+
expect(@logger_str.string).to match(%r{Varies_Due_To_Compilation_Dir_1\[/path/to/catalog1\] .*Suppressed})
39+
end
40+
41+
it 'should remove +/- partial title changes due to compilation dirs' do
42+
opts = { ignore:
43+
[
44+
{ type: 'Varies_Due_To_Compilation_Dir_1' },
45+
{ type: 'Varies_Due_To_Compilation_Dir_3' },
46+
{ type: 'Varies_Due_To_Compilation_Dir_4' }
47+
] }
48+
testobj = OctocatalogDiff::Cli::Diffs.new(opts, @logger)
49+
result = testobj.diffs(from: @cat_compilation_dir_1, to: @cat_compilation_dir_2)
50+
expect(result).to eq([])
51+
r1 = %r{Varies_Due_To_Compilation_Dir_2\[/aldsfjalkfjalksfd/path/to/catalog2/dflkjasfkljasdf\].*Suppressed}
52+
expect(@logger_str.string).to match(r1)
53+
r2 = %r{Varies_Due_To_Compilation_Dir_2\[/aldsfjalkfjalksfd/path/to/catalog1/dflkjasfkljasdf\].*Suppressed}
54+
expect(@logger_str.string).to match(r2)
55+
end
56+
57+
it 'should remove ~ changes due to compilation dirs' do
58+
opts = { ignore:
59+
[
60+
{ type: 'Varies_Due_To_Compilation_Dir_1' },
61+
{ type: 'Varies_Due_To_Compilation_Dir_2' },
62+
{ type: 'Varies_Due_To_Compilation_Dir_4' },
63+
{ attr: "parameters\fdir_in_first_cat" },
64+
{ attr: "parameters\fdir_in_second_cat" }
65+
] }
66+
testobj = OctocatalogDiff::Cli::Diffs.new(opts, @logger)
67+
result = testobj.diffs(from: @cat_compilation_dir_1, to: @cat_compilation_dir_2)
68+
expect(result).to eq([])
69+
expect(@logger_str.string).to match(/WARN -- : Resource key.*parameters => dir .*Suppressed/)
70+
expect(@logger_str.string).to match(/WARN -- : Resource key.*parameters => dir_in_middle .*Suppressed/)
71+
end
72+
73+
it 'should warn but not remove non-matching ! changes due to compilation dirs' do
74+
opts = { ignore:
75+
[
76+
{ type: 'Varies_Due_To_Compilation_Dir_1' },
77+
{ type: 'Varies_Due_To_Compilation_Dir_2' },
78+
{ type: 'Varies_Due_To_Compilation_Dir_4' },
79+
{ attr: "parameters\fdir" },
80+
{ attr: "parameters\fdir_in_middle" }
81+
] }
82+
testobj = OctocatalogDiff::Cli::Diffs.new(opts, @logger)
83+
result = testobj.diffs(from: @cat_compilation_dir_1, to: @cat_compilation_dir_2)
84+
expect(result.size).to eq(2)
85+
common_str = "Varies_Due_To_Compilation_Dir_3\fCommon Title\fparameters\f"
86+
expect(result[0][1]).to eq("#{common_str}dir_in_first_cat")
87+
expect(result[1][1]).to eq("#{common_str}dir_in_second_cat")
88+
expect(@logger_str.string).to match(/WARN.+Varies_Due_To_Compilation_Dir_3\[Common Title\] parameters => dir.*verify/)
89+
end
90+
91+
it 'should warn but not remove non-matching ~ changes due to compilation dirs' do
92+
opts = { ignore:
93+
[
94+
{ type: 'Varies_Due_To_Compilation_Dir_1' },
95+
{ type: 'Varies_Due_To_Compilation_Dir_2' },
96+
{ type: 'Varies_Due_To_Compilation_Dir_3' }
97+
] }
98+
testobj = OctocatalogDiff::Cli::Diffs.new(opts, @logger)
99+
result = testobj.diffs(from: @cat_compilation_dir_1, to: @cat_compilation_dir_2)
100+
answer = [[
101+
'~',
102+
"Varies_Due_To_Compilation_Dir_4\fCommon Title\fparameters\fdir",
103+
'/path/to/catalog1/onetime',
104+
'/path/to/catalog2/twotimes',
105+
{ 'file' => nil, 'line' => nil },
106+
{ 'file' => nil, 'line' => nil }
107+
]]
108+
expect(result).to eq(answer)
109+
expect(@logger_str.string).to match(/WARN -- : Resource key Varies_Due_To_Compilation_Dir_4.*please verify/)
110+
end
111+
end

spec/octocatalog-diff/tests/catalog-diff/filter_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ class OctocatalogDiff::CatalogDiff::Filter::Fake2 < OctocatalogDiff::CatalogDiff
2222
describe '#apply_filters' do
2323
it 'should call self.filter() with appropriate options for each class' do
2424
result = [false]
25-
options = { 'Fake1' => { foo: 'bar' } }
25+
options = { foo: 'bar' }
2626
classes = %w(Fake1 Fake2)
2727
expect_any_instance_of(@class_1).to receive(:'filtered?').with(false, foo: 'bar').and_return(false)
28-
expect_any_instance_of(@class_2).to receive(:'filtered?').with(false, {}).and_return(false)
28+
expect_any_instance_of(@class_2).to receive(:'filtered?').with(false, foo: 'bar').and_return(false)
2929
expect { described_class.apply_filters(result, classes, options) }.not_to raise_error
3030
expect(result).to eq([false])
3131
end

0 commit comments

Comments
 (0)