X-Git-Url: https://review.fuel-infra.org/gitweb?a=blobdiff_plain;f=spec%2Funit%2Futil_spec.rb;fp=spec%2Funit%2Futil_spec.rb;h=a5f60877e8f44e907e2c747574882eed0d522e48;hb=b87d2f4e68281062df1913440ca5753ae63314a9;hp=0000000000000000000000000000000000000000;hpb=ab0ea530b8ac956091f17b104ab2311336cfc250;p=packages%2Fprecise%2Fmcollective.git diff --git a/spec/unit/util_spec.rb b/spec/unit/util_spec.rb new file mode 100755 index 0000000..a5f6087 --- /dev/null +++ b/spec/unit/util_spec.rb @@ -0,0 +1,494 @@ +#!/usr/bin/env rspec + +require 'spec_helper' + +module MCollective + describe Util do + before do + class MCollective::Connector::Stomp "connector_plugin", :class => MCollective::Connector::Stomp.new} + end + + describe "#t" do + it "should correctly translate the message" do + I18n.expects(:t).with("PLMC1.pattern", {:rspec => "test"}) + I18n.expects(:t).with("PLMC1.expanded", {:rspec => "test"}) + Util.t(:PLMC1, :rspec => "test") + Util.t("PLMC1.expanded", :rspec => "test") + end + end + + describe "#windows?" do + it "should correctly detect windows on unix platforms" do + RbConfig::CONFIG.expects("[]").returns("linux") + Util.windows?.should == false + end + + it "should correctly detect windows on windows platforms" do + RbConfig::CONFIG.expects("[]").returns("win32") + Util.windows?.should == true + end + end + + describe "#setup_windows_sleeper" do + it "should set up a thread on the windows platform" do + Thread.expects(:new) + Util.expects("windows?").returns(true).once + Util.setup_windows_sleeper + end + + it "should not set up a thread on other platforms" do + Thread.expects(:new).never + Util.expects("windows?").returns(false).once + Util.setup_windows_sleeper + end + end + + describe "#has_cf_class?" do + before do + logger = mock + logger.stubs(:log) + logger.stubs(:start) + Log.configure(logger) + + config = mock + config.stubs(:classesfile).returns("/some/file") + Config.expects(:instance).returns(config) + end + + it "should read the classes lines from the correct file" do + File.expects(:readlines).with("/some/file") + + Util.has_cf_class?("test") + end + + it "should support regular expression searches" do + File.stubs(:readlines).returns(["test_class_test"]) + String.any_instance.expects(:match).with("^/").returns(true) + String.any_instance.expects(:match).with(Regexp.new("class")).returns(true) + + Util.has_cf_class?("/class/").should == true + end + + it "should support exact string matches" do + File.stubs(:readlines).returns(["test_class_test"]) + String.any_instance.expects(:match).with("^/").returns(false) + String.any_instance.expects(:match).with(Regexp.new("test_class_test")).never + + Util.has_cf_class?("test_class_test").should == true + end + + it "should report a warning when the classes file cannot be parsed" do + File.stubs(:readlines).returns(nil) + Log.expects(:warn).with("Parsing classes file '/some/file' failed: NoMethodError: undefined method `each' for nil:NilClass") + + Util.has_cf_class?("test_class_test").should == false + end + end + + describe "#shellescape" do + it "should return '' for empty strings" do + Util.shellescape("").should == "''" + end + + it "should quote newlines" do + Util.shellescape("\n").should == "'\n'" + end + + it "should escape unwanted characters" do + Util.shellescape("foo;bar").should == 'foo\;bar' + Util.shellescape('foo`bar').should == 'foo\`bar' + Util.shellescape('foo$bar').should == 'foo\$bar' + Util.shellescape('foo|bar').should == 'foo\|bar' + Util.shellescape('foo&&bar').should == 'foo\&\&bar' + Util.shellescape('foo||bar').should == 'foo\|\|bar' + Util.shellescape('foo>bar').should == 'foo\>bar' + Util.shellescape('foo:broadcast, + :agent=>"test", + :collective=>"collective1"}, + {:type=>:broadcast, + :agent=>"test", + :collective=>"collective2"}] + end + + it "should validate given collective" do + Config.instance.stubs(:collectives).returns(["collective1", "collective2"]) + + expect { + Util.make_subscriptions("test", :broadcast, "test") + }.to raise_error("Unknown collective 'test' known collectives are 'collective1, collective2'") + end + + it "should return a single subscription array given a collective" do + Config.instance.stubs(:collectives).returns(["collective1", "collective2"]) + Util.make_subscriptions("test", :broadcast, "collective1").should == [{:type=>:broadcast, :agent=>"test", :collective=>"collective1"}] + end + end + + describe "#subscribe" do + it "should subscribe to multiple topics given an Array" do + subs1 = {:agent => "test_agent", :type => "test_type", :collective => "test_collective"} + subs2 = {:agent => "test_agent2", :type => "test_type2", :collective => "test_collective2"} + + MCollective::Connector::Stomp.any_instance.expects(:subscribe).with("test_agent", "test_type", "test_collective").once + MCollective::Connector::Stomp.any_instance.expects(:subscribe).with("test_agent2", "test_type2", "test_collective2").once + + Util.subscribe([subs1, subs2]) + end + + it "should subscribe to a single topic given a hash" do + MCollective::Connector::Stomp.any_instance.expects(:subscribe).with("test_agent", "test_type", "test_collective").once + Util.subscribe({:agent => "test_agent", :type => "test_type", :collective => "test_collective"}) + end + end + + describe "#unsubscribe" do + it "should unsubscribe to multiple topics given an Array" do + subs1 = {:agent => "test_agent", :type => "test_type", :collective => "test_collective"} + subs2 = {:agent => "test_agent2", :type => "test_type2", :collective => "test_collective2"} + MCollective::Connector::Stomp.any_instance.expects(:unsubscribe).with("test_agent", "test_type", "test_collective").once + MCollective::Connector::Stomp.any_instance.expects(:unsubscribe).with("test_agent2", "test_type2", "test_collective2").once + + Util.unsubscribe([subs1, subs2]) + end + + it "should subscribe to a single topic given a hash" do + MCollective::Connector::Stomp.any_instance.expects(:unsubscribe).with("test_agent", "test_type", "test_collective").once + Util.unsubscribe({:agent => "test_agent", :type => "test_type", :collective => "test_collective"}) + end + end + + describe "#empty_filter?" do + it "should correctly compare empty filters" do + Util.empty_filter?(Util.empty_filter).should == true + end + + it "should treat an empty hash as an empty filter" do + Util.empty_filter?({}).should == true + end + + it "should detect non empty filters correctly" do + filter = Util.empty_filter + filter["cf_class"] << "meh" + + + Util.empty_filter?(filter).should == false + end + end + + describe "#empty_filter" do + it "should create correct empty filters" do + Util.empty_filter.should == {"fact" => [], "cf_class" => [], "agent" => [], "identity" => [], "compound" => []} + end + end + + describe "#default_options" do + it "should supply correct default options" do + Config.instance.stubs(:default_discovery_options).returns([]) + empty_filter = Util.empty_filter + config_file = Util.config_file_for_user + + Util.default_options.should == {:verbose => false, :disctimeout => nil, :timeout => 5, :config => config_file, :filter => empty_filter, :collective => nil, :discovery_method => nil, :discovery_options => []} + end + end + + describe "#has_fact?" do + it "should handle missing facts correctly" do + MCollective::Facts.expects("[]").with("foo").returns(nil).once + Util.has_fact?("foo", "1", "==").should == false + end + + it "should handle regex in a backward compatible way" do + MCollective::Facts.expects("[]").with("foo").returns("foo").times(6) + Util.has_fact?("foo", "foo", "=~").should == true + Util.has_fact?("foo", "/foo/", "=~").should == true + Util.has_fact?("foo", "foo", "=~").should == true + Util.has_fact?("foo", "bar", "=~").should == false + Util.has_fact?("foo", "/bar/", "=~").should == false + Util.has_fact?("foo", "bar", "=~").should == false + end + + it "should evaluate equality" do + MCollective::Facts.expects("[]").with("foo").returns("foo").twice + Util.has_fact?("foo", "foo", "==").should == true + Util.has_fact?("foo", "bar", "==").should == false + end + + it "should handle numeric comparisons correctly" do + MCollective::Facts.expects("[]").with("foo").returns("1").times(8) + Util.has_fact?("foo", "2", ">=").should == false + Util.has_fact?("foo", "1", ">=").should == true + Util.has_fact?("foo", "2", "<=").should == true + Util.has_fact?("foo", "1", "<=").should == true + Util.has_fact?("foo", "1", "<").should == false + Util.has_fact?("foo", "1", ">").should == false + Util.has_fact?("foo", "1", "!=").should == false + Util.has_fact?("foo", "2", "!=").should == true + end + + it "should handle alphabetic comparisons correctly" do + MCollective::Facts.expects("[]").with("foo").returns("b").times(8) + Util.has_fact?("foo", "c", ">=").should == false + Util.has_fact?("foo", "a", ">=").should == true + Util.has_fact?("foo", "a", "<=").should == false + Util.has_fact?("foo", "b", "<=").should == true + Util.has_fact?("foo", "b", "<").should == false + Util.has_fact?("foo", "b", ">").should == false + Util.has_fact?("foo", "b", "!=").should == false + Util.has_fact?("foo", "a", "!=").should == true + end + end + + describe "#parse_fact_string" do + it "should parse old style regex fact matches" do + Util.parse_fact_string("foo=/bar/").should == {:fact => "foo", :value => "/bar/", :operator => "=~"} + Util.parse_fact_string("foo = /bar/").should == {:fact => "foo", :value => "/bar/", :operator => "=~"} + end + + it "should parse old style equality" do + Util.parse_fact_string("foo=bar").should == {:fact => "foo", :value => "bar", :operator => "=="} + Util.parse_fact_string("foo = bar").should == {:fact => "foo", :value => "bar", :operator => "=="} + end + + it "should parse regex fact matches" do + Util.parse_fact_string("foo=~bar").should == {:fact => "foo", :value => "bar", :operator => "=~"} + Util.parse_fact_string("foo =~ bar").should == {:fact => "foo", :value => "bar", :operator => "=~"} + end + + it "should treat => like >=" do + Util.parse_fact_string("foo=>bar").should == {:fact => "foo", :value => "bar", :operator => ">="} + Util.parse_fact_string("foo => bar").should == {:fact => "foo", :value => "bar", :operator => ">="} + end + + it "should treat =< like <=" do + Util.parse_fact_string("foo= "foo", :value => "bar", :operator => "<="} + Util.parse_fact_string("foo =< bar").should == {:fact => "foo", :value => "bar", :operator => "<="} + end + + it "should parse less than or equal" do + Util.parse_fact_string("foo<=bar").should == {:fact => "foo", :value => "bar", :operator => "<="} + Util.parse_fact_string("foo <= bar").should == {:fact => "foo", :value => "bar", :operator => "<="} + end + + it "should parse greater than or equal" do + Util.parse_fact_string("foo>=bar").should == {:fact => "foo", :value => "bar", :operator => ">="} + Util.parse_fact_string("foo >= bar").should == {:fact => "foo", :value => "bar", :operator => ">="} + end + + it "should parse less than" do + Util.parse_fact_string("foo "foo", :value => "bar", :operator => "<"} + Util.parse_fact_string("foo < bar").should == {:fact => "foo", :value => "bar", :operator => "<"} + end + + it "should parse greater than" do + Util.parse_fact_string("foo>bar").should == {:fact => "foo", :value => "bar", :operator => ">"} + Util.parse_fact_string("foo > bar").should == {:fact => "foo", :value => "bar", :operator => ">"} + end + + it "should parse greater than" do + Util.parse_fact_string("foo>bar").should == {:fact => "foo", :value => "bar", :operator => ">"} + Util.parse_fact_string("foo > bar").should == {:fact => "foo", :value => "bar", :operator => ">"} + end + + it "should parse not equal" do + Util.parse_fact_string("foo!=bar").should == {:fact => "foo", :value => "bar", :operator => "!="} + Util.parse_fact_string("foo != bar").should == {:fact => "foo", :value => "bar", :operator => "!="} + end + + it "should parse equal to" do + Util.parse_fact_string("foo==bar").should == {:fact => "foo", :value => "bar", :operator => "=="} + Util.parse_fact_string("foo == bar").should == {:fact => "foo", :value => "bar", :operator => "=="} + end + + it "should fail for facts in the wrong format" do + expect { + Util.parse_fact_string("foo") + }.to raise_error("Could not parse fact foo it does not appear to be in a valid format") + end + end + + describe "#colorize" do + it "should not add color codes when color is disabled" do + Config.instance.stubs(:color).returns(false) + Util.colorize(:red, "hello world").should == "hello world" + end + + it "should add color when color is enabled" do + Config.instance.stubs(:color).returns(true) + Util.colorize(:red, "hello world").should == "hello world" + end + end + + describe "#align_text" do + it "should default to 80 if the terminal dimensions are unknown" do + Util.stubs(:terminal_dimensions).returns([0,0]) + + rootdir = File.dirname(__FILE__) + input = File.read("#{rootdir}/../fixtures/util/4.in") + output = File.read("#{rootdir}/../fixtures/util/4.out") + + (Util.align_text(input, nil, 3) + "\n").should == output + end + + it "should return the origional string if console lines are 0" do + result = Util.align_text("test", 0) + result.should == "test" + end + + it "should return the origional string if preamble is greater than console lines" do + result = Util.align_text("test", 5, 6) + result.should == "test" + end + + it "should return a string prefixed by the preamble" do + result = Util.align_text("test") + result.should == " test" + end + + it "should correctly align strings" do + rootdir = File.dirname(__FILE__) + (1..2).each do |i| + input = File.read("#{rootdir}/../fixtures/util/#{i}.in") + output = File.read("#{rootdir}/../fixtures/util/#{i}.out") + + (Util.align_text(input, 158 , 5) + "\n").should == output + end + + input = File.read("#{rootdir}/../fixtures/util/3.in") + output = File.read("#{rootdir}/../fixtures/util/3.out") + + (Util.align_text(input, 30, 0) + "\n").should == output + end + end + + describe "#terminal_dimensions" do + it "should return 0 if there is no tty" do + stdout = mock() + stdout.expects(:tty?).returns(false) + result = Util.terminal_dimensions(stdout) + result.should == [0,0] + end + + it "should return the default dimensions for a windows terminal" do + stdout = mock() + stdout.expects(:tty?).returns(true) + Util.expects(:windows?).returns(true) + result = Util.terminal_dimensions(stdout) + result.should == [80, 40] + end + + it "should return 0 if an exception was raised" do + stdout = mock() + stdout.expects(:tty?).raises("error") + result = Util.terminal_dimensions(stdout) + result.should == [0, 0] + end + + it "should return the correct dimensions if ENV columns and lines are set" do + stdout = mock() + stdout.expects(:tty?).returns(true) + environment = mock() + environment.expects(:[]).with("COLUMNS").returns(5).twice + environment.expects(:[]).with("LINES").returns(5).twice + result = Util.terminal_dimensions(stdout, environment) + result.should == [5,5] + end + + it "should return the correct dimensions if ENV term is set and tput is present" do + stdout = mock() + stdout.expects(:tty?).returns(true) + environment = mock() + environment.expects(:[]).with("COLUMNS").returns(false) + environment.expects(:[]).with("TERM").returns(true) + + Util.expects(:command_in_path?).with("tput").returns(true) + Util.stubs(:`).returns("5") + + result = Util.terminal_dimensions(stdout, environment) + result.should == [5,5] + end + + it "should return the correct dimensions if stty is present" do + stdout = mock() + stdout.expects(:tty?).returns(true) + + environment = mock() + environment.expects(:[]).with("COLUMNS").returns(false) + environment.expects(:[]).with("TERM").returns(false) + + Util.expects(:command_in_path?).with("stty").returns(true) + Util.stubs(:`).returns("5 5") + + result = Util.terminal_dimensions(stdout, environment) + result.should == [5,5] + end + end + + describe "#command_in_path?" do + it "should return true if the command is found" do + File.stubs(:exist?).returns(true) + result = Util.command_in_path?("test") + result.should == true + end + + it "should return false if the command cannot be found" do + File.stubs(:exist?).returns(false) + result = Util.command_in_path?("test") + result.should == false + end + end + + describe "#absolute_path?" do + it "should work correctly validate the path" do + Util.absolute_path?('.', '/', '\\').should == false + Util.absolute_path?('foo/foo', '/', '\\').should == false + Util.absolute_path?('foo\\bar', '/', '\\').should == false + Util.absolute_path?('../foo/bar', '/', '\\').should == false + + Util.absolute_path?('\\foo/foo', '/', '\\').should == true + Util.absolute_path?('\\', '/', '\\').should == true + Util.absolute_path?('/foo', '/', '\\').should == true + Util.absolute_path?('/foo/foo', '/', '\\').should == true + + Util.absolute_path?('.', '/', nil).should == false + Util.absolute_path?('foo/foo', '/', nil).should == false + Util.absolute_path?('foo\\bar', '/', nil).should == false + Util.absolute_path?('../foo/bar', '/', nil).should == false + + Util.absolute_path?('\\foo/foo', '/', nil).should == false + Util.absolute_path?('\\', '/', nil).should == false + Util.absolute_path?('/foo', '/', nil).should == true + Util.absolute_path?('/foo/foo', '/', nil).should == true + end + end + + describe "#versioncmp" do + it "should be able to sort a long set of various unordered versions" do + ary = %w{ 1.1.6 2.3 1.1a 3.0 1.5 1 2.4 1.1-4 2.3.1 1.2 2.3.0 1.1-3 2.4b 2.4 2.40.2 2.3a.1 3.1 0002 1.1-5 1.1.a 1.06} + + newary = ary.sort {|a, b| Util.versioncmp(a,b) } + + newary.should == ["0002", "1", "1.06", "1.1-3", "1.1-4", "1.1-5", "1.1.6", "1.1.a", "1.1a", "1.2", "1.5", "2.3", "2.3.0", "2.3.1", "2.3a.1", "2.4", "2.4", "2.4b", "2.40.2", "3.0", "3.1"] + end + end + end +end