Friday, April 29, 2011

Using googletest and googlemock in Eclipse

Since my answer on Stack Overflow doesn't seem to be getting any attention (and thereby helping no-one), I figured I'd post it here. :)

Here's the summary for people already familiar with Eclipse:
  1. Created a new C++ project in Eclipse (I chose Executable > Empty Project)
  2. Downloaded googletest 1.5.0, untarred, and ran ./scripts/fuse_gtest_files.py . /contrib
  3. Back in Eclipse, excluded the contrib directory from the Release build configuration, and added /contrib to the include directories (odd, I know)
  4. Added a src directory and added a class named Foo (see below for the contents of Foo.h--I left Foo.cpp empty for now)
  5. Added a test directory in Eclipse, excluded it from the Release build configuration, added /contrib to the include directories, and added new source files FooTest.cpp and AllTests.cpp (see below for contents)
  6. Built and ran the project

Foo.h
#ifndef FOO_H_
#define FOO_H_
class Foo {
public:
virtual ~Foo();
Foo();
bool foo(void) { return true; }
};
#endif /* FOO_H_ */

FooTest.cpp
#include "gtest/gtest.h"
#include "Foo.h"
namespace {
class FooTest : public ::testing::Test {
protected:
Foo foo;
};
TEST_F(FooTest, Foo) {
ASSERT_TRUE(foo.foo());
}
}

AllTests.cpp
#include "gtest/gtest.h"
#include "FooTest.cpp"
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

Here are the detailed steps:
  1. In Eclipse, open the File menu and select New > C++ Project
  2. Project Type: Executable > Empty Project
  3. Toolchain: Linux GCC
  4. Click Finish
  5. Open a terminal and cd /tmp
  6. wget http://googletest.googlecode.com/files/gtest-1.5.0.tar.bz2
  7. cd gtest-1.5.0/
  8. ./scripts/fuse_gtest_files.py . /contrib
  9. Back in Eclipse, right-click on the project folder in the Project Explorer pane, then select Refresh
  10. In the Project Explorer pane, right-click on the contrib folder, select **Exclude from build...*, untick only the Release box, and click OK
  11. Right-click on the contrib folder and select Properties > C/C++ Build > Settings > Tool Settings tab > GCC C++ Compiler > Directories
  12. Click on the Add... button, then the Workspace... button, then select /contrib and click OK to add the directory
  13. Click OK once more to accept your changes to the build settings
  14. Right-click on the project in the Project Explorer pane and select New > Folder, enter src as its name, and click OK
  15. Right-click on the src folder in the Project Explorer pane and select New > Class, name it Foo, then click OK (see above for contents of Foo.h; Foo.cpp can be left as is)
  16. Right-click on the project in the Project Explorer pane and select New > Folder, enter test as its name, and click OK
  17. Follow the steps above to add /contrib and /src as include directories to the test directory
  18. Right-click on the test folder, then select New > Source File to add AllTests.cpp to the test folder, then repeat the same steps to add FooTest.cpp (see above for contents)
  19. Right-click on FooTest.cpp and select Exclude from build..., click the Select All button, then OK
  20. Right-click on the project in the Project Explorer pane, and select Properties > C/C++ Build > Settings > Tool Settings tab > GCC C++ Linker > Libraries, then click the Add... button, enter pthread (required by googletest), click OK to add the library, then OK once more to accept the changes
  21. Hit Ctrl-b to build the project
  22. Hit Ctrl-F11 to run the project
  23. Victory!

Monday, April 11, 2011

nagios-mode for Emacs

