+ All Categories
Home > Technology > Cukeup nyc ian dees on testing embedded systems with cucumber

Cukeup nyc ian dees on testing embedded systems with cucumber

Date post: 13-May-2015
Category:
Upload: skills-matter
View: 961 times
Download: 1 times
Share this document with a friend
Description:
Testing Embedded Systems With Cucumber Cucumber isn't just for web apps. You can test just about anything with it—including embedded systems! In this talk, we'll look at several different facets of driving hardware from Cucumber, including: Connecting to an Arduino using a custom serial protocol Taking advantage of the TCP stack when it's available Building the Cucumber wire protocol into your device What to do when you can't modify the app under test Driving more fully-featured devices such as the BeagleBone or RaspberryPi By the end of the presentation, you'll have a handle on what the various options are for testing embedded devices, and which tradeoffs will apply to your system.
Popular Tags:
75
Testing Embedded Systems With Cucumber Ian Dees • @undees CukeUp! NYC 2013
Transcript
Page 1: Cukeup nyc ian dees on testing embedded systems with cucumber

Testing Embedded Systems With Cucumber

Ian Dees • @undeesCukeUp! NYC 2013

Page 2: Cukeup nyc ian dees on testing embedded systems with cucumber

Plenty of Ruby, but...

Page 3: Cukeup nyc ian dees on testing embedded systems with cucumber

There will be C

Page 4: Cukeup nyc ian dees on testing embedded systems with cucumber

There will be C++

Page 5: Cukeup nyc ian dees on testing embedded systems with cucumber

There will be C#

Page 7: Cukeup nyc ian dees on testing embedded systems with cucumber
Page 8: Cukeup nyc ian dees on testing embedded systems with cucumber

The Embedded Continuum

Page 9: Cukeup nyc ian dees on testing embedded systems with cucumber

almost acomputer

chip andsome ROM

Page 10: Cukeup nyc ian dees on testing embedded systems with cucumber

Simple devicesNo Ruby, no Cucumber

Page 11: Cukeup nyc ian dees on testing embedded systems with cucumber

Gray CodeA simple system to test

Page 12: Cukeup nyc ian dees on testing embedded systems with cucumber

Feature: Gray Code Scenario Outline: Counter When I press the button Then the LEDs should read "<leds>"

Examples: | leds | | ..O | | .OO | | .O. | | OO. | | OOO | | O.O | | O.. | | ... |

Page 13: Cukeup nyc ian dees on testing embedded systems with cucumber

Arduino

Page 14: Cukeup nyc ian dees on testing embedded systems with cucumber

void loop() { button.update(); bool buttonPressed = button.risingEdge();

if (buttonPressed) { counter = (counter + 1) % ENTRIES; updateLeds(); }

delay(50);}

Page 15: Cukeup nyc ian dees on testing embedded systems with cucumber

Drive code directly

Page 16: Cukeup nyc ian dees on testing embedded systems with cucumber

void loop() { button.update(); bool buttonPressed = button.risingEdge();

if (buttonPressed) { counter = (counter + 1) % ENTRIES; updateLeds(); }

delay(50);}

Page 17: Cukeup nyc ian dees on testing embedded systems with cucumber

void loop() { button.update(); bool buttonPressed = button.risingEdge();

if (buttonPressed) { counter = (counter + 1) % ENTRIES; updateLeds(); }

delay(50);}

Page 18: Cukeup nyc ian dees on testing embedded systems with cucumber

static bool isFakeButtonPressed;

class Bounce {public: // ...

bool risingEdge() { bool result = isFakeButtonPressed; isFakeButtonPressed = false; return result; }};

Page 19: Cukeup nyc ian dees on testing embedded systems with cucumber

extern "C" void press() { isFakeButtonPressed = true;}

Page 20: Cukeup nyc ian dees on testing embedded systems with cucumber

const char* leds() { static char buf[LEDS + 1] = {0};

for (int i = 0; i < LEDS; ++i) { buf[i] = STATES[counter][i] == HIGH ? 'O' : '.'; }

return buf;}

Page 21: Cukeup nyc ian dees on testing embedded systems with cucumber

https://github.com/ffi/ffi

FFI

Page 22: Cukeup nyc ian dees on testing embedded systems with cucumber

require 'ffi'

module Arduino extend FFI::Library ffi_lib 'graycode'

attach_function :press, [], :void attach_function :leds, [], :string

attach_function :setup, [], :void attach_function :loop, [], :voidend

Page 23: Cukeup nyc ian dees on testing embedded systems with cucumber

