RSS

Monthly Archives: September 2012

Chasing Heisenbugs from an AKKA actor integration test with awaitility

Abstract : Ever had an impression you’re changing what you’re observing by simply observing it? If you so, you may have hit a Heisenbug ([1], [2]). Well, OK, I agree that a more precise definition of “observing” is needed and that we can differentiate between active and passive observing. The thing is one can hardly be a fully passive observer, especially when it comes to testing multi-threaded programs. Assuming you’re really working on the program and not simply looking a video tutorial in which case you will be a fully passive observer (and I would be wondering how the heck you’ve tumbled on my article). In short, when working on (including testing) a program we may introduce Heisenbugs through levels of indirection which are usually hard to spot. In C/C++ such a level of indirection represents uninitialized auto variables which can change every time you run you program. In Java a source of many indirection levels is the platform independence provided by the JVM. In JavaScript a source of Heisenbugs can be processing uncontrolled (browser dependent) events such as the scroll event.

Goal : Chase and fix a Heisenbug within an AKKA actor integration test using Awaitility

Acknowledgement: My gratitude goes to the open source community

An AKKA actor based program : I’ve already given an example of an AKKA actor base program here and you can find the source code here. So to save you and me some time, I’m going to re-use it.

Naïve test case : First we are going to build a test case which is prone to Heisenbugs and then we are going to fix it. I’m going to use JUnit, Spring Test Framework, Mockito, FEST-Assert, and FEST-Reflect to build up the following integration test case (access article’s full source code):

import akka.actor.ActorRef;
import org.honeysoft.akka.Bootstrap;
import org.honeysoft.akka.service.IBusinessService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import static org.fest.reflect.core.Reflection.field;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

@ContextConfiguration(classes = {Bootstrap.class})
@RunWith(SpringJUnit4ClassRunner.class)
public class BusinessActorTest {

    @Autowired
    @Qualifier(Bootstrap.BUSINESS_ACTOR)
    private ActorRef businessActorRef;

    @Autowired
    private IBusinessService businessService;

    @Mock
    private Logger mockBusinessServiceLogger;

    @Before
    public void beforeEach() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void shouldBeValidWhenNoOneIsNull() {
        //GIVEN
        field("logger").ofType(Logger.class).in(businessService).postDecorateWith(mockBusinessServiceLogger);

        //WHEN
        String testString = "test-string";
        businessActorRef.tell(testString);

        //THEN
        verify(mockBusinessServiceLogger, times(1)).info(anyString(), eq(testString));
    }
}

The thing is that this test case is prone to Heisenbugs. More precisely, the verification (at line 50) may pass some times and may fail others. Depending on the speed of execution and thread priority it can happen that the verification comes before the businessService receives the testString for processing. Luckily we have Awaitility at our disposal so the fix is straightforward:

import akka.actor.ActorRef;
import com.jayway.awaitility.Awaitility;
import com.jayway.awaitility.Duration;
import org.fest.assertions.Assertions;
import org.honeysoft.akka.Bootstrap;
import org.honeysoft.akka.service.IBusinessService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.slf4j.Logger;
import org.slf4j.Marker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import static org.fest.reflect.core.Reflection.field;

@ContextConfiguration(classes = {Bootstrap.class})
@RunWith(SpringJUnit4ClassRunner.class)
public class BusinessActorTest {

    @Autowired
    @Qualifier(Bootstrap.BUSINESS_ACTOR)
    private ActorRef businessActorRef;

    @Autowired
    private IBusinessService businessService;

    @Before
    public void beforeEach() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void shouldBeValidWhenNoOneIsNull() throws Exception {
        //GIVEN
        final ConcurrentMap<String, Object> threadSafeMap = new ConcurrentHashMap<String, Object>(1);
        field("logger").ofType(Logger.class).in(businessService).postDecorateWith(new TestLogger(threadSafeMap));

        //WHEN
        String testString = "test-string";
        businessActorRef.tell(testString);

        //THEN
        Awaitility.waitAtMost(Duration.FIVE_SECONDS).until(new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                return !threadSafeMap.isEmpty();
            }
        });

        Assertions.assertThat(threadSafeMap).hasSize(1);
        Assertions.assertThat(threadSafeMap.values().iterator().next()).isEqualTo(testString);
    }

    private static final class TestLogger implements Logger {

        private final ConcurrentMap<String, Object> map;

        private TestLogger(ConcurrentMap<String, Object> map) {
            this.map = map;
        }

        @Override
        public void info(String format, Object arg) {
            map.put(format, arg);
        }

        //Other overridden methods go here
    }
}

So what happened is that instead of mocking our logger target we’ve created a thread-safe TestLogger to help us. Then we’ve added an awaitility block to either wait for at most 5 secs or until our piggy-bag map is not empty. Well, that was all. Not too painful right?

 

