# frozen_string_literal: true require 'tempfile' describe Nanoc::Int::Checksummer::VerboseDigest do let(:digest) { described_class.new } it 'concatenates' do digest.update('foo') digest.update('bar') expect(digest.to_s).to eql('foobar') end end describe Nanoc::Int::Checksummer::CompactDigest do let(:digest) { described_class.new } it 'uses SHA1 and Base64' do digest.update('foo') digest.update('bar') expect(digest.to_s).to eql(Digest::SHA1.base64digest('foobar')) end end describe Nanoc::Int::Checksummer do subject { described_class.calc(obj, Nanoc::Int::Checksummer::VerboseDigest) } describe '.calc_for_each_attribute_of' do let(:obj) { Nanoc::Int::Item.new('asdf', { 'foo' => 'bar' }, '/foo.md') } context 'compact' do subject do described_class.calc_for_each_attribute_of(obj) end it { is_expected.to have_key(:foo) } end context 'verbose' do subject do described_class.calc_for_each_attribute_of(obj, Nanoc::Int::Checksummer::VerboseDigest) end it { is_expected.to eq(foo: 'String') } end end context 'String' do let(:obj) { 'hello' } it { is_expected.to eql('String') } end context 'Symbol' do let(:obj) { :hello } it { is_expected.to eql('Symbol') } end context 'nil' do let(:obj) { nil } it { is_expected.to eql('NilClass<>') } end context 'true' do let(:obj) { true } it { is_expected.to eql('TrueClass<>') } end context 'false' do let(:obj) { false } it { is_expected.to eql('FalseClass<>') } end context 'Array' do let(:obj) { %w[hello goodbye] } it { is_expected.to eql('Array,String,>') } context 'different order' do let(:obj) { %w[goodbye hello] } it { is_expected.to eql('Array,String,>') } end context 'recursive' do let(:obj) { [].tap { |arr| arr << ['hello', arr] } } it { is_expected.to eql('Array,Array,>,>') } end context 'non-serializable' do let(:obj) { [-> {}] } it { is_expected.to match(/\AArray>,>\z/) } end end context 'Hash' do let(:obj) { { 'a' => 'foo', 'b' => 'bar' } } it { is_expected.to eql('Hash=String,String=String,>') } context 'different order' do let(:obj) { { 'b' => 'bar', 'a' => 'foo' } } it { is_expected.to eql('Hash=String,String=String,>') } end context 'non-serializable' do let(:obj) { { 'a' => -> {} } } it { is_expected.to match(/\AHash=Proc<#>,>\z/) } end context 'recursive values' do let(:obj) { {}.tap { |hash| hash['a'] = hash } } it { is_expected.to eql('Hash=Hash,>') } end context 'recursive keys' do let(:obj) { {}.tap { |hash| hash[hash] = 'hello' } } it { is_expected.to eql('Hash=String,>') } end end context 'Pathname' do let(:obj) { ::Pathname.new(filename) } let(:filename) { '/tmp/whatever' } let(:mtime) { 200 } let(:data) { 'stuffs' } before do FileUtils.mkdir_p(File.dirname(filename)) File.write(filename, data) File.utime(mtime, mtime, filename) end it { is_expected.to eql('Pathname<6-200>') } context 'does not exist' do before do FileUtils.rm_rf(filename) end it { is_expected.to eql('Pathname') } end context 'different data' do let(:data) { 'other stuffs :o' } it { is_expected.to eql('Pathname<15-200>') } end end context 'Time' do let(:obj) { Time.at(111_223) } it { is_expected.to eql('Time<111223>') } end context 'Float' do let(:obj) { 3.14 } it { is_expected.to eql('Float<3.14>') } end context 'Fixnum/Integer' do let(:obj) { 3 } it { is_expected.to match(/\A(Integer|Fixnum)<3>\z/) } end context 'Nanoc::Identifier' do let(:obj) { Nanoc::Identifier.new('/foo.md') } it { is_expected.to eql('Nanoc::Identifier>') } end context 'Nanoc::RuleDSL::RulesCollection' do let(:obj) do Nanoc::RuleDSL::RulesCollection.new.tap { |rc| rc.data = data } end let(:data) { 'STUFF!' } it { is_expected.to eql('Nanoc::RuleDSL::RulesCollection>') } end context 'Nanoc::Int::CodeSnippet' do let(:obj) { Nanoc::Int::CodeSnippet.new('asdf', '/bob.rb') } it { is_expected.to eql('Nanoc::Int::CodeSnippet>') } end context 'Nanoc::Int::Configuration' do let(:obj) { Nanoc::Int::Configuration.new(hash: { 'foo' => 'bar' }) } it { is_expected.to eql('Nanoc::Int::Configuration=String,>') } end context 'Nanoc::Int::Item' do let(:obj) { Nanoc::Int::Item.new('asdf', { 'foo' => 'bar' }, '/foo.md') } it { is_expected.to eql('Nanoc::Int::Item>,attributes=Hash=String,>,identifier=Nanoc::Identifier>>') } context 'binary' do let(:filename) { File.expand_path('foo.dat') } let(:content) { Nanoc::Int::BinaryContent.new(filename) } let(:obj) { Nanoc::Int::Item.new(content, { 'foo' => 'bar' }, '/foo.md') } let(:mtime) { 200 } let(:data) { 'stuffs' } before do File.write(content.filename, data) File.utime(mtime, mtime, content.filename) end it { is_expected.to eql('Nanoc::Int::Item>,attributes=Hash=String,>,identifier=Nanoc::Identifier>>') } end context 'recursive attributes' do before do obj.attributes[:foo] = obj end it { is_expected.to eql('Nanoc::Int::Item>,attributes=Hash=Nanoc::Int::Item,>,identifier=Nanoc::Identifier>>') } end context 'with checksum' do let(:obj) { Nanoc::Int::Item.new('asdf', { 'foo' => 'bar' }, '/foo.md', checksum_data: 'abcdef') } it { is_expected.to eql('Nanoc::Int::Item') } end context 'with content checksum' do let(:obj) { Nanoc::Int::Item.new('asdf', { 'foo' => 'bar' }, '/foo.md', content_checksum_data: 'con-cs') } it { is_expected.to eql('Nanoc::Int::Item=String,>,identifier=Nanoc::Identifier>>') } end context 'with attributes checksum' do let(:obj) { Nanoc::Int::Item.new('asdf', { 'foo' => 'bar' }, '/foo.md', attributes_checksum_data: 'attr-cs') } it { is_expected.to eql('Nanoc::Int::Item>,attributes_checksum_data=attr-cs,identifier=Nanoc::Identifier>>') } end end context 'Nanoc::Int::Layout' do let(:obj) { Nanoc::Int::Layout.new('asdf', { 'foo' => 'bar' }, '/foo.md') } it { is_expected.to eql('Nanoc::Int::Layout>,attributes=Hash=String,>,identifier=Nanoc::Identifier>>') } context 'recursive attributes' do before do obj.attributes[:foo] = obj end it { is_expected.to eql('Nanoc::Int::Layout>,attributes=Hash=Nanoc::Int::Layout,>,identifier=Nanoc::Identifier>>') } end context 'with checksum' do let(:obj) { Nanoc::Int::Layout.new('asdf', { 'foo' => 'bar' }, '/foo.md', checksum_data: 'abcdef') } it { is_expected.to eql('Nanoc::Int::Layout') } end end context 'Nanoc::ItemWithRepsView' do let(:obj) { Nanoc::ItemWithRepsView.new(item, nil) } let(:item) { Nanoc::Int::Item.new('asdf', {}, '/foo.md') } it { is_expected.to eql('Nanoc::ItemWithRepsView>,attributes=Hash<>,identifier=Nanoc::Identifier>>>') } end context 'Nanoc::Int::ItemRep' do let(:obj) { Nanoc::Int::ItemRep.new(item, :pdf) } let(:item) { Nanoc::Int::Item.new('asdf', {}, '/foo.md') } it { is_expected.to eql('Nanoc::Int::ItemRep>,attributes=Hash<>,identifier=Nanoc::Identifier>>,name=Symbol>') } end context 'Nanoc::ItemRepView' do let(:obj) { Nanoc::ItemRepView.new(rep, :_unused_context) } let(:rep) { Nanoc::Int::ItemRep.new(item, :pdf) } let(:item) { Nanoc::Int::Item.new('asdf', {}, '/foo.md') } it { is_expected.to eql('Nanoc::ItemRepView>,attributes=Hash<>,identifier=Nanoc::Identifier>>,name=Symbol>>') } end context 'Nanoc::ItemWithoutRepsView' do let(:obj) { Nanoc::ItemWithoutRepsView.new(item, nil) } let(:item) { Nanoc::Int::Item.new('asdf', {}, '/foo.md') } it { is_expected.to eql('Nanoc::ItemWithoutRepsView>,attributes=Hash<>,identifier=Nanoc::Identifier>>>') } end context 'Nanoc::LayoutView' do let(:obj) { Nanoc::LayoutView.new(layout, nil) } let(:layout) { Nanoc::Int::Layout.new('asdf', {}, '/foo.md') } it { is_expected.to eql('Nanoc::LayoutView>,attributes=Hash<>,identifier=Nanoc::Identifier>>>') } end context 'Nanoc::ConfigView' do let(:obj) { Nanoc::ConfigView.new(config, nil) } let(:config) { Nanoc::Int::Configuration.new(hash: { 'foo' => 'bar' }) } it { is_expected.to eql('Nanoc::ConfigView=String,>>') } end context 'Nanoc::ItemCollectionWithRepsView' do let(:obj) { Nanoc::ItemCollectionWithRepsView.new(wrapped, nil) } let(:config) { Nanoc::Int::Configuration.new(hash: { 'foo' => 'bar' }) } let(:wrapped) do Nanoc::Int::IdentifiableCollection.new( config, [ Nanoc::Int::Item.new('foo', {}, '/foo.md'), Nanoc::Int::Item.new('bar', {}, '/foo.md'), ], ) end it { is_expected.to eql('Nanoc::ItemCollectionWithRepsView>,attributes=Hash<>,identifier=Nanoc::Identifier>>,Nanoc::Int::Item>,attributes=Hash<>,identifier=Nanoc::Identifier>>,>>') } end context 'Nanoc::ItemCollectionWithoutRepsView' do let(:obj) { Nanoc::ItemCollectionWithoutRepsView.new(wrapped, nil) } let(:config) { Nanoc::Int::Configuration.new(hash: { 'foo' => 'bar' }) } let(:wrapped) do Nanoc::Int::IdentifiableCollection.new( config, [ Nanoc::Int::Item.new('foo', {}, '/foo.md'), Nanoc::Int::Item.new('bar', {}, '/foo.md'), ], ) end it { is_expected.to eql('Nanoc::ItemCollectionWithoutRepsView>,attributes=Hash<>,identifier=Nanoc::Identifier>>,Nanoc::Int::Item>,attributes=Hash<>,identifier=Nanoc::Identifier>>,>>') } end context 'Nanoc::RuleDSL::RuleContext' do let(:obj) { Nanoc::RuleDSL::RuleContext.new(rep: rep, site: site, executor: executor, view_context: view_context) } let(:rep) { Nanoc::Int::ItemRep.new(item, :pdf) } let(:item) { Nanoc::Int::Item.new('stuff', {}, '/stuff.md') } let(:site) do Nanoc::Int::Site.new( config: config, code_snippets: code_snippets, data_source: Nanoc::Int::InMemDataSource.new(items, layouts), ) end let(:config) { Nanoc::Int::Configuration.new(hash: { 'foo' => 'bar' }) } let(:code_snippets) { [Nanoc::Int::CodeSnippet.new('asdf', '/bob.rb')] } let(:items) { [item] } let(:layouts) { [Nanoc::Int::Layout.new('asdf', {}, '/foo.md')] } let(:executor) { :_unused_ } let(:view_context) { :_unused_ } let(:expected_item_checksum) { 'Nanoc::Int::Item>,attributes=Hash<>,identifier=Nanoc::Identifier>>' } let(:expected_item_rep_checksum) { 'Nanoc::Int::ItemRep>' } let(:expected_layout_checksum) { 'Nanoc::Int::Layout>,attributes=Hash<>,identifier=Nanoc::Identifier>>' } let(:expected_config_checksum) { 'Nanoc::Int::Configuration=String,>' } let(:expected_checksum) do [ 'Nanoc::RuleDSL::RuleContext<', 'item=', 'Nanoc::ItemWithoutRepsView<' + expected_item_checksum + '>', ',rep=', 'Nanoc::ItemRepView<' + expected_item_rep_checksum + '>', ',items=', 'Nanoc::ItemCollectionWithoutRepsView>', ',layouts=', 'Nanoc::LayoutCollectionView>', ',config=', 'Nanoc::ConfigView<' + expected_config_checksum + '>', '>', ].join('') end it { is_expected.to eql(expected_checksum) } end context 'Nanoc::Int::Context' do let(:obj) { Nanoc::Int::Context.new(foo: 123) } it { is_expected.to match(/\ANanoc::Int::Context<@foo=(Fixnum|Integer)<123>,>\z/) } end context 'Sass::Importers::Filesystem' do let(:obj) { Sass::Importers::Filesystem.new('/foo') } before { require 'sass' } it { is_expected.to eql('Sass::Importers::Filesystem') } end context 'other marshal-able classes' do let(:obj) { klass.new('hello') } let(:klass) do Class.new do def initialize(a) @a = a end end end it { is_expected.to match(/\A#<.*>\z/) } end context 'other non-marshal-able classes' do let(:obj) { proc {} } it { is_expected.to match(/\AProc<#>\z/) } end end