When /^I press the button$/ do Arduino.press Arduino.loopend

Then /^the LEDs should read "(.*?)"$/ do |leds|

expect(Arduino.leds).to eq(leds)end

Page 24: Cukeup nyc ian dees on testing embedded systems with cucumber

Cucumber Wire Protocol

Page 25: Cukeup nyc ian dees on testing embedded systems with cucumber

Cucumber-CPPhttps://github.com/cucumber/cucumber-cpp

Page 26: Cukeup nyc ian dees on testing embedded systems with cucumber

host: localhostport: 3902

features/step_definitions/cucumber.wire

Page 27: Cukeup nyc ian dees on testing embedded systems with cucumber

When /^I press the button$/ do Arduino.press Arduino.loopend

Then /^the LEDs should read "(.*?)"$/ do |expected| expect(Arduino.leds).to eq(expected)end

Page 28: Cukeup nyc ian dees on testing embedded systems with cucumber

WHEN("^I press the button$") { press(); loop();}

THEN("^the LEDs should read \"(.*?)\"$") { REGEX_PARAM(string, expected); BOOST_CHECK_EQUAL(leds(), expected);}

Page 29: Cukeup nyc ian dees on testing embedded systems with cucumber

Bespoke wire server

Page 30: Cukeup nyc ian dees on testing embedded systems with cucumber

http://www.2600.com/code/212/listener.c

listen/accept/read/write

Page 31: Cukeup nyc ian dees on testing embedded systems with cucumber

while(fgets(buf,sizeof buf,rStream)) { respond_to_cucumber(wStream, buf);}

Page 32: Cukeup nyc ian dees on testing embedded systems with cucumber

step_matchesinvoke

Two messages

Page 33: Cukeup nyc ian dees on testing embedded systems with cucumber

Then the LEDs should read "..O"

⬇["step_matches", {"name_to_match": "the LEDs should read"}]

⬇["success", [{"id":"1", "args":[{"val":"..O", "pos":"22"}]}]]

Page 35: Cukeup nyc ian dees on testing embedded systems with cucumber

extern "C" void respond_to_cucumber( FILE* stream, const char* message) { string s = message; Value v; read(s, v);

Array& a = v.get_array(); string type = a[0].get_str();

// handle Cucumber message types

report_success(stream);}

Page 36: Cukeup nyc ian dees on testing embedded systems with cucumber

if (type == "step_matches") { string name = step_name(v);

if (name == "I press the button") { report_success(stream); return; } else if (...)

// ...

}}

Page 37: Cukeup nyc ian dees on testing embedded systems with cucumber

if (type == "step_matches") { string name = step_name(v);

if (name == "I press the button") { report_success(stream); return; } else if (...)

// ...

}}

Page 38: Cukeup nyc ian dees on testing embedded systems with cucumber

if (type == "step_matches") { string name = step_name(v);

if (...) { // ...

} else if (name.find("the LEDs") == 0) const int START = 22; string leds = name.substr(START, 3); report_match(leds, START, stream); return; }}

Page 39: Cukeup nyc ian dees on testing embedded systems with cucumber

Then the LEDs should read "..O"

⬇["invoke", {"id":"1", "args":["..O"]}]

⬇["success", []]

Page 40: Cukeup nyc ian dees on testing embedded systems with cucumber

if (type == "invoke") { string id = step_id(v);

if (id == "0") { press(); loop(); } else if (id == "1") {

// ...

} } }

Page 41: Cukeup nyc ian dees on testing embedded systems with cucumber

if (type == "invoke") { string id = step_id(v);

if (id == "0") { press(); loop(); } else if (id == "1") {

// ...

} } }

Page 42: Cukeup nyc ian dees on testing embedded systems with cucumber

if (type == "invoke") { string id = step_id(v);

if (id == "0") { // ...

} else if (id == "1") { string expected = step_leds(v); if (expected != leds()) { report_failure("LEDs", stream); return; } } }

Page 43: Cukeup nyc ian dees on testing embedded systems with cucumber

https://github.com/hparra/ruby-serialport

Serial

Page 44: Cukeup nyc ian dees on testing embedded systems with cucumber

void loop() { button.update(); bool buttonPressed = button.risingEdge();

if (buttonPressed) { counter = (counter + 1) % ENTRIES; updateLeds(); }

delay(50);}

Page 45: Cukeup nyc ian dees on testing embedded systems with cucumber

