require 'spec_helper'
require 'puppet/util/plist'
require 'puppet_spec/files'
describe Puppet::Util::Plist, :if => Puppet.features.cfpropertylist? do
include PuppetSpec::Files
let(:valid_xml_plist) do
'
LastUsedPrinters
Network
10.85.132.1
PrinterID
baskerville_corp_puppetlabs_net
Network
10.14.96.1
PrinterID
Statler
'
end
let(:invalid_xml_plist) do
'
LastUsedPrinters
Network
10.85.132.1
PrinterID
baskerville_corp_puppetlabs_net
Network
10.14.96.1
PrinterID
Statler
'
end
let(:non_plist_data) do
"Take my love, take my land
Take me where I cannot stand
I don't care, I'm still free
You can't take the sky from me."
end
let(:binary_data) do
"\xCF\xFA\xED\xFE\a\u0000\u0000\u0001\u0003\u0000\u0000\x80\u0002\u0000\u0000\u0000\u0012\u0000\u0000\u0000\b"
end
let(:valid_xml_plist_hash) { {"LastUsedPrinters"=>[{"Network"=>"10.85.132.1", "PrinterID"=>"baskerville_corp_puppetlabs_net"}, {"Network"=>"10.14.96.1", "PrinterID"=>"Statler"}]} }
let(:plist_path) { file_containing('sample.plist', valid_xml_plist) }
let(:binary_plist_magic_number) { 'bplist00' }
let(:bad_xml_doctype) { '' }
describe "#read_plist_file" do
it "calls #convert_cfpropertylist_to_native_types on a plist object when a valid binary plist is read" do
allow(subject).to receive(:read_file_with_offset).with(plist_path, 8).and_return(binary_plist_magic_number)
allow(subject).to receive(:new_cfpropertylist).with({:file => plist_path}).and_return('plist_object')
expect(subject).to receive(:convert_cfpropertylist_to_native_types).with('plist_object').and_return('plist_hash')
expect(subject.read_plist_file(plist_path)).to eq('plist_hash')
end
it "returns a valid hash when a valid XML plist is read" do
allow(subject).to receive(:read_file_with_offset).with(plist_path, 8).and_return('notbinary')
allow(subject).to receive(:open_file_with_args).with(plist_path, 'r:UTF-8').and_return(valid_xml_plist)
expect(subject.read_plist_file(plist_path)).to eq(valid_xml_plist_hash)
end
it "raises a debug message and replaces a bad XML plist doctype should one be encountered" do
allow(subject).to receive(:read_file_with_offset).with(plist_path, 8).and_return('notbinary')
allow(subject).to receive(:open_file_with_args).with(plist_path, 'r:UTF-8').and_return(bad_xml_doctype)
expect(subject).to receive(:new_cfpropertylist).with({:data => good_xml_doctype}).and_return('plist_object')
allow(subject).to receive(:convert_cfpropertylist_to_native_types).with('plist_object').and_return('plist_hash')
expect(Puppet).to receive(:debug).with("Had to fix plist with incorrect DOCTYPE declaration: #{plist_path}")
expect(subject.read_plist_file(plist_path)).to eq('plist_hash')
end
it "attempts to read pure xml using plutil when reading an improperly formatted service plist" do
allow(subject).to receive(:read_file_with_offset).with(plist_path, 8).and_return('notbinary')
allow(subject).to receive(:open_file_with_args).with(plist_path, 'r:UTF-8').and_return(invalid_xml_plist)
expect(Puppet).to receive(:debug).with(/^Failed with CFFormatError/)
expect(Puppet).to receive(:debug).with("Plist #{plist_path} ill-formatted, converting with plutil")
expect(Puppet::Util::Execution).to receive(:execute)
.with(['/usr/bin/plutil', '-convert', 'xml1', '-o', '-', plist_path],
{:failonfail => true, :combine => true})
.and_return(Puppet::Util::Execution::ProcessOutput.new(valid_xml_plist, 0))
expect(subject.read_plist_file(plist_path)).to eq(valid_xml_plist_hash)
end
it "returns nil when direct parsing and plutil conversion both fail" do
allow(subject).to receive(:read_file_with_offset).with(plist_path, 8).and_return('notbinary')
allow(subject).to receive(:open_file_with_args).with(plist_path, 'r:UTF-8').and_return(non_plist_data)
expect(Puppet).to receive(:debug).with(/^Failed with (CFFormatError|NoMethodError)/)
expect(Puppet).to receive(:debug).with("Plist #{plist_path} ill-formatted, converting with plutil")
expect(Puppet::Util::Execution).to receive(:execute)
.with(['/usr/bin/plutil', '-convert', 'xml1', '-o', '-', plist_path],
{:failonfail => true, :combine => true})
.and_raise(Puppet::ExecutionFailure, 'boom')
expect(subject.read_plist_file(plist_path)).to eq(nil)
end
it "returns nil when file is a non-plist binary blob" do
allow(subject).to receive(:read_file_with_offset).with(plist_path, 8).and_return('notbinary')
allow(subject).to receive(:open_file_with_args).with(plist_path, 'r:UTF-8').and_return(binary_data)
expect(Puppet).to receive(:debug).with(/^Failed with (CFFormatError|ArgumentError)/)
expect(Puppet).to receive(:debug).with("Plist #{plist_path} ill-formatted, converting with plutil")
expect(Puppet::Util::Execution).to receive(:execute)
.with(['/usr/bin/plutil', '-convert', 'xml1', '-o', '-', plist_path],
{:failonfail => true, :combine => true})
.and_raise(Puppet::ExecutionFailure, 'boom')
expect(subject.read_plist_file(plist_path)).to eq(nil)
end
end
describe "#parse_plist" do
it "returns a valid hash when a valid XML plist is provided" do
expect(subject.parse_plist(valid_xml_plist)).to eq(valid_xml_plist_hash)
end
it "raises a debug message and replaces a bad XML plist doctype should one be encountered" do
expect(subject).to receive(:new_cfpropertylist).with({:data => good_xml_doctype}).and_return('plist_object')
allow(subject).to receive(:convert_cfpropertylist_to_native_types).with('plist_object')
expect(Puppet).to receive(:debug).with("Had to fix plist with incorrect DOCTYPE declaration: #{plist_path}")
subject.parse_plist(bad_xml_doctype, plist_path)
end
it "raises a debug message with malformed plist" do
allow(subject).to receive(:convert_cfpropertylist_to_native_types).with('plist_object')
expect(Puppet).to receive(:debug).with(/^Failed with CFFormatError/)
subject.parse_plist("Foo")
end
end
end