# encoding: UTF-8 require 'spec_helper' describe "FFI::MemoryPointer", :if => Puppet::Util::Platform.windows? do # use 2 bad bytes at end so we have even number of bytes / characters let (:bad_string) { "hello invalid world".encode(Encoding::UTF_16LE) + "\xDD\xDD".force_encoding(Encoding::UTF_16LE) } let (:bad_string_bytes) { bad_string.bytes.to_a } context "read_wide_string" do let (:string) { "foo_bar" } it "should properly roundtrip a given string" do read_string = nil FFI::MemoryPointer.from_string_to_wide_string(string) do |ptr| read_string = ptr.read_wide_string(string.length) end expect(read_string).to eq(string) end it "should return a given string in UTF-8" do read_string = nil FFI::MemoryPointer.from_string_to_wide_string(string) do |ptr| read_string = ptr.read_wide_string(string.length) end expect(read_string.encoding).to eq(Encoding::UTF_8) end it "should raise an error and emit a debug message when receiving a string containing invalid bytes in the destination encoding" do # enable a debug output sink to local string array Puppet.debug = true arraydest = [] Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(arraydest)) read_string = nil expect { FFI::MemoryPointer.new(:byte, bad_string_bytes.count) do |ptr| # uchar here is synonymous with byte ptr.put_array_of_uchar(0, bad_string_bytes) read_string = ptr.read_wide_string(bad_string.length) end }.to raise_error(Encoding::InvalidByteSequenceError) expect(read_string).to be_nil expect(arraydest.last.message).to eq("Unable to convert value #{bad_string.dump} to encoding UTF-8 due to #<Encoding::InvalidByteSequenceError: \"\\xDD\\xDD\" on UTF-16LE>") end it "should not raise an error when receiving a string containing invalid bytes in the destination encoding, when specifying :invalid => :replace" do read_string = nil FFI::MemoryPointer.new(:byte, bad_string_bytes.count) do |ptr| # uchar here is synonymous with byte ptr.put_array_of_uchar(0, bad_string_bytes) read_string = ptr.read_wide_string(bad_string.length, Encoding::UTF_8, :invalid => :replace) end expect(read_string).to eq("hello invalid world\uFFFD") end end context "read_arbitrary_wide_string_up_to" do let (:string) { "foo_bar" } let (:single_null_string) { string + "\x00" } let (:double_null_string) { string + "\x00\x00" } it "should read a short single null terminated string" do read_string = nil FFI::MemoryPointer.from_string_to_wide_string(single_null_string) do |ptr| read_string = ptr.read_arbitrary_wide_string_up_to() end expect(read_string).to eq(string) end it "should read a short double null terminated string" do read_string = nil FFI::MemoryPointer.from_string_to_wide_string(double_null_string) do |ptr| read_string = ptr.read_arbitrary_wide_string_up_to(512, :double_null) end expect(read_string).to eq(string) end it "should return a string of max_length characters when specified" do read_string = nil FFI::MemoryPointer.from_string_to_wide_string(single_null_string) do |ptr| read_string = ptr.read_arbitrary_wide_string_up_to(3) end expect(read_string).to eq(string[0..2]) end it "should return wide strings in UTF-8" do read_string = nil FFI::MemoryPointer.from_string_to_wide_string(string) do |ptr| read_string = ptr.read_arbitrary_wide_string_up_to() end expect(read_string.encoding).to eq(Encoding::UTF_8) end it "should not raise an error when receiving a string containing invalid bytes in the destination encoding, when specifying :invalid => :replace" do read_string = nil FFI::MemoryPointer.new(:byte, bad_string_bytes.count) do |ptr| # uchar here is synonymous with byte ptr.put_array_of_uchar(0, bad_string_bytes) read_string = ptr.read_arbitrary_wide_string_up_to(ptr.size / 2, :single_null, :invalid => :replace) end expect(read_string).to eq("hello invalid world\uFFFD") end end end