Tags: , , , , , , , , , , , , ,

Surviving Mac OS X bash terminal with Midnight Commander (MC)

Abstract : Midnight Commander (MC) is one of the few tools I’m still using since I’ve been inducted into software engineering more than 10 years ago (for comparison I’ve changed 5 integrated development environments (IDE) ). MC is classified as “a visual file manager” but for me is something much more, it’s an engineering booster. In fact it’s my fist IDE (MCedit supports color highlighting for various programming languages). In short, MC is one of those few things that changes your world. That’s why I’m going to show you how you can painlessly integrate MC with Mac OS X bash terminal.

Goal : Configuring Midnight Commander under Mac OS X bash terminal

Acknowledgement : My gratitude goes to the open source community and especially to:
Miguel de Icaza – creator of Midnight Commander, Gnome, Mono, Gnumeric (and I guess other cool stuff)

Let’s get started!

Installation: I’m a huge fan of homebrew (thanks Max!) and I advise you to install it and work with it. In short, Homebrew is “the missing package manager for OS X”. Installing Homebrew is as easy as running the following command in the terminal prompt:

ruby <(curl -fsSkL raw.github.com/mxcl/homebrew/go)   

Once you have Homebrew, installing MC is exactly three words:

brew install mc

Side note: You may wish to install bash-completion and take advantage of the Tab key on the terminal prompt. If so, do the following:

brew install bash-completion

… and ensure that your .bash_profile has the following content inside:

if [ -f `brew --prefix`/etc/bash_completion ]; then
    . `brew --prefix`/etc/bash_completion
fi

Note that you have to perform:

source .bash_profile 

or re-open your terminal for the changes to take effect.

Mac OS X – fixing the shortcuts mess: No, I have no idea why the Apple guys have created such a big mess with the keyboard shortcuts. And yes, it’s up to you, my dearest reader, to decide if you want to fix it as I suggest. Here is what I’ve done so that I can use my Functional keys (the F keys):

Go to:

System Preferences -> Keyboard -> Keyboard and Shortcuts

… and make sure you have All controls checked

After that go to:

System Preferences -> Keyboard -> Keyboard

… and make sure you have Use all F1, F2 etc. keys as standard function keys checked

Next open a Terminal and go to:

Terminal -> Preferences ... -> Keyboard

… and make sure you have Use option as meta key unchecked.

Now, every F9 will enable MC menu bar and every F10 button press will exit MC (instead of doing a complex fingers split which may not work but which will certainly hurt your hand).

Playing with MC shortcuts: What about the cool MC shortcuts? Before I present you with some of the shortcuts mapping you should be aware that pressing two times the Esc button will close any MC pop-up or search box, and that there are three types of shortcuts:

  1. Pressing (and releasing) Esc and then pressing another key
  2. Pressing and holding Ctrl and then pressing another key
  3. Using the Functional key

Here are some mappings:

----- Esc -----
Quick change directory: Esc + c
Quick change directory history: Esc + c and then Esc + h
Quick change directory previous entry: Esc + c and then Esc + p
Command line history: Esc + h
Command line previous command: Esc + p
View change: Esc + t (each time you do this shortcut a new directory view will appear)
Print current working directory in command line: Esc + a
Switch between background command line and MC: Ctrl + o
Search/Go to directory in active panel: Esc + s / Ctrl + s then start typing directory name
Open same working directory in the inactive panel: Esc + i
Open parent working directory in the inactive panel: Esc + o
Go to top of directory in active pane: Esc + v / Esc + g
Go to bottom of directory in active pane: Esc + j / Ctrl + c
Go to previous directory: Esc + y
Search pop-up: Esc + ?
----- Ctrl -----
Refresh active panel: Ctrl + r
Selecting files and directories: Ctrl + t
Switch active <-> inactive panels: Ctrl + i
Switch active <-> inactive panels content: Ctrl + u
Execute command / Open a directory: Ctrl + j
----- F -----
F1: help
F2: user menu
F3: read file / open directory
F4: edit file
F5: copy file or direcotry
F6: move file or directory
F7: create directory
F8: delete file / directory
F9: open menu bar
F10: exit MC

Keeping working directory after exiting MC: Now, I find this MC feature really cool but of course it’s up to you to decide if you want to enabled it or not. In case you want, make sure you have the following (or similar, depending on the midnight commander version) line in your .bash_profile:

alias mc=". /usr/local/opt/midnight-commander/libexec/mc/mc-wrapper.sh"

Note that you have to perform:

source .bash_profile 

or re-open your terminal for the changes to take effect.

 
14 Comments

Posted by on September 9, 2012 in bash, homebrew, Mac OS X, Midnight Commander

 

Tags: , ,