text:
require_relative 'spec_helper'

unless ENV['CI']
  describe JSON::LD do
    describe "test suite" do
      require_relative 'suite_helper'
      m = Fixtures::SuiteTest::Manifest.open("#{Fixtures::SuiteTest::SUITE}toRdf-manifest.jsonld")
      describe m.name do
        m.entries.each do |t|
          specify "#{t.property('@id')}: #{t.name}#{' (negative test)' unless t.positiveTest?}" do
            pending "Generalized RDF" if t.options[:produceGeneralizedRdf]
            pending "RDF-star" if t.property('@id') == '#te122'
            if %w[#t0118].include?(t.property('@id'))
              expect { t.run self }.to write(/Statement .* is invalid/).to(:error)
            elsif %w[#te075].include?(t.property('@id'))
              expect { t.run self }.to write(/is invalid/).to(:error)
            elsif %w[#te005 #tpr34 #tpr35 #tpr36 #tpr37 #tpr38 #tpr39 #te119 #te120].include?(t.property('@id'))
              expect { t.run self }.to write("beginning with '@' are reserved for future use").to(:error)
            elsif %w[#te068].include?(t.property('@id'))
              expect { t.run self }.to write("[DEPRECATION]").to(:error)
            elsif %w[#twf05].include?(t.property('@id'))
              expect { t.run self }.to write("@language must be valid BCP47").to(:error)
            else
              expect { t.run self }.not_to write.to(:error)
            end
          end
        end
      end
    end
  end
end

repo_name: ruby-rdf/json-ld

repo_language: HTML

mime_type: text/plain


text:
# frozen_string_literal: true

require_relative 'spec_helper'
require 'rdf/spec/reader'

describe JSON::LD::Reader do
  let!(:doap) { File.expand_path('../etc/doap.jsonld', __dir__) }
  let!(:doap_nt) { File.expand_path('../etc/doap.nt', __dir__) }
  let!(:doap_count) { File.open(doap_nt).each_line.to_a.length }
  let(:logger) { RDF::Spec.logger }

  after { |example| puts logger if example.exception }

  it_behaves_like 'an RDF::Reader' do
    let(:reader_input) { File.read(doap) }
    let(:reader) { JSON::LD::Reader.new(reader_input) }
    let(:reader_count) { doap_count }
  end

  describe ".for" do
    [
      :jsonld,
      "etc/doap.jsonld",
      { file_name:      'etc/doap.jsonld' },
      { file_extension: 'jsonld' },
      { content_type:   'application/ld+json' },
      { content_type:   'application/x-ld+json' }
    ].each do |arg|
      it "discovers with #{arg.inspect}" do
        expect(RDF::Reader.for(arg)).to eq described_class
      end
    end
  end

  context "when validating", pending: ("JRuby support for jsonlint" if RUBY_ENGINE == "jruby") do
    it "detects invalid JSON" do
      expect do |b|
        described_class.new(StringIO.new(%({"a": "b", "a": "c"})), validate: true, logger: false).each_statement(&b)
      end.to raise_error(RDF::ReaderError)
    end
  end

  context :interface do
    {
      plain: '{
        "@context": {"foaf": "http://xmlns.com/foaf/0.1/"},
        "@id": "_:bnode1",
        "@type": "foaf:Person",
        "foaf:homepage": "http://example.com/bob/",
        "foaf:name": "Bob"
      }',
      leading_comment: '
      // A comment before content
      {
        "@context": {"foaf": "http://xmlns.com/foaf/0.1/"},
        "@id": "_:bnode1",
        "@type": "foaf:Person",
        "foaf:homepage": "http://example.com/bob/",
        "foaf:name": "Bob"
      }',
      script: '<script type="application/ld+json">
      {
        "@context": {"foaf": "http://xmlns.com/foaf/0.1/"},
        "@id": "_:bnode1",
        "@type": "foaf:Person",
        "foaf:homepage": "http://example.com/bob/",
        "foaf:name": "Bob"
      }
      </script>',
      script_comments: '<script type="application/ld+json">
      // A comment before content
      {
        "@context": {"foaf": "http://xmlns.com/foaf/0.1/"},
        "@id": "_:bnode1",
        "@type": "foaf:Person",
        "foaf:homepage": "http://example.com/bob/",
        "foaf:name": "Bob"
       }
      </script>'
    }.each do |variant, src|
      context variant do
        subject { src }

        describe "#initialize" do
          it "yields reader given string" do
            inner = double("inner")
            expect(inner).to receive(:called).with(JSON::LD::Reader)
            JSON::LD::Reader.new(subject) do |reader|
              inner.called(reader.class)
            end
          end

          it "yields reader given IO" do
            inner = double("inner")
            expect(inner).to receive(:called).with(JSON::LD::Reader)
            JSON::LD::Reader.new(StringIO.new(subject)) do |reader|
              inner.called(reader.class)
            end
          end

          it "returns reader" do
            expect(JSON::LD::Reader.new(subject)).to be_a(JSON::LD::Reader)
          end
        end

        describe "#each_statement" do
          it "yields statements" do
            inner = double("inner")
            expect(inner).to receive(:called).with(RDF::Statement).exactly(3)
            JSON::LD::Reader.new(subject).each_statement do |statement|
              inner.called(statement.class)
            end
          end
        end

        describe "#each_triple" do
          it "yields statements" do
            inner = double("inner")
            expect(inner).to receive(:called).exactly(3)
            JSON::LD::Reader.new(subject).each_triple do |subject, predicate, object|
              inner.called(subject.class, predicate.class, object.class)
            end
          end
        end
      end
    end
  end

  describe "Base IRI resolution" do
    # From https://gist.github.com/RubenVerborgh/39f0e8d63e33e435371a
    let(:json) do
      '[
      {
        "@context": {"@base": "http://a/bb/ccc/d;p?q", "urn:ex:p": {"@type": "@id"}},
        "@graph": [
          {"@id": "urn:ex:s001", "urn:ex:p": "gg:h"},
          {"@id": "urn:ex:s002", "urn:ex:p": "g"},
          {"@id": "urn:ex:s003", "urn:ex:p": "./g"},
          {"@id": "urn:ex:s004", "urn:ex:p": "g/"},
          {"@id": "urn:ex:s005", "urn:ex:p": "/g"},
          {"@id": "urn:ex:s006", "urn:ex:p": "//g"},
          {"@id": "urn:ex:s007", "urn:ex:p": "?y"},
          {"@id": "urn:ex:s008", "urn:ex:p": "g?y"},
          {"@id": "urn:ex:s009", "urn:ex:p": "#s"},
          {"@id": "urn:ex:s010", "urn:ex:p": "g#s"},
          {"@id": "urn:ex:s011", "urn:ex:p": "g?y#s"},
          {"@id": "urn:ex:s012", "urn:ex:p": ";x"},
          {"@id": "urn:ex:s013", "urn:ex:p": "g;x"},
          {"@id": "urn:ex:s014", "urn:ex:p": "g;x?y#s"},
          {"@id": "urn:ex:s015", "urn:ex:p": ""},
          {"@id": "urn:ex:s016", "urn:ex:p": "."},
          {"@id": "urn:ex:s017", "urn:ex:p": "./"},
          {"@id": "urn:ex:s018", "urn:ex:p": ".."},
          {"@id": "urn:ex:s019", "urn:ex:p": "../"},
          {"@id": "urn:ex:s020", "urn:ex:p": "../g"},
          {"@id": "urn:ex:s021", "urn:ex:p": "../.."},
          {"@id": "urn:ex:s022", "urn:ex:p": "../../"},
          {"@id": "urn:ex:s023", "urn:ex:p": "../../g"}
        ]
      },
      {
        "@context": {"@base": "http://a/bb/ccc/d;p?q", "urn:ex:p": {"@type": "@id"}},
        "@graph": [
          {"@id": "urn:ex:s024", "urn:ex:p": "../../../g"},
          {"@id": "urn:ex:s025", "urn:ex:p": "../../../../g"},
          {"@id": "urn:ex:s026", "urn:ex:p": "/./g"},
          {"@id": "urn:ex:s027", "urn:ex:p": "/../g"},
          {"@id": "urn:ex:s028", "urn:ex:p": "g."},
          {"@id": "urn:ex:s029", "urn:ex:p": ".g"},
          {"@id": "urn:ex:s030", "urn:ex:p": "g.."},
          {"@id": "urn:ex:s031", "urn:ex:p": "..g"},
          {"@id": "urn:ex:s032", "urn:ex:p": "./../g"},
          {"@id": "urn:ex:s033", "urn:ex:p": "./g/."},
          {"@id": "urn:ex:s034", "urn:ex:p": "g/./h"},
          {"@id": "urn:ex:s035", "urn:ex:p": "g/../h"},
          {"@id": "urn:ex:s036", "urn:ex:p": "g;x=1/./y"},
          {"@id": "urn:ex:s037", "urn:ex:p": "g;x=1/../y"},
          {"@id": "urn:ex:s038", "urn:ex:p": "g?y/./x"},
          {"@id": "urn:ex:s039", "urn:ex:p": "g?y/../x"},
          {"@id": "urn:ex:s040", "urn:ex:p": "g#s/./x"},
          {"@id": "urn:ex:s041", "urn:ex:p": "g#s/../x"},
          {"@id": "urn:ex:s042", "urn:ex:p": "http:g"}
        ]
      },
      {
        "@context": {"@base": "http://a/bb/ccc/d/", "urn:ex:p": {"@type": "@id"}},
        "@graph": [
          {"@id": "urn:ex:s043", "urn:ex:p": "gg:h"},
          {"@id": "urn:ex:s044", "urn:ex:p": "g"},
          {"@id": "urn:ex:s045", "urn:ex:p": "./g"},
          {"@id": "urn:ex:s046", "urn:ex:p": "g/"},
          {"@id": "urn:ex:s047", "urn:ex:p": "/g"},
          {"@id": "urn:ex:s048", "urn:ex:p": "//g"},
          {"@id": "urn:ex:s049", "urn:ex:p": "?y"},
          {"@id": "urn:ex:s050", "urn:ex:p": "g?y"},
          {"@id": "urn:ex:s051", "urn:ex:p": "#s"},
          {"@id": "urn:ex:s052", "urn:ex:p": "g#s"},
          {"@id": "urn:ex:s053", "urn:ex:p": "g?y#s"},
          {"@id": "urn:ex:s054", "urn:ex:p": ";x"},
          {"@id": "urn:ex:s055", "urn:ex:p": "g;x"},
          {"@id": "urn:ex:s056", "urn:ex:p": "g;x?y#s"},
          {"@id": "urn:ex:s057", "urn:ex:p": ""},
          {"@id": "urn:ex:s058", "urn:ex:p": "."},
          {"@id": "urn:ex:s059", "urn:ex:p": "./"},
          {"@id": "urn:ex:s060", "urn:ex:p": ".."},
          {"@id": "urn:ex:s061", "urn:ex:p": "../"},
          {"@id": "urn:ex:s062", "urn:ex:p": "../g"},
          {"@id": "urn:ex:s063", "urn:ex:p": "../.."},
          {"@id": "urn:ex:s064", "urn:ex:p": "../../"},
          {"@id": "urn:ex:s065", "urn:ex:p": "../../g"}
        ]
      },
      {
        "@context": {"@base": "http://a/bb/ccc/d/", "urn:ex:p": {"@type": "@id"}},
        "@graph": [
          {"@id": "urn:ex:s066", "urn:ex:p": "../../../g"},
          {"@id": "urn:ex:s067", "urn:ex:p": "../../../../g"},
          {"@id": "urn:ex:s068", "urn:ex:p": "/./g"},
          {"@id": "urn:ex:s069", "urn:ex:p": "/../g"},
          {"@id": "urn:ex:s070", "urn:ex:p": "g."},
          {"@id": "urn:ex:s071", "urn:ex:p": ".g"},
          {"@id": "urn:ex:s072", "urn:ex:p": "g.."},
          {"@id": "urn:ex:s073", "urn:ex:p": "..g"},
          {"@id": "urn:ex:s074", "urn:ex:p": "./../g"},
          {"@id": "urn:ex:s075", "urn:ex:p": "./g/."},
          {"@id": "urn:ex:s076", "urn:ex:p": "g/./h"},
          {"@id": "urn:ex:s077", "urn:ex:p": "g/../h"},
          {"@id": "urn:ex:s078", "urn:ex:p": "g;x=1/./y"},
          {"@id": "urn:ex:s079", "urn:ex:p": "g;x=1/../y"},
          {"@id": "urn:ex:s080", "urn:ex:p": "g?y/./x"},
          {"@id": "urn:ex:s081", "urn:ex:p": "g?y/../x"},
          {"@id": "urn:ex:s082", "urn:ex:p": "g#s/./x"},
          {"@id": "urn:ex:s083", "urn:ex:p": "g#s/../x"},
          {"@id": "urn:ex:s084", "urn:ex:p": "http:g"}
        ]
      },
      {
        "@context": {"@base": "http://a/bb/ccc/./d;p?q", "urn:ex:p": {"@type": "@id"}},
        "@graph": [
          {"@id": "urn:ex:s085", "urn:ex:p": "gg:h"},
          {"@id": "urn:ex:s086", "urn:ex:p": "g"},
          {"@id": "urn:ex:s087", "urn:ex:p": "./g"},
          {"@id": "urn:ex:s088", "urn:ex:p": "g/"},
          {"@id": "urn:ex:s089", "urn:ex:p": "/g"},
          {"@id": "urn:ex:s090", "urn:ex:p": "//g"},
          {"@id": "urn:ex:s091", "urn:ex:p": "?y"},
          {"@id": "urn:ex:s092", "urn:ex:p": "g?y"},
          {"@id": "urn:ex:s093", "urn:ex:p": "#s"},
          {"@id": "urn:ex:s094", "urn:ex:p": "g#s"},
          {"@id": "urn:ex:s095", "urn:ex:p": "g?y#s"},
          {"@id": "urn:ex:s096", "urn:ex:p": ";x"},
          {"@id": "urn:ex:s097", "urn:ex:p": "g;x"},
          {"@id": "urn:ex:s098", "urn:ex:p": "g;x?y#s"},
          {"@id": "urn:ex:s099", "urn:ex:p": ""},
          {"@id": "urn:ex:s100", "urn:ex:p": "."},
          {"@id": "urn:ex:s101", "urn:ex:p": "./"},
          {"@id": "urn:ex:s102", "urn:ex:p": ".."},
          {"@id": "urn:ex:s103", "urn:ex:p": "../"},
          {"@id": "urn:ex:s104", "urn:ex:p": "../g"},
          {"@id": "urn:ex:s105", "urn:ex:p": "../.."},
          {"@id": "urn:ex:s106", "urn:ex:p": "../../"},
          {"@id": "urn:ex:s107", "urn:ex:p": "../../g"}
        ]
      },
      {
        "@context": {"@base": "http://a/bb/ccc/./d;p?q", "urn:ex:p": {"@type": "@id"}},
        "@graph": [
          {"@id": "urn:ex:s108", "urn:ex:p": "../../../g"},
          {"@id": "urn:ex:s109", "urn:ex:p": "../../../../g"},
          {"@id": "urn:ex:s110", "urn:ex:p": "/./g"},
          {"@id": "urn:ex:s111", "urn:ex:p": "/../g"},
          {"@id": "urn:ex:s112", "urn:ex:p": "g."},
          {"@id": "urn:ex:s113", "urn:ex:p": ".g"},
          {"@id": "urn:ex:s114", "urn:ex:p": "g.."},
          {"@id": "urn:ex:s115", "urn:ex:p": "..g"},
          {"@id": "urn:ex:s116", "urn:ex:p": "./../g"},
          {"@id": "urn:ex:s117", "urn:ex:p": "./g/."},
          {"@id": "urn:ex:s118", "urn:ex:p": "g/./h"},
          {"@id": "urn:ex:s119", "urn:ex:p": "g/../h"},
          {"@id": "urn:ex:s120", "urn:ex:p": "g;x=1/./y"},
          {"@id": "urn:ex:s121", "urn:ex:p": "g;x=1/../y"},
          {"@id": "urn:ex:s122", "urn:ex:p": "g?y/./x"},
          {"@id": "urn:ex:s123", "urn:ex:p": "g?y/../x"},
          {"@id": "urn:ex:s124", "urn:ex:p": "g#s/./x"},
          {"@id": "urn:ex:s125", "urn:ex:p": "g#s/../x"},
          {"@id": "urn:ex:s126", "urn:ex:p": "http:g"}
        ]
      },
      {
        "@context": {"@base": "http://a/bb/ccc/../d;p?q", "urn:ex:p": {"@type": "@id"}},
        "@graph": [
          {"@id": "urn:ex:s127", "urn:ex:p": "gg:h"},
          {"@id": "urn:ex:s128", "urn:ex:p": "g"},
          {"@id": "urn:ex:s129", "urn:ex:p": "./g"},
          {"@id": "urn:ex:s130", "urn:ex:p": "g/"},
          {"@id": "urn:ex:s131", "urn:ex:p": "/g"},
          {"@id": "urn:ex:s132", "urn:ex:p": "//g"},
          {"@id": "urn:ex:s133", "urn:ex:p": "?y"},
          {"@id": "urn:ex:s134", "urn:ex:p": "g?y"},
          {"@id": "urn:ex:s135", "urn:ex:p": "#s"},
          {"@id": "urn:ex:s136", "urn:ex:p": "g#s"},
          {"@id": "urn:ex:s137", "urn:ex:p": "g?y#s"},
          {"@id": "urn:ex:s138", "urn:ex:p": ";x"},
          {"@id": "urn:ex:s139", "urn:ex:p": "g;x"},
          {"@id": "urn:ex:s140", "urn:ex:p": "g;x?y#s"},
          {"@id": "urn:ex:s141", "urn:ex:p": ""},
          {"@id": "urn:ex:s142", "urn:ex:p": "."},
          {"@id": "urn:ex:s143", "urn:ex:p": "./"},
          {"@id": "urn:ex:s144", "urn:ex:p": ".."},
          {"@id": "urn:ex:s145", "urn:ex:p": "../"},
          {"@id": "urn:ex:s146", "urn:ex:p": "../g"},
          {"@id": "urn:ex:s147", "urn:ex:p": "../.."},
          {"@id": "urn:ex:s148", "urn:ex:p": "../../"},
          {"@id": "urn:ex:s149", "urn:ex:p": "../../g"}
        ]
      },
      {
        "@context": {"@base": "http://a/bb/ccc/../d;p?q", "urn:ex:p": {"@type": "@id"}},
        "@graph": [
          {"@id": "urn:ex:s150", "urn:ex:p": "../../../g"},
          {"@id": "urn:ex:s151", "urn:ex:p": "../../../../g"},
          {"@id": "urn:ex:s152", "urn:ex:p": "/./g"},
          {"@id": "urn:ex:s153", "urn:ex:p": "/../g"},
          {"@id": "urn:ex:s154", "urn:ex:p": "g."},
          {"@id": "urn:ex:s155", "urn:ex:p": ".g"},
          {"@id": "urn:ex:s156", "urn:ex:p": "g.."},
          {"@id": "urn:ex:s157", "urn:ex:p": "..g"},
          {"@id": "urn:ex:s158", "urn:ex:p": "./../g"},
          {"@id": "urn:ex:s159", "urn:ex:p": "./g/."},
          {"@id": "urn:ex:s160", "urn:ex:p": "g/./h"},
          {"@id": "urn:ex:s161", "urn:ex:p": "g/../h"},
          {"@id": "urn:ex:s162", "urn:ex:p": "g;x=1/./y"},
          {"@id": "urn:ex:s163", "urn:ex:p": "g;x=1/../y"},
          {"@id": "urn:ex:s164", "urn:ex:p": "g?y/./x"},
          {"@id": "urn:ex:s165", "urn:ex:p": "g?y/../x"},
          {"@id": "urn:ex:s166", "urn:ex:p": "g#s/./x"},
          {"@id": "urn:ex:s167", "urn:ex:p": "g#s/../x"},
          {"@id": "urn:ex:s168", "urn:ex:p": "http:g"}
        ]
      },
      {
        "@context": {"@base": "http://a/bb/ccc/.", "urn:ex:p": {"@type": "@id"}},
        "@graph": [
          {"@id": "urn:ex:s169", "urn:ex:p": "gg:h"},
          {"@id": "urn:ex:s170", "urn:ex:p": "g"},
          {"@id": "urn:ex:s171", "urn:ex:p": "./g"},
          {"@id": "urn:ex:s172", "urn:ex:p": "g/"},
          {"@id": "urn:ex:s173", "urn:ex:p": "/g"},
          {"@id": "urn:ex:s174", "urn:ex:p": "//g"},
          {"@id": "urn:ex:s175", "urn:ex:p": "?y"},
          {"@id": "urn:ex:s176", "urn:ex:p": "g?y"},
          {"@id": "urn:ex:s177", "urn:ex:p": "#s"},
          {"@id": "urn:ex:s178", "urn:ex:p": "g#s"},
          {"@id": "urn:ex:s179", "urn:ex:p": "g?y#s"},
          {"@id": "urn:ex:s180", "urn:ex:p": ";x"},
          {"@id": "urn:ex:s181", "urn:ex:p": "g;x"},
          {"@id": "urn:ex:s182", "urn:ex:p": "g;x?y#s"},
          {"@id": "urn:ex:s183", "urn:ex:p": ""},
          {"@id": "urn:ex:s184", "urn:ex:p": "."},
          {"@id": "urn:ex:s185", "urn:ex:p": "./"},
          {"@id": "urn:ex:s186", "urn:ex:p": ".."},
          {"@id": "urn:ex:s187", "urn:ex:p": "../"},
          {"@id": "urn:ex:s188", "urn:ex:p": "../g"},
          {"@id": "urn:ex:s189", "urn:ex:p": "../.."},
          {"@id": "urn:ex:s190", "urn:ex:p": "../../"},
          {"@id": "urn:ex:s191", "urn:ex:p": "../../g"}
        ]
      },
      {
        "@context": {"@base": "http://a/bb/ccc/.", "urn:ex:p": {"@type": "@id"}},
        "@graph": [
          {"@id": "urn:ex:s192", "urn:ex:p": "../../../g"},
          {"@id": "urn:ex:s193", "urn:ex:p": "../../../../g"},
          {"@id": "urn:ex:s194", "urn:ex:p": "/./g"},
          {"@id": "urn:ex:s195", "urn:ex:p": "/../g"},
          {"@id": "urn:ex:s196", "urn:ex:p": "g."},
          {"@id": "urn:ex:s197", "urn:ex:p": ".g"},
          {"@id": "urn:ex:s198", "urn:ex:p": "g.."},
          {"@id": "urn:ex:s199", "urn:ex:p": "..g"},
          {"@id": "urn:ex:s200", "urn:ex:p": "./../g"},
          {"@id": "urn:ex:s201", "urn:ex:p": "./g/."},
          {"@id": "urn:ex:s202", "urn:ex:p": "g/./h"},
          {"@id": "urn:ex:s203", "urn:ex:p": "g/../h"},
          {"@id": "urn:ex:s204", "urn:ex:p": "g;x=1/./y"},
          {"@id": "urn:ex:s205", "urn:ex:p": "g;x=1/../y"},
          {"@id": "urn:ex:s206", "urn:ex:p": "g?y/./x"},
          {"@id": "urn:ex:s207", "urn:ex:p": "g?y/../x"},
          {"@id": "urn:ex:s208", "urn:ex:p": "g#s/./x"},
          {"@id": "urn:ex:s209", "urn:ex:p": "g#s/../x"},
          {"@id": "urn:ex:s210", "urn:ex:p": "http:g"}
        ]
      },
      {
        "@context": {"@base": "http://a/bb/ccc/..", "urn:ex:p": {"@type": "@id"}},
        "@graph": [
          {"@id": "urn:ex:s211", "urn:ex:p": "gg:h"},
          {"@id": "urn:ex:s212", "urn:ex:p": "g"},
          {"@id": "urn:ex:s213", "urn:ex:p": "./g"},
          {"@id": "urn:ex:s214", "urn:ex:p": "g/"},
          {"@id": "urn:ex:s215", "urn:ex:p": "/g"},
          {"@id": "urn:ex:s216", "urn:ex:p": "//g"},
          {"@id": "urn:ex:s217", "urn:ex:p": "?y"},
          {"@id": "urn:ex:s218", "urn:ex:p": "g?y"},
          {"@id": "urn:ex:s219", "urn:ex:p": "#s"},
          {"@id": "urn:ex:s220", "urn:ex:p": "g#s"},
          {"@id": "urn:ex:s221", "urn:ex:p": "g?y#s"},
          {"@id": "urn:ex:s222", "urn:ex:p": ";x"},
          {"@id": "urn:ex:s223", "urn:ex:p": "g;x"},
          {"@id": "urn:ex:s224", "urn:ex:p": "g;x?y#s"},
          {"@id": "urn:ex:s225", "urn:ex:p": ""},
          {"@id": "urn:ex:s226", "urn:ex:p": "."},
          {"@id": "urn:ex:s227", "urn:ex:p": "./"},
          {"@id": "urn:ex:s228", "urn:ex:p": ".."},
          {"@id": "urn:ex:s229", "urn:ex:p": "../"},
          {"@id": "urn:ex:s230", "urn:ex:p": "../g"},
          {"@id": "urn:ex:s231", "urn:ex:p": "../.."},
          {"@id": "urn:ex:s232", "urn:ex:p": "../../"},
          {"@id": "urn:ex:s233", "urn:ex:p": "../../g"}
        ]
      },
      {
        "@context": {"@base": "http://a/bb/ccc/..", "urn:ex:p": {"@type": "@id"}},
        "@graph": [
          {"@id": "urn:ex:s234", "urn:ex:p": "../../../g"},
          {"@id": "urn:ex:s235", "urn:ex:p": "../../../../g"},
          {"@id": "urn:ex:s236", "urn:ex:p": "/./g"},
          {"@id": "urn:ex:s237", "urn:ex:p": "/../g"},
          {"@id": "urn:ex:s238", "urn:ex:p": "g."},
          {"@id": "urn:ex:s239", "urn:ex:p": ".g"},
          {"@id": "urn:ex:s240", "urn:ex:p": "g.."},
          {"@id": "urn:ex:s241", "urn:ex:p": "..g"},
          {"@id": "urn:ex:s242", "urn:ex:p": "./../g"},
          {"@id": "urn:ex:s243", "urn:ex:p": "./g/."},
          {"@id": "urn:ex:s244", "urn:ex:p": "g/./h"},
          {"@id": "urn:ex:s245", "urn:ex:p": "g/../h"},
          {"@id": "urn:ex:s246", "urn:ex:p": "g;x=1/./y"},
          {"@id": "urn:ex:s247", "urn:ex:p": "g;x=1/../y"},
          {"@id": "urn:ex:s248", "urn:ex:p": "g?y/./x"},
          {"@id": "urn:ex:s249", "urn:ex:p": "g?y/../x"},
          {"@id": "urn:ex:s250", "urn:ex:p": "g#s/./x"},
          {"@id": "urn:ex:s251", "urn:ex:p": "g#s/../x"},
          {"@id": "urn:ex:s252", "urn:ex:p": "http:g"}
        ]
      },
      {
        "@context": {"@base": "file:///a/bb/ccc/d;p?q", "urn:ex:p": {"@type": "@id"}},
        "@graph": [
          {"@id": "urn:ex:s253", "urn:ex:p": "gg:h"},
          {"@id": "urn:ex:s254", "urn:ex:p": "g"},
          {"@id": "urn:ex:s255", "urn:ex:p": "./g"},
          {"@id": "urn:ex:s256", "urn:ex:p": "g/"},
          {"@id": "urn:ex:s257", "urn:ex:p": "/g"},
          {"@id": "urn:ex:s258", "urn:ex:p": "//g"},
          {"@id": "urn:ex:s259", "urn:ex:p": "?y"},
          {"@id": "urn:ex:s260", "urn:ex:p": "g?y"},
          {"@id": "urn:ex:s261", "urn:ex:p": "#s"},
          {"@id": "urn:ex:s262", "urn:ex:p": "g#s"},
          {"@id": "urn:ex:s263", "urn:ex:p": "g?y#s"},
          {"@id": "urn:ex:s264", "urn:ex:p": ";x"},
          {"@id": "urn:ex:s265", "urn:ex:p": "g;x"},
          {"@id": "urn:ex:s266", "urn:ex:p": "g;x?y#s"},
          {"@id": "urn:ex:s267", "urn:ex:p": ""},
          {"@id": "urn:ex:s268", "urn:ex:p": "."},
          {"@id": "urn:ex:s269", "urn:ex:p": "./"},
          {"@id": "urn:ex:s270", "urn:ex:p": ".."},
          {"@id": "urn:ex:s271", "urn:ex:p": "../"},
          {"@id": "urn:ex:s272", "urn:ex:p": "../g"},
          {"@id": "urn:ex:s273", "urn:ex:p": "../.."},
          {"@id": "urn:ex:s274", "urn:ex:p": "../../"},
          {"@id": "urn:ex:s275", "urn:ex:p": "../../g"}
        ]
      },
      {
        "@context": {"@base": "file:///a/bb/ccc/d;p?q", "urn:ex:p": {"@type": "@id"}},
        "@graph": [
          {"@id": "urn:ex:s276", "urn:ex:p": "../../../g"},
          {"@id": "urn:ex:s277", "urn:ex:p": "../../../../g"},
          {"@id": "urn:ex:s278", "urn:ex:p": "/./g"},
          {"@id": "urn:ex:s279", "urn:ex:p": "/../g"},
          {"@id": "urn:ex:s280", "urn:ex:p": "g."},
          {"@id": "urn:ex:s281", "urn:ex:p": ".g"},
          {"@id": "urn:ex:s282", "urn:ex:p": "g.."},
          {"@id": "urn:ex:s283", "urn:ex:p": "..g"},
          {"@id": "urn:ex:s284", "urn:ex:p": "./../g"},
          {"@id": "urn:ex:s285", "urn:ex:p": "./g/."},
          {"@id": "urn:ex:s286", "urn:ex:p": "g/./h"},
          {"@id": "urn:ex:s287", "urn:ex:p": "g/../h"},
          {"@id": "urn:ex:s288", "urn:ex:p": "g;x=1/./y"},
          {"@id": "urn:ex:s289", "urn:ex:p": "g;x=1/../y"},
          {"@id": "urn:ex:s290", "urn:ex:p": "g?y/./x"},
          {"@id": "urn:ex:s291", "urn:ex:p": "g?y/../x"},
          {"@id": "urn:ex:s292", "urn:ex:p": "g#s/./x"},
          {"@id": "urn:ex:s293", "urn:ex:p": "g#s/../x"},
          {"@id": "urn:ex:s294", "urn:ex:p": "http:g"}
        ]
      },
      {
        "@context": {"@base": "http://abc/def/ghi", "urn:ex:p": {"@type": "@id"}},
        "@graph": [
          {"@id": "urn:ex:s295", "urn:ex:p": "."},
          {"@id": "urn:ex:s296", "urn:ex:p": ".?a=b"},
          {"@id": "urn:ex:s297", "urn:ex:p": ".#a=b"},
          {"@id": "urn:ex:s298", "urn:ex:p": ".."},
          {"@id": "urn:ex:s299", "urn:ex:p": "..?a=b"},
          {"@id": "urn:ex:s300", "urn:ex:p": "..#a=b"}
        ]
      },
      {
        "@context": {"@base": "http://ab//de//ghi", "urn:ex:p": {"@type": "@id"}},
        "@graph": [
          {"@id": "urn:ex:s301", "urn:ex:p": "xyz"},
          {"@id": "urn:ex:s302", "urn:ex:p": "./xyz"},
          {"@id": "urn:ex:s303", "urn:ex:p": "../xyz"}
        ]
      },
      {
        "@context": {"@base": "http://abc/d:f/ghi", "urn:ex:p": {"@type": "@id"}},
        "@graph": [
          {"@id": "urn:ex:s304", "urn:ex:p": "xyz"},
          {"@id": "urn:ex:s305", "urn:ex:p": "./xyz"},
          {"@id": "urn:ex:s306", "urn:ex:p": "../xyz"}
        ]
      }
    ]'
    end
    let(:nt) do
      '
      # RFC3986 normal examples

      <urn:ex:s001> <urn:ex:p> <gg:h>.
      <urn:ex:s002> <urn:ex:p> <http://a/bb/ccc/g>.
      <urn:ex:s003> <urn:ex:p> <http://a/bb/ccc/g>.
      <urn:ex:s004> <urn:ex:p> <http://a/bb/ccc/g/>.
      <urn:ex:s005> <urn:ex:p> <http://a/g>.
      <urn:ex:s006> <urn:ex:p> <http://g>.
      <urn:ex:s007> <urn:ex:p> <http://a/bb/ccc/d;p?y>.
      <urn:ex:s008> <urn:ex:p> <http://a/bb/ccc/g?y>.
      <urn:ex:s009> <urn:ex:p> <http://a/bb/ccc/d;p?q#s>.
      <urn:ex:s010> <urn:ex:p> <http://a/bb/ccc/g#s>.
      <urn:ex:s011> <urn:ex:p> <http://a/bb/ccc/g?y#s>.
      <urn:ex:s012> <urn:ex:p> <http://a/bb/ccc/;x>.
      <urn:ex:s013> <urn:ex:p> <http://a/bb/ccc/g;x>.
      <urn:ex:s014> <urn:ex:p> <http://a/bb/ccc/g;x?y#s>.
      <urn:ex:s015> <urn:ex:p> <http://a/bb/ccc/d;p?q>.
      <urn:ex:s016> <urn:ex:p> <http://a/bb/ccc/>.
      <urn:ex:s017> <urn:ex:p> <http://a/bb/ccc/>.
      <urn:ex:s018> <urn:ex:p> <http://a/bb/>.
      <urn:ex:s019> <urn:ex:p> <http://a/bb/>.
      <urn:ex:s020> <urn:ex:p> <http://a/bb/g>.
      <urn:ex:s021> <urn:ex:p> <http://a/>.
      <urn:ex:s022> <urn:ex:p> <http://a/>.
      <urn:ex:s023> <urn:ex:p> <http://a/g>.

      # RFC3986 abnormal examples

      <urn:ex:s024> <urn:ex:p> <http://a/g>.
      <urn:ex:s025> <urn:ex:p> <http://a/g>.
      <urn:ex:s026> <urn:ex:p> <http://a/g>.
      <urn:ex:s027> <urn:ex:p> <http://a/g>.
      <urn:ex:s028> <urn:ex:p> <http://a/bb/ccc/g.>.
      <urn:ex:s029> <urn:ex:p> <http://a/bb/ccc/.g>.
      <urn:ex:s030> <urn:ex:p> <http://a/bb/ccc/g..>.
      <urn:ex:s031> <urn:ex:p> <http://a/bb/ccc/..g>.
      <urn:ex:s032> <urn:ex:p> <http://a/bb/g>.
      <urn:ex:s033> <urn:ex:p> <http://a/bb/ccc/g/>.
      <urn:ex:s034> <urn:ex:p> <http://a/bb/ccc/g/h>.
      <urn:ex:s035> <urn:ex:p> <http://a/bb/ccc/h>.
      <urn:ex:s036> <urn:ex:p> <http://a/bb/ccc/g;x=1/y>.
      <urn:ex:s037> <urn:ex:p> <http://a/bb/ccc/y>.
      <urn:ex:s038> <urn:ex:p> <http://a/bb/ccc/g?y/./x>.
      <urn:ex:s039> <urn:ex:p> <http://a/bb/ccc/g?y/../x>.
      <urn:ex:s040> <urn:ex:p> <http://a/bb/ccc/g#s/./x>.
      <urn:ex:s041> <urn:ex:p> <http://a/bb/ccc/g#s/../x>.
      <urn:ex:s042> <urn:ex:p> <http:g>.

      # RFC3986 normal examples with trailing slash in base IRI

      <urn:ex:s043> <urn:ex:p> <gg:h>.
      <urn:ex:s044> <urn:ex:p> <http://a/bb/ccc/d/g>.
      <urn:ex:s045> <urn:ex:p> <http://a/bb/ccc/d/g>.
      <urn:ex:s046> <urn:ex:p> <http://a/bb/ccc/d/g/>.
      <urn:ex:s047> <urn:ex:p> <http://a/g>.
      <urn:ex:s048> <urn:ex:p> <http://g>.
      <urn:ex:s049> <urn:ex:p> <http://a/bb/ccc/d/?y>.
      <urn:ex:s050> <urn:ex:p> <http://a/bb/ccc/d/g?y>.
      <urn:ex:s051> <urn:ex:p> <http://a/bb/ccc/d/#s>.
      <urn:ex:s052> <urn:ex:p> <http://a/bb/ccc/d/g#s>.
      <urn:ex:s053> <urn:ex:p> <http://a/bb/ccc/d/g?y#s>.
      <urn:ex:s054> <urn:ex:p> <http://a/bb/ccc/d/;x>.
      <urn:ex:s055> <urn:ex:p> <http://a/bb/ccc/d/g;x>.
      <urn:ex:s056> <urn:ex:p> <http://a/bb/ccc/d/g;x?y#s>.
      <urn:ex:s057> <urn:ex:p> <http://a/bb/ccc/d/>.
      <urn:ex:s058> <urn:ex:p> <http://a/bb/ccc/d/>.
      <urn:ex:s059> <urn:ex:p> <http://a/bb/ccc/d/>.
      <urn:ex:s060> <urn:ex:p> <http://a/bb/ccc/>.
      <urn:ex:s061> <urn:ex:p> <http://a/bb/ccc/>.
      <urn:ex:s062> <urn:ex:p> <http://a/bb/ccc/g>.
      <urn:ex:s063> <urn:ex:p> <http://a/bb/>.
      <urn:ex:s064> <urn:ex:p> <http://a/bb/>.
      <urn:ex:s065> <urn:ex:p> <http://a/bb/g>.

      # RFC3986 abnormal examples with trailing slash in base IRI

      <urn:ex:s066> <urn:ex:p> <http://a/g>.
      <urn:ex:s067> <urn:ex:p> <http://a/g>.
      <urn:ex:s068> <urn:ex:p> <http://a/g>.
      <urn:ex:s069> <urn:ex:p> <http://a/g>.
      <urn:ex:s070> <urn:ex:p> <http://a/bb/ccc/d/g.>.
      <urn:ex:s071> <urn:ex:p> <http://a/bb/ccc/d/.g>.
      <urn:ex:s072> <urn:ex:p> <http://a/bb/ccc/d/g..>.
      <urn:ex:s073> <urn:ex:p> <http://a/bb/ccc/d/..g>.
      <urn:ex:s074> <urn:ex:p> <http://a/bb/ccc/g>.
      <urn:ex:s075> <urn:ex:p> <http://a/bb/ccc/d/g/>.
      <urn:ex:s076> <urn:ex:p> <http://a/bb/ccc/d/g/h>.
      <urn:ex:s077> <urn:ex:p> <http://a/bb/ccc/d/h>.
      <urn:ex:s078> <urn:ex:p> <http://a/bb/ccc/d/g;x=1/y>.
      <urn:ex:s079> <urn:ex:p> <http://a/bb/ccc/d/y>.
      <urn:ex:s080> <urn:ex:p> <http://a/bb/ccc/d/g?y/./x>.
      <urn:ex:s081> <urn:ex:p> <http://a/bb/ccc/d/g?y/../x>.
      <urn:ex:s082> <urn:ex:p> <http://a/bb/ccc/d/g#s/./x>.
      <urn:ex:s083> <urn:ex:p> <http://a/bb/ccc/d/g#s/../x>.
      <urn:ex:s084> <urn:ex:p> <http:g>.

      # RFC3986 normal examples with /. in the base IRI

      <urn:ex:s085> <urn:ex:p> <gg:h>.
      <urn:ex:s086> <urn:ex:p> <http://a/bb/ccc/g>.
      <urn:ex:s087> <urn:ex:p> <http://a/bb/ccc/g>.
      <urn:ex:s088> <urn:ex:p> <http://a/bb/ccc/g/>.
      <urn:ex:s089> <urn:ex:p> <http://a/g>.
      <urn:ex:s090> <urn:ex:p> <http://g>.
      <urn:ex:s091> <urn:ex:p> <http://a/bb/ccc/./d;p?y>.
      <urn:ex:s092> <urn:ex:p> <http://a/bb/ccc/g?y>.
      <urn:ex:s093> <urn:ex:p> <http://a/bb/ccc/./d;p?q#s>.
      <urn:ex:s094> <urn:ex:p> <http://a/bb/ccc/g#s>.
      <urn:ex:s095> <urn:ex:p> <http://a/bb/ccc/g?y#s>.
      <urn:ex:s096> <urn:ex:p> <http://a/bb/ccc/;x>.
      <urn:ex:s097> <urn:ex:p> <http://a/bb/ccc/g;x>.
      <urn:ex:s098> <urn:ex:p> <http://a/bb/ccc/g;x?y#s>.
      <urn:ex:s099> <urn:ex:p> <http://a/bb/ccc/./d;p?q>.
      <urn:ex:s100> <urn:ex:p> <http://a/bb/ccc/>.
      <urn:ex:s101> <urn:ex:p> <http://a/bb/ccc/>.
      <urn:ex:s102> <urn:ex:p> <http://a/bb/>.
      <urn:ex:s103> <urn:ex:p> <http://a/bb/>.
      <urn:ex:s104> <urn:ex:p> <http://a/bb/g>.
      <urn:ex:s105> <urn:ex:p> <http://a/>.
      <urn:ex:s106> <urn:ex:p> <http://a/>.
      <urn:ex:s107> <urn:ex:p> <http://a/g>.

      # RFC3986 abnormal examples with /. in the base IRI

      <urn:ex:s108> <urn:ex:p> <http://a/g>.
      <urn:ex:s109> <urn:ex:p> <http://a/g>.
      <urn:ex:s110> <urn:ex:p> <http://a/g>.
      <urn:ex:s111> <urn:ex:p> <http://a/g>.
      <urn:ex:s112> <urn:ex:p> <http://a/bb/ccc/g.>.
      <urn:ex:s113> <urn:ex:p> <http://a/bb/ccc/.g>.
      <urn:ex:s114> <urn:ex:p> <http://a/bb/ccc/g..>.
      <urn:ex:s115> <urn:ex:p> <http://a/bb/ccc/..g>.
      <urn:ex:s116> <urn:ex:p> <http://a/bb/g>.
      <urn:ex:s117> <urn:ex:p> <http://a/bb/ccc/g/>.
      <urn:ex:s118> <urn:ex:p> <http://a/bb/ccc/g/h>.
      <urn:ex:s119> <urn:ex:p> <http://a/bb/ccc/h>.
      <urn:ex:s120> <urn:ex:p> <http://a/bb/ccc/g;x=1/y>.
      <urn:ex:s121> <urn:ex:p> <http://a/bb/ccc/y>.
      <urn:ex:s122> <urn:ex:p> <http://a/bb/ccc/g?y/./x>.
      <urn:ex:s123> <urn:ex:p> <http://a/bb/ccc/g?y/../x>.
      <urn:ex:s124> <urn:ex:p> <http://a/bb/ccc/g#s/./x>.
      <urn:ex:s125> <urn:ex:p> <http://a/bb/ccc/g#s/../x>.
      <urn:ex:s126> <urn:ex:p> <http:g>.

      # RFC3986 normal examples with /.. in the base IRI

      <urn:ex:s127> <urn:ex:p> <gg:h>.
      <urn:ex:s128> <urn:ex:p> <http://a/bb/g>.
      <urn:ex:s129> <urn:ex:p> <http://a/bb/g>.
      <urn:ex:s130> <urn:ex:p> <http://a/bb/g/>.
      <urn:ex:s131> <urn:ex:p> <http://a/g>.
      <urn:ex:s132> <urn:ex:p> <http://g>.
      <urn:ex:s133> <urn:ex:p> <http://a/bb/ccc/../d;p?y>.
      <urn:ex:s134> <urn:ex:p> <http://a/bb/g?y>.
      <urn:ex:s135> <urn:ex:p> <http://a/bb/ccc/../d;p?q#s>.
      <urn:ex:s136> <urn:ex:p> <http://a/bb/g#s>.
      <urn:ex:s137> <urn:ex:p> <http://a/bb/g?y#s>.
      <urn:ex:s138> <urn:ex:p> <http://a/bb/;x>.
      <urn:ex:s139> <urn:ex:p> <http://a/bb/g;x>.
      <urn:ex:s140> <urn:ex:p> <http://a/bb/g;x?y#s>.
      <urn:ex:s141> <urn:ex:p> <http://a/bb/ccc/../d;p?q>.
      <urn:ex:s142> <urn:ex:p> <http://a/bb/>.
      <urn:ex:s143> <urn:ex:p> <http://a/bb/>.
      <urn:ex:s144> <urn:ex:p> <http://a/>.
      <urn:ex:s145> <urn:ex:p> <http://a/>.
      <urn:ex:s146> <urn:ex:p> <http://a/g>.
      <urn:ex:s147> <urn:ex:p> <http://a/>.
      <urn:ex:s148> <urn:ex:p> <http://a/>.
      <urn:ex:s149> <urn:ex:p> <http://a/g>.

      # RFC3986 abnormal examples with /.. in the base IRI

      <urn:ex:s150> <urn:ex:p> <http://a/g>.
      <urn:ex:s151> <urn:ex:p> <http://a/g>.
      <urn:ex:s152> <urn:ex:p> <http://a/g>.
      <urn:ex:s153> <urn:ex:p> <http://a/g>.
      <urn:ex:s154> <urn:ex:p> <http://a/bb/g.>.
      <urn:ex:s155> <urn:ex:p> <http://a/bb/.g>.
      <urn:ex:s156> <urn:ex:p> <http://a/bb/g..>.
      <urn:ex:s157> <urn:ex:p> <http://a/bb/..g>.
      <urn:ex:s158> <urn:ex:p> <http://a/g>.
      <urn:ex:s159> <urn:ex:p> <http://a/bb/g/>.
      <urn:ex:s160> <urn:ex:p> <http://a/bb/g/h>.
      <urn:ex:s161> <urn:ex:p> <http://a/bb/h>.
      <urn:ex:s162> <urn:ex:p> <http://a/bb/g;x=1/y>.
      <urn:ex:s163> <urn:ex:p> <http://a/bb/y>.
      <urn:ex:s164> <urn:ex:p> <http://a/bb/g?y/./x>.
      <urn:ex:s165> <urn:ex:p> <http://a/bb/g?y/../x>.
      <urn:ex:s166> <urn:ex:p> <http://a/bb/g#s/./x>.
      <urn:ex:s167> <urn:ex:p> <http://a/bb/g#s/../x>.
      <urn:ex:s168> <urn:ex:p> <http:g>.

      # RFC3986 normal examples with trailing /. in the base IRI

      <urn:ex:s169> <urn:ex:p> <gg:h>.
      <urn:ex:s170> <urn:ex:p> <http://a/bb/ccc/g>.
      <urn:ex:s171> <urn:ex:p> <http://a/bb/ccc/g>.
      <urn:ex:s172> <urn:ex:p> <http://a/bb/ccc/g/>.
      <urn:ex:s173> <urn:ex:p> <http://a/g>.
      <urn:ex:s174> <urn:ex:p> <http://g>.
      <urn:ex:s175> <urn:ex:p> <http://a/bb/ccc/.?y>.
      <urn:ex:s176> <urn:ex:p> <http://a/bb/ccc/g?y>.
      <urn:ex:s177> <urn:ex:p> <http://a/bb/ccc/.#s>.
      <urn:ex:s178> <urn:ex:p> <http://a/bb/ccc/g#s>.
      <urn:ex:s179> <urn:ex:p> <http://a/bb/ccc/g?y#s>.
      <urn:ex:s180> <urn:ex:p> <http://a/bb/ccc/;x>.
      <urn:ex:s181> <urn:ex:p> <http://a/bb/ccc/g;x>.
      <urn:ex:s182> <urn:ex:p> <http://a/bb/ccc/g;x?y#s>.
      <urn:ex:s183> <urn:ex:p> <http://a/bb/ccc/.>.
      <urn:ex:s184> <urn:ex:p> <http://a/bb/ccc/>.
      <urn:ex:s185> <urn:ex:p> <http://a/bb/ccc/>.
      <urn:ex:s186> <urn:ex:p> <http://a/bb/>.
      <urn:ex:s187> <urn:ex:p> <http://a/bb/>.
      <urn:ex:s188> <urn:ex:p> <http://a/bb/g>.
      <urn:ex:s189> <urn:ex:p> <http://a/>.
      <urn:ex:s190> <urn:ex:p> <http://a/>.
      <urn:ex:s191> <urn:ex:p> <http://a/g>.

      # RFC3986 abnormal examples with trailing /. in the base IRI

      <urn:ex:s192> <urn:ex:p> <http://a/g>.
      <urn:ex:s193> <urn:ex:p> <http://a/g>.
      <urn:ex:s194> <urn:ex:p> <http://a/g>.
      <urn:ex:s195> <urn:ex:p> <http://a/g>.
      <urn:ex:s196> <urn:ex:p> <http://a/bb/ccc/g.>.
      <urn:ex:s197> <urn:ex:p> <http://a/bb/ccc/.g>.
      <urn:ex:s198> <urn:ex:p> <http://a/bb/ccc/g..>.
      <urn:ex:s199> <urn:ex:p> <http://a/bb/ccc/..g>.
      <urn:ex:s200> <urn:ex:p> <http://a/bb/g>.
      <urn:ex:s201> <urn:ex:p> <http://a/bb/ccc/g/>.
      <urn:ex:s202> <urn:ex:p> <http://a/bb/ccc/g/h>.
      <urn:ex:s203> <urn:ex:p> <http://a/bb/ccc/h>.
      <urn:ex:s204> <urn:ex:p> <http://a/bb/ccc/g;x=1/y>.
      <urn:ex:s205> <urn:ex:p> <http://a/bb/ccc/y>.
      <urn:ex:s206> <urn:ex:p> <http://a/bb/ccc/g?y/./x>.
      <urn:ex:s207> <urn:ex:p> <http://a/bb/ccc/g?y/../x>.
      <urn:ex:s208> <urn:ex:p> <http://a/bb/ccc/g#s/./x>.
      <urn:ex:s209> <urn:ex:p> <http://a/bb/ccc/g#s/../x>.
      <urn:ex:s210> <urn:ex:p> <http:g>.

      # RFC3986 normal examples with trailing /.. in the base IRI

      <urn:ex:s211> <urn:ex:p> <gg:h>.
      <urn:ex:s212> <urn:ex:p> <http://a/bb/ccc/g>.
      <urn:ex:s213> <urn:ex:p> <http://a/bb/ccc/g>.
      <urn:ex:s214> <urn:ex:p> <http://a/bb/ccc/g/>.
      <urn:ex:s215> <urn:ex:p> <http://a/g>.
      <urn:ex:s216> <urn:ex:p> <http://g>.
      <urn:ex:s217> <urn:ex:p> <http://a/bb/ccc/..?y>.
      <urn:ex:s218> <urn:ex:p> <http://a/bb/ccc/g?y>.
      <urn:ex:s219> <urn:ex:p> <http://a/bb/ccc/..#s>.
      <urn:ex:s220> <urn:ex:p> <http://a/bb/ccc/g#s>.
      <urn:ex:s221> <urn:ex:p> <http://a/bb/ccc/g?y#s>.
      <urn:ex:s222> <urn:ex:p> <http://a/bb/ccc/;x>.
      <urn:ex:s223> <urn:ex:p> <http://a/bb/ccc/g;x>.
      <urn:ex:s224> <urn:ex:p> <http://a/bb/ccc/g;x?y#s>.
      <urn:ex:s225> <urn:ex:p> <http://a/bb/ccc/..>.
      <urn:ex:s226> <urn:ex:p> <http://a/bb/ccc/>.
      <urn:ex:s227> <urn:ex:p> <http://a/bb/ccc/>.
      <urn:ex:s228> <urn:ex:p> <http://a/bb/>.
      <urn:ex:s229> <urn:ex:p> <http://a/bb/>.
      <urn:ex:s230> <urn:ex:p> <http://a/bb/g>.
      <urn:ex:s231> <urn:ex:p> <http://a/>.
      <urn:ex:s232> <urn:ex:p> <http://a/>.
      <urn:ex:s233> <urn:ex:p> <http://a/g>.

      # RFC3986 abnormal examples with trailing /.. in the base IRI

      <urn:ex:s234> <urn:ex:p> <http://a/g>.
      <urn:ex:s235> <urn:ex:p> <http://a/g>.
      <urn:ex:s236> <urn:ex:p> <http://a/g>.
      <urn:ex:s237> <urn:ex:p> <http://a/g>.
      <urn:ex:s238> <urn:ex:p> <http://a/bb/ccc/g.>.
      <urn:ex:s239> <urn:ex:p> <http://a/bb/ccc/.g>.
      <urn:ex:s240> <urn:ex:p> <http://a/bb/ccc/g..>.
      <urn:ex:s241> <urn:ex:p> <http://a/bb/ccc/..g>.
      <urn:ex:s242> <urn:ex:p> <http://a/bb/g>.
      <urn:ex:s243> <urn:ex:p> <http://a/bb/ccc/g/>.
      <urn:ex:s244> <urn:ex:p> <http://a/bb/ccc/g/h>.
      <urn:ex:s245> <urn:ex:p> <http://a/bb/ccc/h>.
      <urn:ex:s246> <urn:ex:p> <http://a/bb/ccc/g;x=1/y>.
      <urn:ex:s247> <urn:ex:p> <http://a/bb/ccc/y>.
      <urn:ex:s248> <urn:ex:p> <http://a/bb/ccc/g?y/./x>.
      <urn:ex:s249> <urn:ex:p> <http://a/bb/ccc/g?y/../x>.
      <urn:ex:s250> <urn:ex:p> <http://a/bb/ccc/g#s/./x>.
      <urn:ex:s251> <urn:ex:p> <http://a/bb/ccc/g#s/../x>.
      <urn:ex:s252> <urn:ex:p> <http:g>.

      # RFC3986 normal examples with file path

      <urn:ex:s253> <urn:ex:p> <gg:h>.
      <urn:ex:s254> <urn:ex:p> <file:///a/bb/ccc/g>.
      <urn:ex:s255> <urn:ex:p> <file:///a/bb/ccc/g>.
      <urn:ex:s256> <urn:ex:p> <file:///a/bb/ccc/g/>.
      <urn:ex:s257> <urn:ex:p> <file:///g>.
      <urn:ex:s258> <urn:ex:p> <file://g>.
      <urn:ex:s259> <urn:ex:p> <file:///a/bb/ccc/d;p?y>.
      <urn:ex:s260> <urn:ex:p> <file:///a/bb/ccc/g?y>.
      <urn:ex:s261> <urn:ex:p> <file:///a/bb/ccc/d;p?q#s>.
      <urn:ex:s262> <urn:ex:p> <file:///a/bb/ccc/g#s>.
      <urn:ex:s263> <urn:ex:p> <file:///a/bb/ccc/g?y#s>.
      <urn:ex:s264> <urn:ex:p> <file:///a/bb/ccc/;x>.
      <urn:ex:s265> <urn:ex:p> <file:///a/bb/ccc/g;x>.
      <urn:ex:s266> <urn:ex:p> <file:///a/bb/ccc/g;x?y#s>.
      <urn:ex:s267> <urn:ex:p> <file:///a/bb/ccc/d;p?q>.
      <urn:ex:s268> <urn:ex:p> <file:///a/bb/ccc/>.
      <urn:ex:s269> <urn:ex:p> <file:///a/bb/ccc/>.
      <urn:ex:s270> <urn:ex:p> <file:///a/bb/>.
      <urn:ex:s271> <urn:ex:p> <file:///a/bb/>.
      <urn:ex:s272> <urn:ex:p> <file:///a/bb/g>.
      <urn:ex:s273> <urn:ex:p> <file:///a/>.
      <urn:ex:s274> <urn:ex:p> <file:///a/>.
      <urn:ex:s275> <urn:ex:p> <file:///a/g>.

      # RFC3986 abnormal examples with file path

      <urn:ex:s276> <urn:ex:p> <file:///g>.
      <urn:ex:s277> <urn:ex:p> <file:///g>.
      <urn:ex:s278> <urn:ex:p> <file:///g>.
      <urn:ex:s279> <urn:ex:p> <file:///g>.
      <urn:ex:s280> <urn:ex:p> <file:///a/bb/ccc/g.>.
      <urn:ex:s281> <urn:ex:p> <file:///a/bb/ccc/.g>.
      <urn:ex:s282> <urn:ex:p> <file:///a/bb/ccc/g..>.
      <urn:ex:s283> <urn:ex:p> <file:///a/bb/ccc/..g>.
      <urn:ex:s284> <urn:ex:p> <file:///a/bb/g>.
      <urn:ex:s285> <urn:ex:p> <file:///a/bb/ccc/g/>.
      <urn:ex:s286> <urn:ex:p> <file:///a/bb/ccc/g/h>.
      <urn:ex:s287> <urn:ex:p> <file:///a/bb/ccc/h>.
      <urn:ex:s288> <urn:ex:p> <file:///a/bb/ccc/g;x=1/y>.
      <urn:ex:s289> <urn:ex:p> <file:///a/bb/ccc/y>.
      <urn:ex:s290> <urn:ex:p> <file:///a/bb/ccc/g?y/./x>.
      <urn:ex:s291> <urn:ex:p> <file:///a/bb/ccc/g?y/../x>.
      <urn:ex:s292> <urn:ex:p> <file:///a/bb/ccc/g#s/./x>.
      <urn:ex:s293> <urn:ex:p> <file:///a/bb/ccc/g#s/../x>.
      <urn:ex:s294> <urn:ex:p> <http:g>.

      # additional cases

      <urn:ex:s295> <urn:ex:p> <http://abc/def/>.
      <urn:ex:s296> <urn:ex:p> <http://abc/def/?a=b>.
      <urn:ex:s297> <urn:ex:p> <http://abc/def/#a=b>.
      <urn:ex:s298> <urn:ex:p> <http://abc/>.
      <urn:ex:s299> <urn:ex:p> <http://abc/?a=b>.
      <urn:ex:s300> <urn:ex:p> <http://abc/#a=b>.

      <urn:ex:s301> <urn:ex:p> <http://ab//de//xyz>.
      <urn:ex:s302> <urn:ex:p> <http://ab//de//xyz>.
      <urn:ex:s303> <urn:ex:p> <http://ab//de/xyz>.

      <urn:ex:s304> <urn:ex:p> <http://abc/d:f/xyz>.
      <urn:ex:s305> <urn:ex:p> <http://abc/d:f/xyz>.
      <urn:ex:s306> <urn:ex:p> <http://abc/xyz>.
    '
    end

    it "produces equivalent triples" do
      nt_str = RDF::NTriples::Reader.new(nt).dump(:ntriples)
      json_str = described_class.new(json).dump(:ntriples)
      expect(json_str).to eql(nt_str)
    end
  end
end


text:
# frozen_string_literal: true

require_relative 'spec_helper'
require 'rdf/spec/writer'

describe JSON::LD::Writer do
  let(:logger) { RDF::Spec.logger }

  after { |example| puts logger if example.exception }

  it_behaves_like 'an RDF::Writer' do
    let(:writer) { described_class.new(StringIO.new, logger: logger) }
  end

  describe ".for" do
    [
      :jsonld,
      "etc/doap.jsonld",
      { file_name:      'etc/doap.jsonld' },
      { file_extension: 'jsonld' },
      { content_type:   'application/ld+json' },
      { content_type:   'application/x-ld+json' }
    ].each do |arg|
      it "discovers with #{arg.inspect}" do
        expect(RDF::Reader.for(arg)).to eq JSON::LD::Reader
      end
    end
  end

  context "simple tests" do
    it "uses full URIs without base" do
      input = %(<http://a/b> <http://a/c> <http://a/d> .)
      expect(serialize(input)).to produce_jsonld([{
        '@id' => "http://a/b",
        "http://a/c" => [{ "@id" => "http://a/d" }]
      }], logger)
    end

    it "uses qname URIs with standard prefix" do
      input = %(<http://xmlns.com/foaf/0.1/b> <http://xmlns.com/foaf/0.1/c> <http://xmlns.com/foaf/0.1/d> .)
      expect(serialize(input, standard_prefixes: true)).to produce_jsonld({
        '@context' => {
          "foaf" => "http://xmlns.com/foaf/0.1/"
        },
        '@id' => "foaf:b",
        "foaf:c" => { "@id" => "foaf:d" }
      }, logger)
    end

    it "uses qname URIs with parsed prefix" do
      input = %(
        <https://senet.org/gm> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://vocab.org/frbr/core#Work> .
        <https://senet.org/gm> <http://purl.org/dc/terms/title> "Rhythm Paradise"@en .
        <https://senet.org/gm> <https://senet.org/ns#unofficialTitle> "Rhythm Tengoku"@en .
        <https://senet.org/gm> <https://senet.org/ns#urlkey> "rhythm-tengoku" .
      )
      expect(serialize(input, prefixes: {
        dc: "http://purl.org/dc/terms/",
        frbr: "http://vocab.org/frbr/core#",
        senet: "https://senet.org/ns#"
      })).to produce_jsonld({
        '@context' => {
          "dc" => "http://purl.org/dc/terms/",
          "frbr" => "http://vocab.org/frbr/core#",
          "senet" => "https://senet.org/ns#"
        },
        '@id' => "https://senet.org/gm",
        "@type" => "frbr:Work",
        "dc:title" => { "@value" => "Rhythm Paradise", "@language" => "en" },
        "senet:unofficialTitle" => { "@value" => "Rhythm Tengoku", "@language" => "en" },
        "senet:urlkey" => "rhythm-tengoku"
      }, logger)
    end

    it "uses CURIEs with empty prefix" do
      input = %(<http://xmlns.com/foaf/0.1/b> <http://xmlns.com/foaf/0.1/c> <http://xmlns.com/foaf/0.1/d> .)
      begin
        expect(serialize(input, prefixes: { "" => RDF::Vocab::FOAF }))
          .to produce_jsonld({
            "@context" => {
              "" => "http://xmlns.com/foaf/0.1/"
            },
            '@id' => ":b",
            ":c" => { "@id" => ":d" }
          }, logger)
      rescue JSON::LD::JsonLdError, JSON::LD::JsonLdError, TypeError => e
        raise("#{e.class}: #{e.message}\n" \
              "#{logger}\n" \
              "Backtrace:\n#{e.backtrace.join("\n")}")
      end
    end

    it "does not use terms if no suffix" do
      input = %(<http://xmlns.com/foaf/0.1/> <http://xmlns.com/foaf/0.1/> <http://xmlns.com/foaf/0.1/> .)
      expect(serialize(input, standard_prefixes: true))
        .not_to produce_jsonld({
          "@context" => { "foaf" => "http://xmlns.com/foaf/0.1/" },
          '@id' => "foaf",
          "foaf" => { "@id" => "foaf" }
        }, logger)
    end

    it "does not use CURIE with illegal local part" do
      input = %(
        @prefix db: <http://dbpedia.org/resource/> .
        @prefix dbo: <http://dbpedia.org/ontology/> .
        db:Michael_Jackson dbo:artistOf <http://dbpedia.org/resource/%28I_Can%27t_Make_It%29_Another_Day> .
      )

      expect(serialize(input, prefixes: {
        "db" => RDF::URI("http://dbpedia.org/resource/"),
        "dbo" => RDF::URI("http://dbpedia.org/ontology/")
      }))
        .to produce_jsonld({
          "@context" => {
            "db" => "http://dbpedia.org/resource/",
            "dbo" => "http://dbpedia.org/ontology/"
          },
          '@id' => "db:Michael_Jackson",
          "dbo:artistOf" => { "@id" => "db:%28I_Can%27t_Make_It%29_Another_Day" }
        }, logger)
    end

    it "does not use provided node identifiers if :unique_bnodes set" do
      input = %(_:a <http://example.com/foo> _:b .)
      result = serialize(input, unique_bnodes: true, context: {})
      expect(result.to_json).to match(/_:g\w+/)
    end

    it "serializes multiple subjects" do
      input = '
        @prefix : <http://www.w3.org/2006/03/test-description#> .
        @prefix dc: <http://purl.org/dc/terms/> .
        <http://example.com/test-cases/0001> a :TestCase .
        <http://example.com/test-cases/0002> a :TestCase .
      '
      expect(serialize(input, prefixes: { "" => "http://www.w3.org/2006/03/test-description#" }))
        .to produce_jsonld({
          '@context' => {
            "" => "http://www.w3.org/2006/03/test-description#",
            "dc" => RDF::Vocab::DC.to_s
          },
          '@graph' => [
            { '@id' => "http://example.com/test-cases/0001", '@type' => ":TestCase" },
            { '@id' => "http://example.com/test-cases/0002", '@type' => ":TestCase" }
          ]
        }, logger)
    end

    it "serializes Wikia OWL example" do
      input = '
        @prefix owl: <http://www.w3.org/2002/07/owl#> .
        @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
        @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
        @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

        <http://data.wikia.com/terms#Character> a owl:Class;
           rdfs:subClassOf _:a .
        _:a a owl:Restriction;
           owl:minQualifiedCardinality "1"^^xsd:nonNegativeInteger;
           owl:onClass <http://data.wikia.com/terms#Element>;
           owl:onProperty <http://data.wikia.com/terms#characterIn> .
      '
      expect(serialize(input, rename_bnodes: false, prefixes: {
        owl: "http://www.w3.org/2002/07/owl#",
        rdfs: "http://www.w3.org/2000/01/rdf-schema#",
        xsd: "http://www.w3.org/2001/XMLSchema#"
      }))
        .to produce_jsonld({
          '@context' => {
            "owl" => "http://www.w3.org/2002/07/owl#",
            "rdf" => "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
            "rdfs" => "http://www.w3.org/2000/01/rdf-schema#",
            "xsd" => "http://www.w3.org/2001/XMLSchema#"
          },
          '@graph' => [
            {
              "@id" => "_:a",
              "@type" => "owl:Restriction",
              "owl:minQualifiedCardinality" => { "@value" => "1", "@type" => "xsd:nonNegativeInteger" },
              "owl:onClass" => { "@id" => "http://data.wikia.com/terms#Element" },
              "owl:onProperty" => { "@id" => "http://data.wikia.com/terms#characterIn" }
            },
            {
              "@id" => "http://data.wikia.com/terms#Character",
              "@type" => "owl:Class",
              "rdfs:subClassOf" => { "@id" => "_:a" }
            }
          ]
        }, logger)
    end
  end

  context "RDF-star" do
    {
      'subject-iii': {
        input: RDF::Statement(
          RDF::Statement(
            RDF::URI('http://example/s1'),
            RDF::URI('http://example/p1'),
            RDF::URI('http://example/o1')
          ),
          RDF::URI('http://example/p'),
          RDF::URI('http://example/o')
        ),
        output: %({
         "@context": {"ex": "http://example/"},
         "@id": {
           "@id": "ex:s1",
           "ex:p1": {"@id": "ex:o1"}
         },
         "ex:p": {"@id": "ex:o"}
       })
      },
      'subject-iib': {
        input: RDF::Statement(
          RDF::Statement(
            RDF::URI('http://example/s1'),
            RDF::URI('http://example/p1'),
            RDF::Node.new('o1')
          ),
          RDF::URI('http://example/p'),
          RDF::URI('http://example/o')
        ),
        output: %({
          "@context": {"ex": "http://example/"},
          "@id": {
            "@id": "ex:s1",
            "ex:p1": {"@id": "_:o1"}
          },
          "ex:p": {"@id": "ex:o"}
        })
      },
      'subject-iil': {
        input: RDF::Statement(
          RDF::Statement(
            RDF::URI('http://example/s1'),
            RDF::URI('http://example/p1'),
            RDF::Literal('o1')
          ),
          RDF::URI('http://example/p'),
          RDF::URI('http://example/o')
        ),
        output: %({
          "@context": {"ex": "http://example/"},
          "@id": {
            "@id": "ex:s1",
            "ex:p1": "o1"
          },
          "ex:p": {"@id": "ex:o"}
        })
      },
      'subject-bii': {
        input: RDF::Statement(
          RDF::Statement(
            RDF::Node('s1'),
            RDF::URI('http://example/p1'),
            RDF::URI('http://example/o1')
          ),
          RDF::URI('http://example/p'),
          RDF::URI('http://example/o')
        ),
        output: %({
          "@context": {"ex": "http://example/"},
          "@id": {
            "@id": "_:s1",
            "ex:p1": {"@id": "ex:o1"}
          },
          "ex:p": {"@id": "ex:o"}
        })
      },
      'subject-bib': {
        input: RDF::Statement(
          RDF::Statement(
            RDF::Node('s1'),
            RDF::URI('http://example/p1'),
            RDF::Node.new('o1')
          ),
          RDF::URI('http://example/p'), RDF::URI('http://example/o')
        ),
        output: %({
          "@context": {"ex": "http://example/"},
          "@id": {
            "@id": "_:s1",
            "ex:p1": {"@id": "_:o1"}
          },
          "ex:p": {"@id": "ex:o"}
        })
      },
      'subject-bil': {
        input: RDF::Statement(
          RDF::Statement(
            RDF::Node('s1'),
            RDF::URI('http://example/p1'),
            RDF::Literal('o1')
          ),
          RDF::URI('http://example/p'),
          RDF::URI('http://example/o')
        ),
        output: %({
          "@context": {"ex": "http://example/"},
          "@id": {
            "@id": "_:s1",
            "ex:p1": "o1"
          },
          "ex:p": {"@id": "ex:o"}
        })
      },
      'object-iii': {
        input: RDF::Statement(
          RDF::URI('http://example/s'),
          RDF::URI('http://example/p'),
          RDF::Statement(
            RDF::URI('http://example/s1'),
            RDF::URI('http://example/p1'),
            RDF::URI('http://example/o1')
          )
        ),
        output: %({
          "@context": {"ex": "http://example/"},
          "@id": "ex:s",
          "ex:p": {
            "@id": {
              "@id": "ex:s1",
              "ex:p1": {"@id": "ex:o1"}
            }
          }
        })
      },
      'object-iib': {
        input: RDF::Statement(
          RDF::URI('http://example/s'),
          RDF::URI('http://example/p'),
          RDF::Statement(
            RDF::URI('http://example/s1'),
            RDF::URI('http://example/p1'),
            RDF::Node.new('o1')
          )
        ),
        output: %({
          "@context": {"ex": "http://example/"},
          "@id": "ex:s",
          "ex:p": {
            "@id": {
              "@id": "ex:s1",
              "ex:p1": {"@id": "_:o1"}
            }
          }
        })
      },
      'object-iil': {
        input: RDF::Statement(
          RDF::URI('http://example/s'),
          RDF::URI('http://example/p'),
          RDF::Statement(
            RDF::URI('http://example/s1'),
            RDF::URI('http://example/p1'),
            RDF::Literal('o1')
          )
        ),
        output: %({
          "@context": {"ex": "http://example/"},
          "@id": "ex:s",
          "ex:p": {
            "@id": {
              "@id": "ex:s1",
              "ex:p1": "o1"
            }
          }
        })
      },
      'recursive-subject': {
        input: RDF::Statement(
          RDF::Statement(
            RDF::Statement(
              RDF::URI('http://example/s2'),
              RDF::URI('http://example/p2'),
              RDF::URI('http://example/o2')
            ),
            RDF::URI('http://example/p1'),
            RDF::URI('http://example/o1')
          ),
          RDF::URI('http://example/p'),
          RDF::URI('http://example/o')
        ),
        output: %({
          "@context": {"ex": "http://example/"},
          "@id": {
            "@id": {
              "@id": "ex:s2",
              "ex:p2": {"@id": "ex:o2"}
            },
            "ex:p1": {"@id": "ex:o1"}
          },
          "ex:p": {"@id": "ex:o"}
        })
      }
    }.each do |name, params|
      it name do
        graph = RDF::Graph.new { |g| g << params[:input] }
        expect(
          serialize(graph, rdfstar: true, prefixes: { ex: 'http://example/' })
        ).to produce_jsonld(JSON.parse(params[:output]), logger)
      end
    end
  end

  unless ENV['CI']
    context "Writes fromRdf tests to isomorphic graph" do
      require 'suite_helper'
      m = Fixtures::SuiteTest::Manifest.open("#{Fixtures::SuiteTest::SUITE}fromRdf-manifest.jsonld")
      describe m.name do
        m.entries.each do |t|
          next unless t.positiveTest? && !t.property('input').include?('0016')

          specify "#{t.property('@id')}: #{t.name}" do
            logger.info "test: #{t.inspect}"
            logger.info "source: #{t.input}"
            t.logger = logger
            pending "Shared list BNode in different graphs" if t.property('input').include?("fromRdf-0021")
            repo = RDF::Repository.load(t.input_loc, format: :nquads)
            jsonld = described_class.buffer(logger: t.logger, **t.options) do |writer|
              writer << repo
            end

            # And then, re-generate jsonld as RDF

            expect(parse(jsonld, format: :jsonld, **t.options)).to be_equivalent_graph(repo, t)
          end
        end
      end
    end
  end

  def parse(input, format: :trig, **options)
    reader = RDF::Reader.for(format)
    RDF::Repository.new << reader.new(input, **options)
  end

  # Serialize ntstr to a string and compare against regexps
  def serialize(ntstr, **options)
    g = ntstr.is_a?(String) ? parse(ntstr, **options) : ntstr
    # logger.info g.dump(:ttl)
    result = JSON::LD::Writer.buffer(logger: logger, **options) do |writer|
      writer << g
    end
    if $verbose
      # puts hash.to_json
    end

    JSON.parse(result)
  end
end


text:
# frozen_string_literal: true

require_relative 'spec_helper'

unless ENV['CI']
  describe JSON::LD do
    describe "test suite" do
      require_relative 'suite_helper'
      %w[
        expand
        compact
        flatten
        fromRdf
        toRdf
      ].each do |partial|
        m = Fixtures::SuiteTest::Manifest.open("#{Fixtures::SuiteTest::STAR_SUITE}#{partial}-manifest.jsonld")
        describe m.name do
          m.entries.each do |t|
            specify "#{t.property('@id')}: #{t.name}#{' (negative test)' unless t.positiveTest?}" do
              t.options[:ordered] = false
              expect { t.run self }.not_to write.to(:error)
            end
          end
        end
      end
    end
  end
end


text:
require_relative 'spec_helper'
require 'rdf/spec/writer'
require 'json/ld/streaming_writer'

describe JSON::LD::StreamingWriter do
  let(:logger) { RDF::Spec.logger }

  after { |example| puts logger if example.exception }

  it_behaves_like 'an RDF::Writer' do
    let(:writer) { JSON::LD::Writer.new(StringIO.new(""), stream: true) }
  end

  context "simple tests" do
    it "uses full URIs without base" do
      input = %(<http://a/b> <http://a/c> <http://a/d> .)
      obj = serialize(input)
      expect(parse(obj.to_json, format: :jsonld)).to be_equivalent_graph(parse(input), logger: logger)
      expect(obj).to produce_jsonld([{
        '@id' => "http://a/b",
        "http://a/c" => [{ "@id" => "http://a/d" }]
      }], logger)
    end

    it "writes multiple kinds of statements" do
      input = %(
        <https://senet.org/gm> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://vocab.org/frbr/core#Work> .
        <https://senet.org/gm> <http://purl.org/dc/terms/title> "Rhythm Paradise"@en .
        <https://senet.org/gm> <https://senet.org/ns#unofficialTitle> "Rhythm Tengoku"@en .
        <https://senet.org/gm> <https://senet.org/ns#urlkey> "rhythm-tengoku" .
      )
      obj = serialize(input)
      expect(parse(obj.to_json, format: :jsonld)).to be_equivalent_graph(parse(input), logger: logger)
      expect(obj).to eql JSON.parse(%([{
        "@id": "https://senet.org/gm",
        "@type": ["http://vocab.org/frbr/core#Work"],
        "http://purl.org/dc/terms/title": [{"@value": "Rhythm Paradise", "@language": "en"}],
        "https://senet.org/ns#unofficialTitle": [{"@value": "Rhythm Tengoku", "@language": "en"}],
        "https://senet.org/ns#urlkey": [{"@value": "rhythm-tengoku"}]
      }]))
    end

    it "serializes multiple subjects" do
      input = '
        @prefix : <http://www.w3.org/2006/03/test-description#> .
        @prefix dc: <http://purl.org/dc/terms/> .
        <http://example.com/test-cases/0001> a :TestCase .
        <http://example.com/test-cases/0002> a :TestCase .
      '
      obj = serialize(input)
      expect(parse(obj.to_json, format: :jsonld)).to be_equivalent_graph(parse(input), logger: logger)
      expect(obj).to match_array(JSON.parse(%([
        {"@id": "http://example.com/test-cases/0001", "@type": ["http://www.w3.org/2006/03/test-description#TestCase"]},
        {"@id": "http://example.com/test-cases/0002", "@type": ["http://www.w3.org/2006/03/test-description#TestCase"]}
      ])))
    end
  end

  context "Named Graphs" do
    {
      "default" => [
        '{<a> <b> <c> .}',
        '[{"@id": "a", "b": [{"@id": "c"}]}]'
      ],
      "named" => [
        '<C> {<a> <b> <c> .}',
        '[{"@id" :  "C", "@graph" :  [{"@id": "a", "b": [{"@id": "c"}]}]}]'
      ],
      "combo" => [
        '
          <a> <b> <c> .
          <C> {<A> <b> <c> .}
        ',
        '[
          {"@id": "a", "b": [{"@id": "c"}]},
          {"@id": "C", "@graph": [{"@id": "A", "b": [{"@id": "c"}]}]}
        ]'
      ],
      "combo with duplicated statement" => [
        '
          <a> <b> <c> .
          <C> {<a> <b> <c> .}
        ',
        '[
          {"@id": "a", "b": [{"@id": "c"}]},
          {"@id": "C", "@graph": [{"@id": "a", "b": [{"@id": "c"}]}]}
        ]'
      ]
    }.each_pair do |title, (input, matches)|
      context title do
        subject { serialize(input) }

        it "matches expected json" do
          expect(subject).to match_array(JSON.parse(matches))
        end
      end
    end
  end

  unless ENV['CI']
    context "Writes fromRdf tests to isomorphic graph" do
      require 'suite_helper'
      m = Fixtures::SuiteTest::Manifest.open("#{Fixtures::SuiteTest::SUITE}fromRdf-manifest.jsonld")
      [nil, {}].each do |ctx|
        context "with context #{ctx.inspect}" do
          describe m.name do
            m.entries.each do |t|
              next unless t.positiveTest? && !t.property('input').include?('0016')

              t.logger = RDF::Spec.logger
              t.logger.info "test: #{t.inspect}"
              t.logger.info "source: #{t.input}"
              specify "#{t.property('@id')}: #{t.name}" do
                repo = RDF::Repository.load(t.input_loc, format: :nquads)
                jsonld = JSON::LD::Writer.buffer(stream: true, context: ctx, logger: t.logger, **t.options) do |writer|
                  writer << repo
                end
                t.logger.info "Generated: #{jsonld}"

                # And then, re-generate jsonld as RDF
                expect(parse(jsonld, format: :jsonld, **t.options)).to be_equivalent_graph(repo, t)
              end
            end
          end
        end
      end
    end
  end

  def parse(input, format: :trig, **options)
    reader = RDF::Reader.for(format)
    RDF::Repository.new << reader.new(input, **options)
  end

  # Serialize ntstr to a string and compare against regexps
  def serialize(ntstr, **options)
    g = ntstr.is_a?(String) ? parse(ntstr, **options) : ntstr
    logger = RDF::Spec.logger
    logger.info(g.dump(:ttl))
    result = JSON::LD::Writer.buffer(logger: logger, stream: true, **options) do |writer|
      writer << g
    end
    puts result if $verbose

    JSON.parse(result)
  end
end


text:
# frozen_string_literal: true

require_relative 'spec_helper'
require 'rdf/spec/format'

describe JSON::LD::Format do
  it_behaves_like 'an RDF::Format' do
    let(:format_class) { described_class }
  end

  describe ".for" do
    formats = [
      :jsonld,
      "etc/doap.jsonld",
      { file_name:      'etc/doap.jsonld' },
      { file_extension: 'jsonld' },
      { content_type:   'application/ld+json' },
      { content_type:   'application/x-ld+json' }
    ].each do |arg|
      it "discovers with #{arg.inspect}" do
        expect(RDF::Format.for(arg)).to eq described_class
      end
    end

    {
      jsonld: '{"@context" => "foo"}',
      context: %({\n"@context": {),
      id: %({\n"@id": {),
      type: %({\n"@type": {)
    }.each do |sym, str|
      it "detects #{sym}" do
        expect(described_class.for { str }).to eq described_class
      end
    end

    it "discovers 'jsonld'" do
      expect(RDF::Format.for(:jsonld).reader).to eq JSON::LD::Reader
    end
  end

  describe "#to_sym" do
    specify { expect(described_class.to_sym).to eq :jsonld }
  end

  describe "#to_uri" do
    specify { expect(described_class.to_uri).to eq RDF::URI('http://www.w3.org/ns/formats/JSON-LD') }
  end

  describe ".detect" do
    {
      jsonld: '{"@context" => "foo"}'
    }.each do |sym, str|
      it "detects #{sym}" do
        expect(described_class.detect(str)).to be_truthy
      end
    end

    {
      n3: "@prefix foo: <bar> .\nfoo:bar = {  <c>} .",
      nquads: "  <c> <d> . ",
      rdfxml: '<rdf:RDF about="foo"></rdf:RDF>',
      rdfa: '

',
      microdata: '

',
      ntriples: "  <c> .",
      multi_line: '\n  \n  "literal"\n .',
      turtle: "@prefix foo: <bar> .\n foo:a foo:b <c> ."
    }.each do |sym, str|
      it "does not detect #{sym}" do
        expect(described_class.detect(str)).to be_falsey
      end
    end
  end

  describe ".cli_commands", skip: Gem.win_platform? do
    require 'rdf/cli'
    let(:ttl) { File.expand_path('test-files/test-1-rdf.ttl', __dir__) }
    let(:json) { File.expand_path('test-files/test-1-input.jsonld', __dir__) }
    let(:context) { File.expand_path('test-files/test-1-context.jsonld', __dir__) }

    describe "#expand" do
      it "expands RDF" do
        expect { RDF::CLI.exec(["expand", ttl], format: :ttl, output_format: :jsonld) }.to write.to(:output)
      end

      it "expands JSON" do
        expect do
          RDF::CLI.exec(["expand", json], format: :jsonld, output_format: :jsonld, validate: false)
        end.to write.to(:output)
      end
    end

    describe "#compact" do
      it "compacts RDF" do
        expect do
          RDF::CLI.exec(["compact", ttl], context: context, format: :ttl, output_format: :jsonld,
            validate: false)
        end.to write.to(:output)
      end

      it "compacts JSON" do
        expect do
          RDF::CLI.exec(["compact", json], context: context, format: :jsonld, output_format: :jsonld,
            validate: false)
        end.to write.to(:output)
      end
    end

    describe "#flatten" do
      it "flattens RDF" do
        expect do
          RDF::CLI.exec(["flatten", ttl], context: context, format: :ttl, output_format: :jsonld,
            validate: false)
        end.to write.to(:output)
      end

      it "flattens JSON" do
        expect do
          RDF::CLI.exec(["flatten", json], context: context, format: :jsonld, output_format: :jsonld,
            validate: false)
        end.to write.to(:output)
      end
    end

    describe "#frame" do
      it "frames RDF" do
        expect do
          RDF::CLI.exec(["frame", ttl], frame: context, format: :ttl, output_format: :jsonld)
        end.to write.to(:output)
      end

      it "frames JSON" do
        expect do
          RDF::CLI.exec(["frame", json], frame: context, format: :jsonld, output_format: :jsonld,
            validate: false)
        end.to write.to(:output)
      end
    end
  end
end


text:
# frozen_string_literal: true

require_relative 'spec_helper'
require 'rack/linkeddata'
require 'rack/test'

describe JSON::LD::ContentNegotiation do
  include Rack::Test::Methods
  let(:logger) { RDF::Spec.logger }

  let(:app) do
    described_class.new(double("Target Rack Application", :call => [200, {}, @results || "A String"]))
  end

  describe "#parse_accept_header" do
    {
      "application/n-triples, application/ld+json;q=0.5" => %w[application/ld+json],
      "application/ld+json, application/ld+json;profile=http://www.w3.org/ns/json-ld#compacted" =>
        %w[application/ld+json;profile=http://www.w3.org/ns/json-ld#compacted application/ld+json]
    }.each do |accept, content_types|
      it "returns #{content_types.inspect} given #{accept.inspect}" do
        expect(app.send(:parse_accept_header, accept)).to eq content_types
      end
    end
  end

  describe "#find_content_type_for_media_range" do
    {
      "*/*" => "application/ld+json",
      "application/*" => "application/ld+json",
      "application/json" => "application/ld+json",
      "application/json;profile=http://www.w3.org/ns/json-ld#compacted" =>
        "application/ld+json;profile=http://www.w3.org/ns/json-ld#compacted",
      "text/plain" => nil
    }.each do |media_range, content_type|
      it "returns #{content_type.inspect} for #{media_range.inspect}" do
        expect(app.send(:find_content_type_for_media_range, media_range)).to eq content_type
      end
    end
  end

  describe "#call" do
    let(:schema_context) do
      JSON::LD::API::RemoteDocument.new('{
        "@context": {
          "@vocab": "http://schema.org/",
          "id": "@id",
          "type": "@type"
        }
      }', documentUrl: "http://schema.org")
    end
    let(:frame) do
      JSON::LD::API::RemoteDocument.new('{
        "@context": {
          "dc": "http://purl.org/dc/elements/1.1/",
          "ex": "http://example.org/vocab#"
        },
        "@type": "ex:Library",
        "ex:contains": {
          "@type": "ex:Book",
          "ex:contains": {
            "@type": "ex:Chapter"
          }
        }
      }', documentUrl: "http://conneg.example.com/frame")
    end
    let(:context) do
      JSON::LD::API::RemoteDocument.new('{
        "@context": {
          "dc": "http://purl.org/dc/elements/1.1/",
          "ex": "http://example.org/vocab#"
        }
      }', documentUrl: "http://conneg.example.com/context")
    end

    before do
      allow(JSON::LD::API).to receive(:documentLoader).with("http://schema.org", any_args).and_yield(schema_context)
      allow(JSON::LD::API).to receive(:documentLoader).with("http://conneg.example.com/context",
        any_args).and_yield(context)
      allow(JSON::LD::API).to receive(:documentLoader).with("http://conneg.example.com/frame",
        any_args).and_yield(frame)
    end

    context "with text result" do
      it "returns text unchanged" do
        get '/'
        expect(last_response.body).to eq 'A String'
      end
    end

    context "with object result" do
      before do
        @results = LIBRARY_INPUT
      end

      it "returns expanded result" do
        get '/'
        expect(JSON.parse(last_response.body)).to produce_jsonld(LIBRARY_INPUT, logger)
      end

      context "with Accept" do
        {
          "application/n-triples" => "406 Not Acceptable (No appropriate combinaion of media-type and parameters found)\n",
          "application/json" => LIBRARY_INPUT,
          "application/ld+json" => LIBRARY_INPUT,
          %(application/ld+json;profile=http://www.w3.org/ns/json-ld#expanded) =>
                                                               LIBRARY_EXPANDED,

          %(application/ld+json;profile=http://www.w3.org/ns/json-ld#compacted) =>
                                                               LIBRARY_COMPACTED_DEFAULT,

          %(application/ld+json;profile=http://www.w3.org/ns/json-ld#flattened) =>
                                                               LIBRARY_FLATTENED_EXPANDED,
          %(application/ld+json;profile="http://www.w3.org/ns/json-ld#flattened http://www.w3.org/ns/json-ld#expanded") =>
                                                               LIBRARY_FLATTENED_EXPANDED,
          %(application/ld+json;profile="http://www.w3.org/ns/json-ld#expanded http://www.w3.org/ns/json-ld#flattened") =>
                                                               LIBRARY_FLATTENED_EXPANDED,

          %(application/ld+json;profile="http://www.w3.org/ns/json-ld#flattened http://www.w3.org/ns/json-ld#compacted") =>
                                                               LIBRARY_FLATTENED_COMPACTED_DEFAULT,
          %(application/ld+json;profile="http://www.w3.org/ns/json-ld#compacted http://www.w3.org/ns/json-ld#flattened") =>
                                                               LIBRARY_FLATTENED_COMPACTED_DEFAULT,

          %(application/ld+json;profile=http://www.w3.org/ns/json-ld#framed) =>
                                                               "406 Not Acceptable (framed profile without a frame)\n",
          %(application/ld+json;profile="http://www.w3.org/ns/json-ld#framed http://www.w3.org/ns/json-ld#expanded") =>
                                                               "406 Not Acceptable (framed profile without a frame)\n",
          %(application/ld+json;profile="http://www.w3.org/ns/json-ld#expanded http://www.w3.org/ns/json-ld#framed") =>
                                                               "406 Not Acceptable (framed profile without a frame)\n",
          %(application/ld+json;profile="http://www.w3.org/ns/json-ld#framed http://www.w3.org/ns/json-ld#compacted") =>
                                                               "406 Not Acceptable (framed profile without a frame)\n",
          %(application/ld+json;profile="http://www.w3.org/ns/json-ld#compacted http://www.w3.org/ns/json-ld#framed") =>
                                                               "406 Not Acceptable (framed profile without a frame)\n"
        }.each do |accepts, result|
          context accepts do
            before do
              get '/', {}, { "HTTP_ACCEPT" => accepts }
            end

            it "status" do
              expect(last_response.status).to satisfy("be 200 or 406") { |x| [200, 406].include?(x) }
            end

            it "sets content type" do
              expect(last_response.content_type).to eq(last_response.status == 406 ? 'text/plain' : 'application/ld+json')
            end

            it "returns serialization" do
              if last_response.status == 406
                expect(last_response.body).to eq result
              else
                expect(JSON.parse(last_response.body)).to produce_jsonld(result, logger)
              end
            end
          end
        end
      end

      context "with Link" do
        {
          "compacted with context" => {
            accept: %(application/ld+json;profile=http://www.w3.org/ns/json-ld#compacted),
            link: %(<http://conneg.example.com/context> rel="http://www.w3.org/ns/json-ld#context"),
            result: LIBRARY_COMPACTED
          },
          "flattened with context" => {
            accept: %(application/ld+json;profile=http://www.w3.org/ns/json-ld#flattened),
            link: %(<http://conneg.example.com/context> rel="http://www.w3.org/ns/json-ld#context"),
            result: LIBRARY_FLATTENED_COMPACTED
          },
          "flattened and compacted with context" => {
            accept: %(application/ld+json;profile="http://www.w3.org/ns/json-ld#flattened http://www.w3.org/ns/json-ld#compacted"),
            link: %(<http://conneg.example.com/context> rel="http://www.w3.org/ns/json-ld#context"),
            result: LIBRARY_FLATTENED_COMPACTED
          },
          "compacted and flattened with context" => {
            accept: %(application/ld+json;profile="http://www.w3.org/ns/json-ld#compacted http://www.w3.org/ns/json-ld#flattened"),
            link: %(<http://conneg.example.com/context> rel="http://www.w3.org/ns/json-ld#context"),
            result: LIBRARY_FLATTENED_COMPACTED
          },
          "framed with frame" => {
            accept: %(application/ld+json;profile=http://www.w3.org/ns/json-ld#framed),
            link: %(<http://conneg.example.com/frame> rel="http://www.w3.org/ns/json-ld#frame"),
            result: LIBRARY_FRAMED
          },

          "framed with context" => {
            accept: %(application/ld+json;profile=http://www.w3.org/ns/json-ld#framed),
            link: %(<http://conneg.example.com/context> rel="http://www.w3.org/ns/json-ld#context"),
            result: "406 Not Acceptable (framed profile without a frame)\n"
          }
        }.each do |name, params|
          context name do
            before do
              get '/', {}, { "HTTP_ACCEPT" => params[:accept], "HTTP_LINK" => params[:link] }
            end

            it "status" do
              expect(last_response.status).to satisfy("be 200 or 406") { |x| [200, 406].include?(x) }
            end

            it "sets content type" do
              expect(last_response.content_type).to eq(last_response.status == 406 ? 'text/plain' : 'application/ld+json')
            end

            it "returns serialization" do
              if last_response.status == 406
                expect(last_response.body).to eq params[:result]
              else
                expect(JSON.parse(last_response.body)).to produce_jsonld(params[:result], logger)
              end
            end
          end
        end
      end
    end
  end
end

describe Rack::LinkedData::ContentNegotiation do
  include Rack::Test::Methods
  let(:logger) { RDF::Spec.logger }

  let(:app) do
    graph = RDF::NTriples::Reader.new(%(
      <http://example.org/library> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/vocab#Library> .
      <http://example.org/library> <http://example.org/vocab#contains> <http://example.org/library/the-republic> .
      <http://example.org/library/the-republic> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/vocab#Book> .
      <http://example.org/library/the-republic> <http://purl.org/dc/elements/1.1/title> "The Republic" .
      <http://example.org/library/the-republic> <http://purl.org/dc/elements/1.1/creator> "Plato" .
      <http://example.org/library/the-republic> <http://example.org/vocab#contains> <http://example.org/library/the-republic#introduction> .
      <http://example.org/library/the-republic#introduction> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/vocab#Chapter> .
      <http://example.org/library/the-republic#introduction> <http://purl.org/dc/elements/1.1/title> "The Introduction" .
      <http://example.org/library/the-republic#introduction> <http://purl.org/dc/elements/1.1/description> "An introductory chapter on The Republic." .
    ))
    described_class.new(double("Target Rack Application", :call => [200, {}, graph]), {})
  end

  describe "#call" do
    let(:schema_context) do
      JSON::LD::API::RemoteDocument.new('{
        "@context": {
          "@vocab": "http://schema.org/",
          "id": "@id",
          "type": "@type"
        }
      }', documentUrl: "http://schema.org")
    end
    let(:frame) do
      JSON::LD::API::RemoteDocument.new('{
        "@context": {
          "dc": "http://purl.org/dc/elements/1.1/",
          "ex": "http://example.org/vocab#"
        },
        "@type": "ex:Library",
        "ex:contains": {
          "@type": "ex:Book",
          "ex:contains": {
            "@type": "ex:Chapter"
          }
        }
      }', documentUrl: "http://conneg.example.com/frame")
    end
    let(:context) do
      JSON::LD::API::RemoteDocument.new('{
        "@context": {
          "dc": "http://purl.org/dc/elements/1.1/",
          "ex": "http://example.org/vocab#"
        }
      }', documentUrl: "http://conneg.example.com/context")
    end

    before do
      allow(JSON::LD::API).to receive(:documentLoader).with("http://schema.org", any_args).and_yield(schema_context)
      allow(JSON::LD::API).to receive(:documentLoader).with("http://conneg.example.com/context",
        any_args).and_yield(context)
      allow(JSON::LD::API).to receive(:documentLoader).with("http://conneg.example.com/frame",
        any_args).and_yield(frame)
    end

    {
      "application/json" => LIBRARY_FLATTENED_EXPANDED,
      "application/ld+json" => LIBRARY_FLATTENED_EXPANDED,
      %(application/ld+json;profile=http://www.w3.org/ns/json-ld#expanded) =>
                                                           LIBRARY_FLATTENED_EXPANDED,

      %(application/ld+json;profile=http://www.w3.org/ns/json-ld#compacted) =>
                                                           LIBRARY_FLATTENED_COMPACTED_DEFAULT,

      %(application/ld+json;profile=http://www.w3.org/ns/json-ld#flattened) =>
                                                           LIBRARY_FLATTENED_EXPANDED,
      %(application/ld+json;profile="http://www.w3.org/ns/json-ld#flattened http://www.w3.org/ns/json-ld#expanded") =>
                                                           LIBRARY_FLATTENED_EXPANDED,
      %(application/ld+json;profile="http://www.w3.org/ns/json-ld#expanded http://www.w3.org/ns/json-ld#flattened") =>
                                                           LIBRARY_FLATTENED_EXPANDED,

      %(application/ld+json;profile="http://www.w3.org/ns/json-ld#flattened http://www.w3.org/ns/json-ld#compacted") =>
                                                           LIBRARY_FLATTENED_COMPACTED_DEFAULT,
      %(application/ld+json;profile="http://www.w3.org/ns/json-ld#compacted http://www.w3.org/ns/json-ld#flattened") =>
                                                           LIBRARY_FLATTENED_COMPACTED_DEFAULT

    }.each do |accepts, result|
      context accepts do
        before do
          get '/', {}, { "HTTP_ACCEPT" => accepts }
        end

        it "status" do
          expect(last_response.status).to satisfy("200 or 406") { |x| [200, 406].include?(x) }
        end

        it "sets content type" do
          expect(last_response.content_type).to eq(last_response.status == 406 ? 'text/plain' : 'application/ld+json')
        end

        it "returns serialization" do
          if last_response.status == 406
            expect(last_response.body).to eq result
          else
            expect(JSON.parse(last_response.body)).to produce_jsonld(result, logger)
          end
        end
      end
    end

    context "with Link" do
      {
        "with context" => {
          accept: %(application/ld+json),
          link: %(<http://conneg.example.com/context> rel="http://www.w3.org/ns/json-ld#context"),
          result: LIBRARY_FLATTENED_COMPACTED
        },
        "compacted with context" => {
          accept: %(application/ld+json;profile=http://www.w3.org/ns/json-ld#compacted),
          link: %(<http://conneg.example.com/context> rel="http://www.w3.org/ns/json-ld#context"),
          result: LIBRARY_FLATTENED_COMPACTED
        },
        "flattened and compacted with context" => {
          accept: %(application/ld+json;profile="http://www.w3.org/ns/json-ld#flattened http://www.w3.org/ns/json-ld#compacted"),
          link: %(<http://conneg.example.com/context> rel="http://www.w3.org/ns/json-ld#context"),
          result: LIBRARY_FLATTENED_COMPACTED
        },
        "compacted and flattened with context" => {
          accept: %(application/ld+json;profile="http://www.w3.org/ns/json-ld#compacted http://www.w3.org/ns/json-ld#flattened"),
          link: %(<http://conneg.example.com/context> rel="http://www.w3.org/ns/json-ld#context"),
          result: LIBRARY_FLATTENED_COMPACTED
        },
        "framed with frame" => {
          accept: %(application/ld+json;profile=http://www.w3.org/ns/json-ld#framed),
          link: %(<http://conneg.example.com/frame> rel="http://www.w3.org/ns/json-ld#frame"),
          result: LIBRARY_FRAMED
        }
      }.each do |name, params|
        context name do
          before do
            get '/', {}, { "HTTP_ACCEPT" => params[:accept], "HTTP_LINK" => params[:link] }
          end

          it "status" do
            expect(last_response.status).to satisfy("be 200 or 406") { |x| [200, 406].include?(x) }
          end

          it "sets content type" do
            expect(last_response.content_type).to eq(last_response.status == 406 ? 'text/plain' : 'application/ld+json')
          end

          it "returns serialization" do
            if last_response.status == 406
              expect(last_response.body).to eq params[:result]
            else
              expect(JSON.parse(last_response.body)).to produce_jsonld(params[:result], logger)
            end
          end
        end
      end
    end
  end
end


text:
require_relative 'spec_helper'

unless ENV['CI']
  describe JSON::LD do
    describe "test suite" do
      require_relative 'suite_helper'
      m = Fixtures::SuiteTest::Manifest.open("#{Fixtures::SuiteTest::FRAME_SUITE}frame-manifest.jsonld")
      describe m.name do
        m.entries.each do |t|
          t.options[:remap_bnodes] = %w[#t0021 #tp021].include?(t.property('@id'))

          specify "#{t.property('@id')}: #{t.name} unordered#{' (negative test)' unless t.positiveTest?}" do
            t.options[:ordered] = false
            expect { t.run self }.not_to write.to(:error)
          end

          # Skip ordered tests when remapping bnodes
          next if t.options[:remap_bnodes]

          specify "#{t.property('@id')}: #{t.name} ordered#{' (negative test)' unless t.positiveTest?}" do
            t.options[:ordered] = true
            pending("changes due to blank node reordering") if %w[#tp021].include?(t.property('@id'))
            expect { t.run self }.not_to write.to(:error)
          end
        end
      end
    end
  end
end


text:
# frozen_string_literal: true

require_relative 'spec_helper'

describe JSON::LD::Resource do
  subject { JSON::LD::Resource.new({ '@id' => '_:foo', "http://schema.org/name" => "foo" }) }

  describe "#initialize" do
    specify { expect(subject).not_to be_nil }
    specify { expect(subject).to be_a(described_class) }
    specify { expect(subject).not_to be_clean }
    specify { expect(subject).to be_anonymous }
    specify { expect(subject).to be_dirty }
    specify { expect(subject).to be_new }
    specify { expect(subject).not_to be_resolved }
    specify { expect(subject).not_to be_stub }

    context "schema:name property" do
      specify { expect(subject.property("http://schema.org/name")).to eq "foo" }
    end

    describe "compacted with context" do
      subject do
        described_class.new({ '@id' => '_:foo', "http://schema.org/name" => "foo" }, compact: true,
          context: { "@vocab" => "http://schema.org/" })
      end

      specify { expect(subject).not_to be_nil }
      specify { expect(subject).to be_a(described_class) }
      specify { expect(subject).not_to be_clean }
      specify { expect(subject).to be_anonymous }
      specify { expect(subject).to be_dirty }
      specify { expect(subject).to be_new }
      specify { expect(subject).not_to be_resolved }
      specify { expect(subject).not_to be_stub }
      its(:name) { is_expected.to eq "foo" }
    end
  end

  describe "#deresolve" do
    it "FIXME"
  end

  describe "#resolve" do
    it "FIXME"
  end

  describe "#hash" do
    specify { expect(subject.hash).to be_a(Integer) }

    it "returns the hash of the attributes" do
      expect(subject.hash).to eq subject.deresolve.hash
    end
  end

  describe "#to_json" do
    it "has JSON" do
      expect(subject.to_json).to be_a(String)
      expect(JSON.parse(subject.to_json)).to be_a(Hash)
    end

    it "has same ID" do
      expect(JSON.parse(subject.to_json)['@id']).to eq subject.id
    end
  end

  describe "#each" do
    specify { expect { |b| subject.each(&b) }.to yield_with_args(subject.statements.first) }
  end

  describe RDF::Enumerable do
    specify { expect(subject).to be_enumerable }

    it "initializes a graph" do
      g = RDF::Graph.new << subject
      expect(g.count).to eq 1
      expect(g.objects.first).to eq "foo"
    end
  end

  describe "#save" do
    specify { expect { subject.save }.to raise_error(NotImplementedError) }
  end
end


text:
# frozen_string_literal: true

require_relative 'spec_helper'

describe JSON::LD::API do
  let(:logger) { RDF::Spec.logger }

  before { JSON::LD::Context::PRELOADED.clear }

  describe "#initialize" do
    context "with string input" do
      let(:context) do
        JSON::LD::API::RemoteDocument.new('{
          "@context": {
            "xsd": "http://www.w3.org/2001/XMLSchema#",
            "name": "http://xmlns.com/foaf/0.1/name",
            "homepage": {"@id": "http://xmlns.com/foaf/0.1/homepage", "@type": "@id"},
            "avatar": {"@id": "http://xmlns.com/foaf/0.1/avatar", "@type": "@id"}
          }
          }',
          documentUrl: "http://example.com/context",
          contentType: 'application/ld+json')
      end
      let(:remote_doc) do
        JSON::LD::API::RemoteDocument.new('{"@id": "", "name": "foo"}',
          documentUrl: "http://example.com/foo",
          contentType: 'application/ld+json',
          contextUrl: "http://example.com/context")
      end

      it "loads document with loader and loads context" do
        expect(described_class).to receive(:documentLoader).with("http://example.com/foo",
          anything).and_yield(remote_doc)
        expect(described_class).to receive(:documentLoader).with("http://example.com/context",
          anything).and_yield(context)
        described_class.new("http://example.com/foo", nil)
      end
    end
  end

  context "when validating", pending: ("JRuby support for jsonlint" if RUBY_ENGINE == "jruby") do
    it "detects invalid JSON" do
      expect do
        described_class.new(StringIO.new(%({"a": "b", "a": "c"})), nil,
          validate: true)
      end.to raise_error(JSON::LD::JsonLdError::LoadingDocumentFailed)
    end
  end

  context "Test Files" do
    %i[oj json_gem ok_json yajl].each do |adapter|
      context "with MultiJson adapter #{adapter.inspect}" do
        Dir.glob(File.expand_path(File.join(File.dirname(__FILE__), 'test-files/*-input.*'))) do |filename|
          test = File.basename(filename).sub(/-input\..*$/, '')
          frame = filename.sub(/-input\..*$/, '-frame.jsonld')
          framed = filename.sub(/-input\..*$/, '-framed.jsonld')
          compacted = filename.sub(/-input\..*$/, '-compacted.jsonld')
          context = filename.sub(/-input\..*$/, '-context.jsonld')
          expanded = filename.sub(/-input\..*$/, '-expanded.jsonld')
          ttl = filename.sub(/-input\..*$/, '-rdf.ttl')

          context test,
            skip: ("Not supported in JRuby" if RUBY_ENGINE == "jruby" && %w[oj yajl].include?(adapter.to_s)) do
            around do |example|
              @file = File.open(filename)
              case filename
              when /.jsonld$/
                @file.define_singleton_method(:content_type) { 'application/ld+json' }
              end
              if context
                @ctx_io = File.open(context)
                case context
                when /.jsonld$/
                  @ctx_io.define_singleton_method(:content_type) { 'application/ld+json' }
                end
              end
              example.run
              @file.close
              @ctx_io&.close
            end

            if File.exist?(expanded)
              it "expands" do
                options = { logger: logger, adapter: adapter }
                options[:expandContext] = @ctx_io if context
                jld = described_class.expand(@file, **options)
                expect(jld).to produce_jsonld(JSON.parse(File.read(expanded)), logger)
              end

              it "expands with serializer" do
                options = { logger: logger, adapter: adapter }
                options[:expandContext] = @ctx_io if context
                jld = described_class.expand(@file, serializer: described_class.method(:serializer), **options)
                expect(jld).to be_a(String)
                expect(JSON.parse(jld)).to produce_jsonld(JSON.parse(File.read(expanded)), logger)
              end
            end

            if File.exist?(compacted) && File.exist?(context)
              it "compacts" do
                jld = described_class.compact(@file, @ctx_io, adapter: adapter, logger: logger)
                expect(jld).to produce_jsonld(JSON.parse(File.read(compacted)), logger)
              end

              it "compacts with serializer" do
                jld = described_class.compact(@file, @ctx_io, serializer: described_class.method(:serializer),
                  adapter: adapter, logger: logger)
                expect(jld).to be_a(String)
                expect(JSON.parse(jld)).to produce_jsonld(JSON.parse(File.read(compacted)), logger)
              end
            end

            if File.exist?(framed) && File.exist?(frame)
              it "frames" do
                File.open(frame) do |frame_io|
                  jld = described_class.frame(@file, frame_io, adapter: adapter, logger: logger)
                  expect(jld).to produce_jsonld(JSON.parse(File.read(framed)), logger)
                end
              end

              it "frames with serializer" do
                File.open(frame) do |frame_io|
                  jld = described_class.frame(@file, frame_io, serializer: described_class.method(:serializer),
                    adapter: adapter, logger: logger)
                  expect(jld).to be_a(String)
                  expect(JSON.parse(jld)).to produce_jsonld(JSON.parse(File.read(framed)), logger)
                end
              end
            end

            if File.exist?(ttl)
              it "toRdf" do
                expect(RDF::Repository.load(filename, format: :jsonld, adapter: adapter,
                  logger: logger)).to be_equivalent_graph(RDF::Repository.load(ttl), logger: logger)
              end
            end
          end
        end
      end
    end
  end
end