|
# Ensure rspec gem is installed |
|
# Then run `ruby ./linked_list_spec.rb --format=doc` |
|
|
|
require "rspec/autorun" |
|
require_relative "linked_list" |
|
|
|
RSpec.describe Node do |
|
let(:item) { :a } |
|
|
|
subject { Node.new(item) } |
|
|
|
it "sets item attribute from arguments" do |
|
expect(subject.item).to eq(item) |
|
end |
|
end |
|
|
|
RSpec.describe LinkedList do |
|
let(:item) { :my_new_item_value } |
|
let(:existing_item) { :existing_item_value } |
|
let(:some_other_head) { :some_other_head } |
|
let(:some_other_tail) { :some_other_tail } |
|
let(:list) { described_class.new } |
|
|
|
shared_context "a list containing existing_item" do |
|
before { list.add_to_front(existing_item) } |
|
end |
|
|
|
shared_context "a list containing item" do |
|
before { list.add_to_back(item) } |
|
end |
|
|
|
shared_examples "inserting a node as head" do |
|
context "when the list contains items" do |
|
include_context "a list containing existing_item" |
|
include_examples "a head node with next node" |
|
end |
|
|
|
context "when the list is empty" do |
|
include_examples "a singular node that is both tail and head" |
|
end |
|
end |
|
|
|
shared_examples "inserting a node as tail" do |
|
context "when the list contains items" do |
|
include_context "a list containing existing_item" |
|
include_examples "a tail node with prev node" |
|
end |
|
|
|
context "when the list is empty" do |
|
include_examples "a singular node that is both tail and head" |
|
end |
|
end |
|
|
|
shared_examples "a head node with next node" do |
|
it "adds the new item to the list" do |
|
expect { subject }.to change { list.length }.by(1) |
|
end |
|
|
|
it "sets the new item as head" do |
|
subject |
|
expect(list.head).to eq(list.find(item)) |
|
end |
|
|
|
it "sets the new item's prev node to nil" do |
|
subject |
|
expect(list.find(item).prev).to be_nil |
|
end |
|
|
|
it "sets the new item as the prev node of the former head" do |
|
subject |
|
expect(list.find(existing_item).prev).to eq(list.find(item)) |
|
end |
|
|
|
it "sets the former head as the next node of the new item" do |
|
subject |
|
expect(list.find(item).next).to eq(list.find(existing_item)) |
|
end |
|
end |
|
|
|
shared_examples "a tail node with prev node" do |
|
it "adds the new item to the list" do |
|
expect { subject }.to change { list.length }.by(1) |
|
end |
|
|
|
it "sets the new item as tail" do |
|
subject |
|
expect(list.tail).to eq(list.find(item)) |
|
end |
|
|
|
it "sets the new item's next node to nil" do |
|
subject |
|
expect(list.find(item).next).to be_nil |
|
end |
|
|
|
it "sets the new item as the next node of the former tail" do |
|
subject |
|
expect(list.find(existing_item).next).to eq(list.find(item)) |
|
end |
|
|
|
it "sets the former tail as the prev node of the new item" do |
|
subject |
|
expect(list.find(item).prev).to eq(list.find(existing_item)) |
|
end |
|
end |
|
|
|
shared_examples "a middle node with prev and next nodes" do |
|
it "adds the new item to the list" do |
|
expect { subject }.to change { list.length }.by(1) |
|
end |
|
|
|
it "sets the prev node of the new item to the reference item's prev node" do |
|
subject |
|
expect(list.find(item).prev).to eq(list.find(prev_item)) |
|
end |
|
|
|
it "sets the next node of the new item to the reference item" do |
|
subject |
|
expect(list.find(item).next).to eq(list.find(next_item)) |
|
end |
|
|
|
it "sets the prev node of the reference item to the new item" do |
|
subject |
|
expect(list.find(next_item).prev).to eq(list.find(item)) |
|
end |
|
|
|
it "sets the next node of the reference item's prev node to the new node" do |
|
subject |
|
expect(list.find(prev_item).next).to eq(list.find(item)) |
|
end |
|
end |
|
|
|
shared_examples "a singular node that is both tail and head" do |
|
it "adds the new item" do |
|
expect { subject }.to change { list.length }.from(0).to(1) |
|
end |
|
|
|
it "sets the new item as both head and tail" do |
|
subject |
|
expect(list.head).to eq(list.find(item)) |
|
expect(list.tail).to eq(list.find(item)) |
|
end |
|
|
|
it "sets the new item's prev node to nil" do |
|
subject |
|
expect(list.find(item).prev).to be_nil |
|
end |
|
|
|
it "sets the new item's next node to nil" do |
|
subject |
|
expect(list.find(item).next).to be_nil |
|
end |
|
end |
|
|
|
describe "#add_to_front" do |
|
subject { list.add_to_front(item) } |
|
include_examples "inserting a node as head" |
|
end |
|
|
|
describe "#add_to_back" do |
|
subject { list.add_to_back(item) } |
|
include_examples "inserting a node as tail" |
|
end |
|
|
|
describe "#insert_before" do |
|
subject { list.insert_before(item, existing_item) } |
|
|
|
context "when the list contains the reference item" do |
|
include_context "a list containing existing_item" |
|
|
|
context "when the reference item is head" do |
|
include_examples "a head node with next node" |
|
end |
|
|
|
context "when the reference item is not head" do |
|
before { list.add_to_front(some_other_head) } |
|
include_examples "a middle node with prev and next nodes" do |
|
let(:prev_item) { some_other_head } |
|
let(:next_item) { existing_item } |
|
end |
|
end |
|
end |
|
|
|
context "when the list does not contain the reference item" do |
|
include_examples "inserting a node as head" |
|
end |
|
end |
|
|
|
describe "#insert_after" do |
|
subject { list.insert_after(item, existing_item) } |
|
|
|
context "when the list contains the reference item" do |
|
include_context "a list containing existing_item" |
|
|
|
context "when the reference item is tail" do |
|
include_examples "a tail node with prev node" |
|
end |
|
|
|
context "when the reference item is not tail" do |
|
before { list.add_to_back(some_other_tail) } |
|
include_examples "a middle node with prev and next nodes" do |
|
let(:prev_item) { existing_item } |
|
let(:next_item) { some_other_tail } |
|
end |
|
end |
|
end |
|
|
|
context "when the list does not contain the reference item" do |
|
include_examples "inserting a node as tail" |
|
end |
|
end |
|
|
|
describe "#remove" do |
|
subject { list.remove(item) } |
|
|
|
context "when the list contains the item" do |
|
include_context "a list containing item" |
|
|
|
it "reduces the list length by one" do |
|
expect { subject }.to change { list.length }.by(-1) |
|
end |
|
|
|
context "when the item is head and tail" do |
|
it "sets the head to nil" do |
|
subject |
|
expect(list.head).to be_nil |
|
end |
|
|
|
it "sets the tail to nil" do |
|
subject |
|
expect(list.head).to be_nil |
|
end |
|
end |
|
|
|
context "when the item is head" do |
|
before { list.add_to_back(some_other_tail) } |
|
|
|
it "sets the head to the next node" do |
|
subject |
|
expect(list.head).to eq(list.find(some_other_tail)) |
|
end |
|
|
|
it "sets prev of the next node to nil" do |
|
subject |
|
expect(list.find(some_other_tail).prev).to be_nil |
|
end |
|
end |
|
|
|
context "when the item is tail" do |
|
before { list.add_to_front(some_other_head) } |
|
|
|
it "sets the tail to the prev node" do |
|
subject |
|
expect(list.head).to eq(list.find(some_other_head)) |
|
end |
|
|
|
it "sets next of the prev node to nil" do |
|
subject |
|
expect(list.find(some_other_head).next).to be_nil |
|
end |
|
end |
|
|
|
context "when the item is neither head nor tail" do |
|
before do |
|
list.add_to_front(some_other_head) |
|
list.add_to_back(some_other_tail) |
|
end |
|
|
|
|
|
it "sets prev of the next node to the removed item's prev node" do |
|
subject |
|
expect(list.find(some_other_head).next).to eq(list.find(some_other_tail)) |
|
end |
|
|
|
it "sets next of the prev node to the removed item's next node" do |
|
subject |
|
expect(list.find(some_other_tail).prev).to eq(list.find(some_other_head)) |
|
end |
|
end |
|
end |
|
|
|
context "when the list does not contain the item" do |
|
include_context "a list containing existing_item" |
|
|
|
it "does not remove anything" do |
|
expect { subject }.to_not change { list.length } |
|
end |
|
end |
|
|
|
context "when the list is empty" do |
|
it "does not remove anything" do |
|
expect { subject }.to_not change { list.length } |
|
end |
|
end |
|
end |
|
|
|
describe "#contains?" do |
|
subject { list.contains?(item) } |
|
|
|
context "when the list contains the item" do |
|
include_context "a list containing item" |
|
|
|
it "returns true" do |
|
expect(subject).to be_truthy |
|
end |
|
end |
|
|
|
context "when the list does not contain the item" do |
|
include_context "a list containing existing_item" |
|
|
|
it "returns false" do |
|
expect(subject).to be_falsey |
|
end |
|
end |
|
end |
|
|
|
describe "#sort" do |
|
subject { list.sort } |
|
|
|
it "returns a new linked list" do |
|
expect(subject).to be_a(LinkedList) |
|
expect(subject).to_not be(list) |
|
end |
|
|
|
context "when the list is empty" do |
|
let(:expected_list) { LinkedList.new } |
|
|
|
it "returns an empty list" do |
|
expect(subject.length).to eq(0) |
|
end |
|
end |
|
|
|
context "when the list contains one item" do |
|
include_context "a list containing item" |
|
|
|
it "returns a list containing the item" do |
|
expect(subject.length).to eq(1) |
|
expect(subject.head.item).to eq(item) |
|
end |
|
end |
|
|
|
context "when the list contains more than one item" do |
|
let(:items) { [nil, :b, "a", false, :bbb, :b, "aaa", 4] } |
|
|
|
before do |
|
items.shuffle.each { |item| list.add_to_back(item) } |
|
expect(list.length).to eq(items.size) |
|
end |
|
|
|
it "returns a list containing the original items sorted naturally" do |
|
expect(subject.length).to eq(items.size) |
|
expect(subject.to_a).to eq([nil, 4, "a", "aaa", :b, :b, :bbb, false]) |
|
end |
|
end |
|
end |
|
|
|
describe "#find" do |
|
subject { list.find(item) } |
|
|
|
context "when the item exists in the list" do |
|
include_context "a list containing item" |
|
|
|
it "returns the node of the item" do |
|
expect(subject).to be_a(Node) |
|
expect(subject.item).to eq(item) |
|
end |
|
end |
|
|
|
context "when the item does not exist in the list" do |
|
include_context "a list containing existing_item" |
|
|
|
it "returns nil" do |
|
expect(subject).to be_nil |
|
end |
|
end |
|
|
|
context "when the list is empty" do |
|
it "returns nil" do |
|
expect(subject).to be_nil |
|
end |
|
end |
|
end |
|
|
|
describe "#to_a" do |
|
subject { list.to_a } |
|
|
|
context "when the list is empty" do |
|
it "returns an empty array" do |
|
expect(subject).to eq([]) |
|
end |
|
end |
|
|
|
context "when the list contains items" do |
|
before do |
|
list.add_to_back(item) |
|
list.add_to_back(existing_item) |
|
list.add_to_back(some_other_head) |
|
list.add_to_back(some_other_tail) |
|
end |
|
|
|
it "returns an array of items" do |
|
expect(subject).to contain_exactly(item, existing_item, some_other_head, some_other_tail) |
|
end |
|
end |
|
end |
|
end |