Upload
itamar-hassin
View
197
Download
5
Embed Size (px)
DESCRIPTION
This presentation makes the case for BDD in general and focuses on its use within embedded software development. Using the Cucumber gem, I will demonstrate how to use feature files in the context of working with embedded hardware projects, and explain how to extend the framework using the Wire protocol to allow integration tests to run in-situ, which greatly enhances testing coverage compared to PC-based testing using emulators. I also cover the notion of a SpecFlow gateway, with which one can achieve end-to-end testing with a variety of devices as an orchestration mechanism for broader tests.
Citation preview
EMBEDDED SOFTWARE DEVELOPMENT USING BDD
Itamar Hassin September 2014
THIS TALK COVERS
•Embedded development challenges !
•The case for BDD !
•Simulating the target !
•Accessing the target remotely/in-situ !
•Orchestrating access to multiple targets
EMBEDDED DEVELOPMENT CHALLENGES
•A simulator rarely available or not fully functional !
•Remote device verification !
•Complex state-machines !
•Test coverage often limited to unit testing
THE CASE FOR CUCUMBER
•Direct mapping from user story acceptance criteria
!
•Living documentation, unified view of the product !
•Helps defines ‘done’: Code is tested and validated !
•BDD promotes lean code & emergent design !
•Authored by the team: BAs/QA/Devs
CLOSER TO THE SOURCE
PREMISE FOR AUTOMATIONProgramatically validate that code solves the problem by articulating behaviour in machine-readable form.
CUCUMBER FOR EMBEDDED!
WRITE ONCE, USE THRICE
Feature: Alarm assured to appear in quiet mode !Scenario: Pressure alarm Given device is in quiet mode When pressure sensor is disconnected Then a silent alarm will appear
IMPLEMENT STEPS
Given(/Given device is in quiet mode $/) do @device.set_quiet_mode(1) end
IMPLEMENT A SIMULATOR
class Device def set_quiet_mode(flag) if (flag) mode |= QUIET_MODE else mode &= ~QUIET_MODE end update_driver(mode) end end
VALIDATE UNDER SIMULATOR
Scenario: Pressure alarm Given device is in quiet mode When pressure sensor is disconnected Then a silent alarm will appear !
5 scenarios (5 passed) 26 steps (26 passed) 0m0.052s
WHEN SIMULATION IS NOT ENOUGH
THE “WIRE”
•When your system does not have native support
•When you want a lean, portable implementation
SIMPLIFIED WIRE PROTOCOL
WIRE IMPLEMENTATION BLUEPRINT
•TCP/IP loop managing Cucumber protocol
•Function table for API invocation
•API implementation returning status to Cucumber
HOOKING CUCUMBER TO LISTENER
features/step_definitions/cucumber.wire host: deviceport: 3901
LISTENER TCP/IP LOOP
while(fgets(buff, sizeof(buff), rStream)) { respond_to_cucumber(wStream, buff); }
WIRE HANDLERvoid respond_to_cucumber(FILE* stream, char* msg) { if (MSG_TYPE_IS(msg, "step_matches")) respond_to_step_matches(stream, msg); else if (MSG_TYPE_IS(msg, "invoke")) respond_to_invoke(stream, msg); else if (MSG_TYPE_IS(msg, "begin_scenario")) respond_to_begin(stream, msg); else if (MSG_TYPE_IS(msg, "end_scenario")) respond_to_end(stream, msg); else respond_success(stream); }
FUNCTION TABLE
stepdef_t stepdefs[] = { { "device is in quiet mode”, set_quiet_mode } };
INVOKING API FUNCTIONS
void respond_to_invoke(…) { int id = get_function_id(msg); !
stepdefs[id].callback(arg_val) ? failure(stream) : success(stream); }
TARGET API
int set_quiet_mode(const char *arg) { context->requested_mode = atoi(arg); context->quiet_mode |= context->requested_mode; return(update_driver(context)); }
IMPLEMENTATION STACK
WORKING WITH CUCUMBER•Decide on a strategy (off-board, on-board)
•Get appropriate toolchain (cross compiler, linker)
•Implement and port Wire to target
•Run the feature files
•fail/implement/pass/refactor/repeat
USAGE PATTERNS Off-Board
• Framework on PC • Listener on PC • Proxy API on PC
• Network calls to Target API !
In-Situ • Framework on PC • Listener on Target • API calls on Target
Prog
ress
ion
WORKING AT A SAFE DISTANCE
OFF-BOARDCucumber running on
PC
Wire running on PC
Target API running on
PC
Target
NetworkC-implementation
• + Target untouched
• - All API’s must be exposed; low-fidelity; many moving parts;
UP CLOSE AND PERSONAL
IN-SITUFramework running on
PC
Cucumber-Wire running
on Target
Network C-implementation
Target API
• + high-fidelity, API’s not exposed
• - Server part of codebase
COLLABORATIVE ENVIRONMENT
GATEWAY
!
•Acts as an end-to-end test orchestrator !
•Switchboard events across heterogeneous devices
COLLABORATIVE END-TO-END TESTING
Framework running on
PC
C-implementation
Cucumber-Wire running
on Target
Targets
NativeWire
Collaboration
GATEWAY ARCHITECTURE
SpecFlow
Target B
Proxies
A1
B1
Hardware
Serial
Wire
Target ACucumber
Behave
END-TO-END FEATURES
Feature: Alarm assured to appear in quiet mode !
Scenario: Pressure alarm Given device is in quiet mode When pressure sensor is disconnected Then a silent alarm will appear
GATEWAY STEPS public class QuietModeSteps { SignalSimulator signalSimulator = new SignalSimulator(); MedicalDevice medicalDevice = new MedicalDevice(“192.168.1.1”, 3901); ! [Given(@"device is quiet mode")] public void GivenDeviceIsQuietMode() { NUnit.Framework.Assert.IsTrue(medicalDevice.SetQuietMode()); } ! [When(@“pressure sensor is disconnected")] public void GivenPressureSensorIsDisconnected() { NUnit.Framework.Assert.IsTrue(signalSimulator.SetPressure(off)); } }
GATEWAY PROXIES class MedicalDevice { protected Wire wire; ! public MedicalDevice(string ipAddress, int port) { myAddress = ipAddress; wire = new Wire(myAddress, port); wire.Open(); } ! public bool SetQuietMode() { wire.Send("[\"step_matches\",{\"name_to_match\":\"set quiet mode on\"}]\n"); wire.Send("[\"invoke\",{\"id\":\"7\",\"args\":[\"on\"]}]\n"); return(wire.Ack()); } }
EMULATING WIRE public class Wire { public int Open() { client = new TcpClient(myAddress, myPort); stream = client.GetStream(); return(Send(“[\”begin_scenario\"]\n")); } ! public int Close() { stream = client.GetStream(); Send("[\"end_scenario\"]\n"); return(client.Close()); } }
SpecFlow
Wire
Proxies
A1
Target
TCP
Given … quiet mode
Wire
int SetQuietMode(“on”) {}
Match: “set quiet\’(on|off)’\”
Invoke: idx:0, params: “on”
SPECFLOW TO WIRE
int set_quiet(char* state){}A
Exists in proxy? Write wrappers
Exists in Wire?
No
NoYes
Function table entry
Yes
Use in feature/step files
API Exists? No Implement API
Yes
MAINTENANCE CONSIDERATIONS
•Security - Anyone can connect to Wire!
•Regulation may not allow non-application code on a production system
Shut down the wire thread in production
COMPLIANCE CONSIDERATIONS
LESSONS LEARNED
Threading
Dual VocabularyThreads & Target Architecture
REFERENCES•Specification by example
•The Cucumber Book
•Cucumber Recipes
@history_pics/@historyinpicsJim Reese#Wikipedia
National Library of Australia
Photo Credits:
•SpecFlow