void loop() { button.update(); bool buttonPressed = button.risingEdge();

if ( buttonPressed ) { counter = (counter + 1) % ENTRIES; updateLeds();

delay(50);}

Page 46: Cukeup nyc ian dees on testing embedded systems with cucumber

void loop() { button.update(); bool buttonPressed = button.risingEdge();

int command = (Serial.available() > 0 ? Serial.read() : -1);

if (isIncrement(buttonPressed, command)) { counter = (counter + 1) % ENTRIES; updateLeds(); } else if (isQuery(command)) { Serial.write(leds()); Serial.write('\n'); }

delay(50);}

Page 47: Cukeup nyc ian dees on testing embedded systems with cucumber

void loop() { button.update(); bool buttonPressed = button.risingEdge();

int command = (Serial.available() > 0 ? Serial.read() : -1);

if (isIncrement(buttonPressed, command)) { counter = (counter + 1) % ENTRIES; updateLeds(); } else if (isQuery(command)) { Serial.write(leds()); Serial.write('\n'); }

delay(50);}

Page 48: Cukeup nyc ian dees on testing embedded systems with cucumber

void loop() { button.update(); bool buttonPressed = button.risingEdge();

int command = (Serial.available() > 0 ? Serial.read() : -1);

if (isIncrement(buttonPressed, command)) { counter = (counter + 1) % ENTRIES; updateLeds(); } else if (isQuery(command)) { Serial.write(leds()); Serial.write('\n'); }

delay(50);}

Page 49: Cukeup nyc ian dees on testing embedded systems with cucumber

void loop() { button.update(); bool buttonPressed = button.risingEdge();

int command = (Serial.available() > 0 ? Serial.read() : -1);

if (isIncrement(buttonPressed, command)) { counter = (counter + 1) % ENTRIES; updateLeds(); } else if (isQuery(command)) { Serial.write(leds()); Serial.write('\n'); }

delay(50);}

Page 50: Cukeup nyc ian dees on testing embedded systems with cucumber

require 'serialport'

module Arduino @@port = SerialPort.open 2, 9600 at_exit { @@port.close }

def self.press @@port.write '+' end

def self.leds @@port.write '?' @@port.read.strip endend

Page 51: Cukeup nyc ian dees on testing embedded systems with cucumber

Ruby, C#, SpecFlow, Cucumber

Almost a computer

Page 52: Cukeup nyc ian dees on testing embedded systems with cucumber

Feature: Calculator

Scenario: Add two numbers When I multiply 2 and 3 Then I should get 6

Page 53: Cukeup nyc ian dees on testing embedded systems with cucumber

Run Cucumber directly

Page 54: Cukeup nyc ian dees on testing embedded systems with cucumber

rsync -av --delete . remote1:test_path

ssh remote1 'cd test_path && cucumber'

Page 55: Cukeup nyc ian dees on testing embedded systems with cucumber

Drive the GUI

Page 56: Cukeup nyc ian dees on testing embedded systems with cucumber

TestStack Whitehttps://github.com/TestStack/White

Page 57: Cukeup nyc ian dees on testing embedded systems with cucumber

namespace Calc.Spec{ [Binding] public class CalculatorSteps { private Window window;

[Before] public void Before() { Application application = Application.Launch("calc.exe"); window = application.GetWindow( "Calculator", InitializeOption.NoCache); } // ... }}

Page 58: Cukeup nyc ian dees on testing embedded systems with cucumber

namespace Calc.Spec{ [Binding] public class CalculatorSteps { private Window window;

[Before] public void Before() { Application application = Application.Launch("calc.exe"); window = application.GetWindow( "Calculator", InitializeOption.NoCache); } // ... }}

Page 59: Cukeup nyc ian dees on testing embedded systems with cucumber

namespace Calc.Spec{ [Binding] public class CalculatorSteps { private Window window;

[Before] public void Before() { Application application = Application.Launch("calc.exe"); window = application.GetWindow( "Calculator", InitializeOption.NoCache); } // ... }}

Page 60: Cukeup nyc ian dees on testing embedded systems with cucumber

[When(@"I multiply (.*) and (.*)")]public void WhenIMultiply(string a, string b){ window.Keyboard.Enter(a + "*" + b + "=");}

[Then(@"I should get (.*)")]public void ThenIShouldGet(string expected){ window.Get<Label>(expected);}

Page 61: Cukeup nyc ian dees on testing embedded systems with cucumber

Give your app an API

Page 62: Cukeup nyc ian dees on testing embedded systems with cucumber

Mongoose web server

Page 63: Cukeup nyc ian dees on testing embedded systems with cucumber

int main(){ struct mg_context *ctx = mg_start(); mg_set_option(ctx, "ports", "33333");

mg_set_uri_callback(ctx, "/", &show_index, 0); mg_set_uri_callback(ctx, "/multiply", &multiply, 0); mg_set_uri_callback(ctx, "/result", &get_result, 0);

getchar(); mg_stop(ctx);

return 0;}

Page 64: Cukeup nyc ian dees on testing embedded systems with cucumber

int main(){ struct mg_context *ctx = mg_start(); mg_set_option(ctx, "ports", "33333");

mg_set_uri_callback(ctx, "/", &show_index, 0); mg_set_uri_callback(ctx, "/multiply", &multiply, 0); mg_set_uri_callback(ctx, "/result", &get_result, 0);

getchar(); mg_stop(ctx);

return 0;}

Page 65: Cukeup nyc ian dees on testing embedded systems with cucumber

static void multiply( struct mg_connection *conn, const struct mg_request_info *request_info, void *user_data){ char *ap = mg_get_var(conn, "multiplier"); char *bp = mg_get_var(conn, "multiplicand");

int a = atol(ap); int b = atol(bp);

result = a * b;

mg_printf(conn, "HTTP/1.1 200 OK\r\n\Content-Type: text/plain\r\n\r\n");

mg_free(multiplier_s); mg_free(multiplicand_s);}

Page 66: Cukeup nyc ian dees on testing embedded systems with cucumber

static void multiply( struct mg_connection *conn, const struct mg_request_info *request_info, void *user_data){ char *ap = mg_get_var(conn, "multiplier"); char *bp = mg_get_var(conn, "multiplicand");

int a = atol(ap); int b = atol(bp);

result = a * b;

mg_printf(conn, "HTTP/1.1 200 OK\r\n\Content-Type: text/plain\r\n\r\n");

mg_free(multiplier_s); mg_free(multiplicand_s);}

Page 67: Cukeup nyc ian dees on testing embedded systems with cucumber

static void multiply( struct mg_connection *conn, const struct mg_request_info *request_info, void *user_data){ char *ap = mg_get_var(conn, "multiplier"); char *bp = mg_get_var(conn, "multiplicand");

int a = atol(ap); int b = atol(bp);

result = a * b;

mg_printf(conn, "HTTP/1.1 200 OK\r\n\Content-Type: text/plain\r\n\r\n");

mg_free(multiplier_s); mg_free(multiplicand_s);}

Page 68: Cukeup nyc ian dees on testing embedded systems with cucumber

static void multiply( struct mg_connection *conn, const struct mg_request_info *request_info, void *user_data){ char *ap = mg_get_var(conn, "multiplier"); char *bp = mg_get_var(conn, "multiplicand");

int a = atol(ap); int b = atol(bp);

result = a * b;

mg_printf(conn, "HTTP/1.1 200 OK\r\n\Content-Type: text/plain\r\n\r\n");

mg_free(multiplier_s); mg_free(multiplicand_s);}

Page 69: Cukeup nyc ian dees on testing embedded systems with cucumber

static void get_result( struct mg_connection *conn, const struct mg_request_info *request_info, void *user_data){ mg_printf(conn, "HTTP/1.1 200 OK\r\n\Content-Type: text/plain\r\n\r\n%d", result);}

Page 70: Cukeup nyc ian dees on testing embedded systems with cucumber

https://github.com/jnunemaker/httparty

HTTParty

Page 71: Cukeup nyc ian dees on testing embedded systems with cucumber

When(/^I multiply (\d+) and (\d+)$/) do |a, b| Calculator.multiply a, bend

Then(/^I should get (\d+)$/) do |n| expect(Calculator.result).to eq(n.to_i)end

Page 72: Cukeup nyc ian dees on testing embedded systems with cucumber

require 'httparty'

class Calculator include HTTParty base_uri 'localhost:33333'

def self.multiply(a, b) get "/multiply?multiplier=#{a}\&multiplicand=#{b}" end

def self.result get("/result").to_i endend

Page 73: Cukeup nyc ian dees on testing embedded systems with cucumber

(because tl;dr is passé)

In summary:

Page 74: Cukeup nyc ian dees on testing embedded systems with cucumber

Have source? Device Technique

yes simple Direct

yes simple Serial

yes mediumCucumber Wire

Protocol

yes powerful Custom TCP

no powerful GUI

Page 75: Cukeup nyc ian dees on testing embedded systems with cucumber

Thank youhttps://github.com/undees/cukeup


Recommended