Thanks for nagios-mode.el, Michael Orlitzky! Here's how I achieved syntax highlighting-induced joy for Nagios config files in XEmacs Instant Classic (if you have to ask, you don't deserve to know ;).
  1. Browse to http://michael.orlitzky.com/git/?p=nagios-mode.git;a=blob_plain;f=nagios-mode.el;hb=HEAD
  2. Save file to something like ~/.xemacs/user_lisp/nagios-mode.el
  3. If you haven't already included ~/.xemacs/user_lisp in your load path, do so now by adding the following to your ~/.xemacs/custom.el:
    (add-to-list 'load-path "$HOME/.xemacs/user_lisp/")
  4. Now, load nagios-mode.el by adding the following to your ~/.xemacs/custom.el:
    (autoload 'nagios-mode "$HOME/.xemacs/user_lisp/nagios-mode.el"
    "Major mode for editing Nagios config files" t)
  5. Finally, you'll want nagios-mode.el automatically enabled for Nagios config files, so add the following to your ~/.xemacs/custom.el:
    ; nagios-mode
    (add-to-list 'auto-mode-alist
    '("nagios-config/objects/.+\\.cfg$" . nagios-mode))

You're welcome. ;)

Wednesday, April 06, 2011

Ruby Light #6

This is the most complicated bit of meta-programming in Ruby Light so far. Johan and I cooked it up; the beautiful plumbing is his, the basis of the instance_exec rebinding trick is mine.

The intent is as follows: for each request to a Rails controller, redirect unless all of the URL query parameters you get are valid.

And this is how it actually works:
  1. When Rails loads the FooController class:
    1. FooController mixes in the ParamsFilter module, resulting in it getting the known_params and parse_known_params class methods and the redirect_if_unknown_params and select_unknown instance methods (et al.).
    2. FooController calls the known_params class method to always accept the "limit" and "offset" params, but only accept the "bar" param when the Foo object is of type "bar".
  2. On each request:
    1. FooController's before_filter calls its redirect_if_unknown_params instance method.
    2. redirect_if_unknown_params rejects unknown parameters using the private select_unknown instance method.
    3. select_unknown calls known_param? on each param.
    4. If the param has a :when component, known_param? executes its value (which is a block)--and here's the cool part--in the context of the controller instance! This is cool because Proc objects normally operate in the context in which they were defined (i.e. they are closures). Calling instance_eval or instance_exec with a Proc argument (which is not the same thing as a block!) results in the Proc's binding being changed to context where it is called. This results in self, which was the FooController class when the Proc was defined, being set to the FooController instance on which known_param? is executing.


And now, the code:

app/controllers/foo_controller.rb
class FooController < ApplicationController
include ParamsFilter

before_filter :redirect_if_unknown_params
known_params :limit, :offset, :only => :bar, :when => lambda {@type == 'bar'}
# [...]


lib/params_filter.rb
module ParamsFilter
def self.included(base)
base.send :extend, ClassMethods
base.send :include, InstanceMethods
end

module ClassMethods
attr_reader :known_params

def known_params(*args, &block)
@@known_params ||= HashWithIndifferentAccess.new
@@known_params.merge! parse_known_params(*args, &block)
end

def parse_known_params(*args, &block)
HashWithIndifferentAccess.new.tap do |known_params|
options = (args.last.is_a? Hash) ? args.pop.dup : {}
options[:when] = block if block_given?

args.each do |param|
known_params[param] = options
end
end
end
end

module InstanceMethods
def known_params(*args)
@known_params ||= HashWithIndifferentAccess.new
@known_params.merge! self.class.parse_known_params(*args)
end

def redirect_if_unknown_params
unknown = select_unknown request.query_parameters, request.path_parameters
redirect_without_unknown_params unknown unless unknown.empty?
end

private

def select_unknown(query_params = {}, path_params = {})
query_params.reject {|param, _| known_param? param, path_params}
end

def known_param?(param, params)
if options = param_options(param)
if action_matches_params? options[:only], params
options[:when].nil? || instance_exec(param, &options[:when])
end
end
end

def redirect_without_unknown_params(unknown_params)
# Actually perform redirect
end

# [...]
end
end

Friday, April 01, 2011

Ruby Light #5

This is a remix of Ruby Light #1, done better (IMHO):

test/lib/redirect_io.rb
require 'stringio'

module RedirectIo
def setup
$stderr = @stderr = StringIO.new
$stdin = @stdin = StringIO.new
$stdout = @stdout = StringIO.new
super
end

def teardown
$stderr = STDERR
$stdin = STDIN
$stdout = STDOUT
super
end
end


test/unit/timestamp_logger_test.rb
require 'test_helper'
require 'lib/generic_test_helper.rb' # from Ruby Light #3
require 'lib/redirect_io'

class TimestampLoggerTest < Test::Unit::TestCase
include GenericTestHelper
include RedirectIo

LOG_LEVELS = [:error, :warn, :info, :debug]

def setup
super # RedirectIO.setup needs to run

@logger = TimestampLogger.new $stdout
@logger.level = Logger::DEBUG
end

def test_timestamps
LOG_LEVELS.each do |level|
@logger.send level, 'foo'
end

@stdout.string.split("\n").each do |line|
assert_match /^#{TIMESTAMP_MATCHER} /, line
end
end

def test_labels
LOG_LEVELS.each do |level|
@logger.send level, 'foo'
end

[:error, :warn].each do |level|
assert_match /^#{TIMESTAMP_MATCHER} \[#{level.to_s.upcase}\] /, @stdout.string
end
end

Friday, March 11, 2011

Ruby Light #4

eval_string.rb
class EvalString < String
def eval(b)
b.eval String.new(self)
end
end


irb session
>> foo = 1
=> 1
>> bar = 2
=> 2
>> str = EvalString.new "foo+bar"
=> "foo+bar"
>> str
=> "foo+bar"
>> str.eval(binding)
=> 3

Thursday, March 10, 2011

Ruby Light #3

This insanity is my own:

generic_test_helper.rb
require 'flexmock'

module GenericTestHelper
include FlexMock::TestCase

TIMESTAMP_MATCHER = '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+]\d{2}:\d{2}'

def clear_mocked_method(mock, method)
return unless mock.is_a? FlexMock

mock.instance_eval { @expectations.delete method.to_sym }
end

def mock_method(mock, method, retval, with = nil)
mock = flexmock mock unless mock.is_a? FlexMock
clear_mocked_method mock, method

if with.nil?
mock.should_receive(method).and_return retval
else
mock.should_receive(method).with(with).and_return retval
end
end
end


foo_test.rb
require 'generic_test_helper'
class FooTest < Test::Unit::TestCase
include GenericTestHelper

def test_exceptions_are_logged
exception = nil
begin
1 / 0
rescue Exception => exception
end

mock_database_lookups
clear_mocked_method Bar.instance_eval{@flexmock_proxy}.mock, :find_by_id
flexmock(Bar).should_receive(:find_by_id).and_raise(exception)

assert_nothing_raised { Foo.run 0 }
assert_match /exception caught: #{Regexp.escape exception.message} #{exception.backtrace.map{|str| Regexp.escape(str)}.join "\n#{TIMESTAMP_MATCHER}" }/,
@stdout.string
end
end

Friday, March 04, 2011

LXRing Ruby

As those of you who follow me on StackOverflow already know (Anyone? Bueller? Anyone?), I need to browse the source code of the Ruby interpreter. Getting no immediate answer, I set up LXR myself:

: josh@josh; cat /etc/issue
Ubuntu 10.10 \n \l
: josh@josh; sudo aptitude install apache2 lxr
: josh@josh; sudo vim /etc/apache2/sites-available/default
: josh@josh; tail -6 /etc/apache2/sites-available/default
Alias /lxr /usr/share/lxr

Options All
AllowOverride All

: josh@josh; sudo vim /usr/share/lxr/http/.htaccess
: josh@josh; sudo cat /usr/share/lxr/http/.htaccess

SetHandler cgi-script

: josh@josh; sudo mkdir -p /usr/share/lxr/source/1.8.7-p334
: josh@josh; cd /usr/share/lxr/source/1.8.7-p334
: josh@josh; sudo tar xvjf /tmp/ruby-1.8.7.tar.bz2
: josh@josh; sudo mv ruby-1.8.7 ruby
: josh@josh; sudo vim /usr/share/lxr/source/versions
: josh@josh; sudo cat /usr/share/lxr/source/versions
1.8.7-p334
: josh@josh; sudo ln -s /usr/share/lxr/source/1.8.7-p334 /usr/share/lxr/source/defversion
: josh@josh; sudo genxref ruby
Starting pass 1: Collect identifier definitions.
(Pass 1) vms/vmsruby_private.c (993), file 1 of 262…
[...]
(Pass 3) identifier 8200 of maximum 17136…
(Pass 3) identifier 8300 of maximum 17136…
Completed pass 3 (0s):Information on 8316 identifiers dumped to disk.
: josh@josh; sudo chmod -R o+r .
: josh@josh; sudo vim /usr/share/lxr/http/lxr.conf
: josh@josh; sudo cat /usr/share/lxr/http/lxr.conf
# Configuration file.
# [...]
# The source is here.
sourceroot: /usr/share/lxr/source/$v/ruby/
srcrootname: Ruby
# [...]

Thursday, March 03, 2011

Ruby Light #2

This is the work of one of my colleagues here at TestFreaks, and is the coolest little meta-programming trick I've seen in a while!

code_timer_helper.rb
module CodeTimerHelper

# Creates a CodeTimer section around the method
#
def timed_method(timer, method_name, section_name = method_name.to_s)
eigenclass = (class << self; self; end)
eigenclass.class_eval do

define_method method_name do |*args, &block|
timer.start(section_name)
begin
super(*args, &block)
ensure
timer.stop(section_name)
end
end
end
end
end

Thursday, February 24, 2011

Ruby Light #1

The Ruby Light series is for Ruby code dumps that amuse me for one reason or another, possibly including commentary.

Here's the first one:

redirect_stdio.rb
# See: http://thinkingdigitally.com/archive/capturing-output-from-puts-in-ruby/

require 'stringio'

module Kernel
def capture_stdout(out = StringIO.new)
begin
$stdout = out
yield
ensure
$stdout = STDOUT
end

out
end

def use_my_stdin(instream)
begin
$stdin = instream
yield
ensure
$stdin = STDIN
end

instream
end
end


converter_test.rb
require 'lib/redirect_stdio'
require 'lib/converter.rb'

class ConverterTest < Test::Unit::TestCase
def test_convert
input_data = [
[ # headers
'rank', # index 0
'', # index 1
'', # index 2
'', # index 3
'', # index 4
'', # index 5
'name', # index 6
'category', # index 7
],
[ # data line 0
'123', # index 0
'', # index 1
'', # index 2
'', # index 3
'', # index 4
'', # index 5
'Foo', # index 6
'Bar', # index 7
],
]

converter = Converter.new
instream = StringIO.new input_data.map{|line| line.join("\t")}.join("\n")
output = capture_stdout do
use_my_stdin(instream) { converter.convert }
end

input_data[1..-1].each do |data|
assert_match /^#{data[6]} is rank #{data[0]} in #{data[7]}$/, output.string
end
end
end

Sunday, January 09, 2011

Wheaton's Law

In which several pleas for a saner New Year are put forward, unified by the concept of Wheaton's Law and made necessary by the attempted assassination of Arizona Rep. Gabrielle Giffords.
  1. Dial it Back: To illustrate the premise that the sort of incendiary remarks made regularly on Fox "News", on the radio (e.g. Messrs. Rush Limbaugh, Glen Beck, et al.) and by an increasing number of politicians, let me quote the news piece linked above:
    At a news conference Saturday night, a clearly emotional Dupnik, who has been close to both Giffords and Roll, repeatedly cited what he characterized as the "vitriol" that has infected political discourse. He said that his own state has become "the mecca for prejudice and bigotry."

    There is reason to believe, he said, that the shooting suspect "may have a mental issue," adding that people like that "are especially susceptible to vitriol."
    And here's a treasure trove of absurdity from The Huffington Post:

    • A statement by Giffords herself, earlier last year and eerily prescient:
      Giffords expressed similar concern, even before the shooting. In an interview after her office was vandalized, she referred to the animosity against her by conservatives, including Sarah Palin's decision to list Giffords' seat as one of the top "targets" in the midterm elections.

      "For example, we're on Sarah Palin's targeted list, but the thing is, that the way that she has it depicted has the crosshairs of a gun sight over our district. When people do that, they have to realize that there are consequences to that action," Giffords said in an interview with MSNBC.

    • It just gets worse:
      During his campaign effort to unseat Giffords in November, Republican challenger Jesse Kelly held fundraisers where he urged supporters to help remove Giffords from office by joining him to shoot a fully loaded M-16 rifle. Kelly is a former Marine who served in Iraq and was pictured on his website in military gear holding his automatic weapon and promoting the event.

      "I don't see the connection," between the fundraisers featuring weapons and Saturday's shooting, said John Ellinwood, Kelly's spokesman. "I don't know this person, we cannot find any records that he was associated with the campaign in any way. I just don't see the connection.

      "Arizona is a state where people are firearms owners - this was just a deranged individual."

    • And finally:
      Law enforcement officials said members of Congress reported 42 cases of threats or violence in the first three months of 2010, nearly three times the 15 cases reported during the same period a year earlier. Nearly all dealt with the health care bill, and Giffords was among the targets.

    So next time you are tempted to say something extreme, nasty, or just mean, stop and make sure your statement is proportional. Words to steer clear of include, but are not limited to: Hitler, Nazi, Stalin, socialist / communist / liberal (which seem to be synonyms in right-wing political discourse in America), devil, terrorist, enemy of the state, un-American, Satan (Prince of Lies), evil, etc.

  2. Guns. What are they good for? (Absolutely nothing?) Though the weapon allegedly used by Jared Lee Loughner (is it an unspoken rule that all political assassins must be known by all three names?) was only a handgun (and not a fully automatic assault rifle, which are also bizarrely legal to buy in the US), he still managed to kill six people and wound 13 others. If we must own guns, is it too much to ask that they be limited to hunting rifles, shotguns, and semi-automatic handguns with six-round clips, the last of which must be locked in a gun safe at a shooting range? Full disclosure: I used to be a stiff opponent of gun control, in any way, shape or form, but for the sole reason that I considered weapons as the last resort of citizens against an illegimate government. Thinking at some length on the last point, it finally dawned on me that even a fully automatic assault rifle would not do much against tanks, planes, and missiles (see: Ruby Ridge, Waco, etc.), thus removing my only point of contention with the idea of gun control. If you think it is wrong for the government to tell you that you cannot own a deadly weapon, would you be OK for it to be legal to purchase a tank? An anti-aircraft gun? An anti-personnel mines? A bomb? Need I go on?

OK, so really only two points. In the spirit of the above, I've made only two New Year's resolutions for 2011, and they're ones that, if I cannot keep, say something about my character:
  1. Make an earnest attempt to abide by Wheaton's Law, and in the instances where I fail,
  2. Make a sincere apology to the victim.

Happy New Year, and let's work together to make 2011 a less ridiculous year.