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
# [...]