# frozen_string_literal: true RSpec.describe GameOfLife do describe ".parsed_options" do let(:options) do { "live-cell" => "L", "dead-cell" => "D", } end before do console = instance_double("") allow(IO).to receive(:console).and_return(console) allow(console).to receive(:winsize).and_return([10, 20]) end context "with options['delay']" do it "raises GameOfLife::Error when negative" do options["delay"] = -13 expect { described_class.parsed_options(options) }.to raise_error(GameOfLife::Error, /Invalid --delay value/) end it "uses options['delay'] when posotive" do options["delay"] = 15 expect(described_class.parsed_options(options)).to include("delay" => 15) end end context "with options['width']" do it "raises GameOfLife::Error when < 1" do options["width"] = 0 expect { described_class.parsed_options(options) }.to raise_error(GameOfLife::Error, /Invalid --width value/) end it "raises GameOfLife::Error when > IO.console.winsize[1]" do options["width"] = 23 expect { described_class.parsed_options(options) }.to raise_error(GameOfLife::Error, /Invalid --width value/) end it "uses options['width']" do options["width"] = 15 expect(described_class.parsed_options(options)).to include("width" => 15) end it "uses IO.console.winsize[1] as default" do expect(described_class.parsed_options(options)).to include("width" => 20) end end context "with options['height']" do it "raises GameOfLife::Error < 1" do options["height"] = 0 expect { described_class.parsed_options(options) }.to raise_error(GameOfLife::Error, /Invalid --height value/) end it "raises GameOfLife::Error when > winsize[0] - 2" do options["height"] = 9 expect { described_class.parsed_options(options) }.to raise_error(GameOfLife::Error, /Invalid --height value/) end it "uses options['height']" do options["height"] = 5 expect(described_class.parsed_options(options)).to include("height" => 5) end it "uses IO.console.winsize[0] - 2 as default" do expect(described_class.parsed_options(options)).to include("height" => 8) end end context "with options['live-cell']" do it "raises GameOfLife::Error if live-cell is multiple characters" do options["live-cell"] = "LL" expect { described_class.parsed_options(options) }.to raise_error(GameOfLife::Error, /Invalid --live-cell /) end end context "with options['dead-cell']" do it "raises GameOfLife::Error if dead-cell is multiple characters" do options["dead-cell"] = "DD" expect { described_class.parsed_options(options) }.to raise_error(GameOfLife::Error, /Invalid --dead-cell /) end it "raises GameOfLife::Error if dead-cell is same as live-cell character" do options = { "live-cell" => "X", "dead-cell" => "X" } expect { described_class.parsed_options(options) }.to raise_error(GameOfLife::Error, /Invalid --dead-cell /) end end context "with options['seed']" do it "uses options['seed']" do options["seed"] = 1337 expect(described_class.parsed_options(options)).to include("seed" => 1337) end # rubocop:disable RSpec/AnyInstance it "defaults to Kernel.random" do allow_any_instance_of(Kernel).to receive(:rand).and_return "random" expect(described_class.parsed_options(options)).to include("seed" => "random") end # rubocop:enable RSpec/AnyInstance end end describe ".generate" do let(:options) do { "live-cell" => "L", "dead-cell" => "D", "delay" => 100, "width" => 10, "height" => 20, "seed" => 500, } end context "with options['input']" do before { options["input"] = "./file.txt" } it "defines configurations constants" do expect(described_class::Cell).to receive(:const_set).with(:LIVE_CELL, "L") expect(described_class::Cell).to receive(:const_set).with(:DEAD_CELL, "D") expect(described_class).to receive(:const_set).with(:DELAY, 100.0) expect(described_class).to receive(:const_set).with(:BANNER, "Input: ./file.txt") allow(described_class::Generators::Input).to receive(:new) described_class.generate(options) end it "calls Generators::Input.new" do allow(described_class::Cell).to receive(:const_set) allow(described_class).to receive(:const_set) expect(described_class::Generators::Input).to receive(:new).with(options) described_class.generate(options) end end context "without options['input']" do before { options["seed"] = 1337 } it "defines configurations constants" do expect(described_class::Cell).to receive(:const_set).with(:LIVE_CELL, "L") expect(described_class::Cell).to receive(:const_set).with(:DEAD_CELL, "D") expect(described_class).to receive(:const_set).with(:DELAY, 100.0) expect(described_class).to receive(:const_set).with(:BANNER, "Seed: 1337") allow(described_class::Generators::Seed).to receive(:new) described_class.generate(options) end it "calls Generators::Seed.new" do allow(described_class::Cell).to receive(:const_set) allow(described_class).to receive(:const_set) expect(described_class::Generators::Seed).to receive(:new).with(options) described_class.generate(options) end end end describe ".run" do let(:universe) { GameOfLife::Universe.new(width: 3, height: 3) } it "calls universe#evolve! and #{described_class}.run" do # allow kernel loop tiwce allow(Kernel).to receive(:loop).and_yield.and_yield expect(described_class).to receive(:render).with(universe).twice expect(universe).to receive(:evolve!).twice described_class.run(universe) end end describe ".render" do let(:universe) { GameOfLife::Universe.new(width: 3, height: 3) } # rubocop:disable RSpec/AnyInstance it "renders the game of life presentation" do stub_const("BANNER", "") stub_const("DELAY", 100.0) expect_any_instance_of(Kernel).to receive(:print).with("\e[?1049h\e[H") expect_any_instance_of(Kernel).to receive(:print).twice expect_any_instance_of(Kernel).to receive(:sleep) described_class.render(universe) end # rubocop:enable RSpec/AnyInstance end end