# frozen_string_literal: true require 'spec_helper' require 'image_optim/config' describe ImageOptim::Config do before do stub_const('IOConfig', ImageOptim::Config) end describe '#assert_no_unused_options!' do before do allow(IOConfig).to receive(:read_options).and_return({}) end it 'does not raise when no unused options' do config = IOConfig.new({}) config.assert_no_unused_options! end it 'raises when there are unused options' do config = IOConfig.new(unused: true) expect do config.assert_no_unused_options! end.to raise_error(ImageOptim::ConfigurationError) end end describe '#nice' do before do allow(IOConfig).to receive(:read_options).and_return({}) end it 'is 10 by default' do config = IOConfig.new({}) expect(config.nice).to eq(10) end it 'is 0 if disabled' do config = IOConfig.new(nice: false) expect(config.nice).to eq(0) end it 'converts value to number' do config = IOConfig.new(nice: '13') expect(config.nice).to eq(13) end end describe '#threads' do before do allow(IOConfig).to receive(:read_options).and_return({}) end it 'is processor_count by default' do config = IOConfig.new({}) allow(config).to receive(:processor_count).and_return(13) expect(config.threads).to eq(13) end it 'is 1 if disabled' do config = IOConfig.new(threads: false) expect(config.threads).to eq(1) end it 'converts value to number' do config = IOConfig.new(threads: '616') expect(config.threads).to eq(616) end end describe '#timeout' do before do allow(IOConfig).to receive(:read_options).and_return({}) end it 'is nil by default' do config = IOConfig.new({}) expect(config.timeout).to eq(nil) end it 'converts value to a float' do config = IOConfig.new(timeout: '15.1') expect(config.timeout).to eq(15.1) end end describe '#cache_dir' do before do allow(IOConfig).to receive(:read_options).and_return({}) end it 'is nil by default' do config = IOConfig.new({}) expect(config.cache_dir).to be nil end it 'is nil if set to the empty string' do config = IOConfig.new(cache_dir: '') expect(config.cache_dir).to be nil end end describe '#cache_worker_digests' do before do allow(IOConfig).to receive(:read_options).and_return({}) end it 'is false by default' do config = IOConfig.new({}) expect(config.cache_worker_digests).to be false end end describe '#for_worker' do before do allow(IOConfig).to receive(:read_options).and_return({}) stub_const('Abc', Class.new do def self.bin_sym :abc end end) end it 'returns empty hash by default' do config = IOConfig.new({}) expect(config.for_worker(Abc)).to eq({}) end it 'returns passed hash' do config = IOConfig.new(abc: {option: true}) expect(config.for_worker(Abc)).to eq(option: true) end it 'returns {:disable => true} for false' do config = IOConfig.new(abc: false) expect(config.for_worker(Abc)).to eq(disable: true) end it 'raises on unknown option' do config = IOConfig.new(abc: 13) expect do config.for_worker(Abc) end.to raise_error(ImageOptim::ConfigurationError) end end describe '#initialize' do it 'reads options from default locations' do expect(IOConfig).to receive(:read_options). with(IOConfig::GLOBAL_PATH).and_return(a: 1, b: 2, c: 3) expect(IOConfig).to receive(:read_options). with(IOConfig::LOCAL_PATH).and_return(a: 10, b: 20) config = IOConfig.new(a: 100) expect(config.get!(:a)).to eq(100) expect(config.get!(:b)).to eq(20) expect(config.get!(:c)).to eq(3) config.assert_no_unused_options! end it 'does not read options with empty config_paths' do expect(IOConfig).not_to receive(:read_options) config = IOConfig.new(config_paths: []) config.assert_no_unused_options! end it 'reads options from specified paths' do expect(IOConfig).to receive(:read_options). with('/etc/image_optim.yml').and_return(a: 1, b: 2, c: 3) expect(IOConfig).to receive(:read_options). with('config/image_optim.yml').and_return(a: 10, b: 20) config = IOConfig.new(a: 100, config_paths: %w[ /etc/image_optim.yml config/image_optim.yml ]) expect(config.get!(:a)).to eq(100) expect(config.get!(:b)).to eq(20) expect(config.get!(:c)).to eq(3) config.assert_no_unused_options! end it 'converts config_paths to array' do expect(IOConfig).to receive(:read_options). with('config/image_optim.yml').and_return({}) config = IOConfig.new(config_paths: 'config/image_optim.yml') config.assert_no_unused_options! end end describe '.read_options' do let(:path){ double(:path) } let(:full_path){ double(:full_path) } it 'warns if expand path fails' do expect(IOConfig).to receive(:warn) expect(File).to receive(:expand_path). with(path).and_raise(ArgumentError) expect(File).not_to receive(:size?) expect(IOConfig.read_options(path)).to eq({}) end it 'returns empty hash if path is not a file or is an empty file' do expect(IOConfig).not_to receive(:warn) expect(File).to receive(:expand_path). with(path).and_return(full_path) expect(File).to receive(:size?). with(full_path).and_return(false) expect(IOConfig.read_options(path)).to eq({}) end it 'returns hash with deep symbolised keys from reader' do stringified = {'config' => {'this' => true}} symbolized = {config: {this: true}} expect(IOConfig).not_to receive(:warn) expect(File).to receive(:expand_path). with(path).and_return(full_path) expect(File).to receive(:size?). with(full_path).and_return(true) expect(IOConfig).to receive(:load_yaml_file). with(full_path).and_return(stringified) expect(IOConfig.read_options(path)).to eq(symbolized) end it 'warns and returns an empty hash if reader returns non hash' do expect(IOConfig).to receive(:warn) expect(File).to receive(:expand_path). with(path).and_return(full_path) expect(File).to receive(:size?). with(full_path).and_return(true) expect(IOConfig).to receive(:load_yaml_file). with(full_path).and_return([:config]) expect(IOConfig.read_options(path)).to eq({}) end it 'warns and returns an empty hash if reader raises exception' do expect(IOConfig).to receive(:warn) expect(File).to receive(:expand_path). with(path).and_return(full_path) expect(File).to receive(:size?). with(full_path).and_return(true) expect(IOConfig).to receive(:load_yaml_file). with(full_path).and_raise expect(IOConfig.read_options(path)).to eq({}) end end describe '.load_yaml_file' do describe 'selecting method' do let(:path){ 'foo' } let(:result){ 'bar' } let(:yaml){ double } before do stub_const('YAML', yaml) end it 'uses YAML.safe_load_file if available' do expect(yaml).to receive(:safe_load_file). with(path, permitted_classes: [Range]).and_return(result) expect(IOConfig.load_yaml_file(path)).to eq(result) end it 'uses YAML.load if safe_load_file is not available' do expect(yaml).to receive(:load_file). with(path).and_return(result) expect(IOConfig.load_yaml_file(path)).to eq(result) end end it 'handles yaml that includes `!ruby/range`' do path = 'spec/files/config_with_range.yaml' expect(IOConfig.load_yaml_file(path)).to eq({ 'range' => 80..99, 'number' => 3, 'string' => 'foo', }) end end end