Initial commit

main
oniboni 2022-08-27 17:11:45 +02:00
parent efdf7b4920
commit fd1938a16c
Signed by: oniboni
GPG Key ID: 11E6EDEFD248BB50
15 changed files with 1341 additions and 1 deletions

5
.gitignore vendored
View File

@ -70,3 +70,8 @@ dkms.conf
# Local History for Visual Studio Code
.history/
# Meson
subprojects/*
!subprojects/*.wrap
!subprojects/packagefiles
!subprojects/packagefiles/*

View File

@ -1,3 +1,11 @@
# tdd-in-c
code repo for talk..
code repo for talk..
slides can be opended in [Obsidian](https://obsidian.md/download):
* (optional) install via flatpak (+ flatseal)
* install excalidraw community plugin if you want to see the first image
* enable slides plugin if you want to see the presentation
* (optional) disable network access in flatseal for obsidian
* (optional) only allow this folder in flatseal for obsidian
* open this folder in obsidian

3
inc/mqtt_example.h Normal file
View File

@ -0,0 +1,3 @@
#include <stddef.h>
int send_mqtt_msg(char *payload, size_t payload_len);

View File

@ -0,0 +1 @@
int add(int a, int b);

46
meson.build Normal file
View File

@ -0,0 +1,46 @@
project('tdd-in-c', 'c')
cgreen_dep = dependency('cgreen')
cgreen_runner = find_program('cgreen-runner')
inc_dir = include_directories('inc')
my_fancy_calculator_lib = library('my_fancy_calculator',
'src/my_fancy_calculator.c',
include_directories: inc_dir)
my_fancy_calculator_dep = declare_dependency(
link_with: my_fancy_calculator_lib,
include_directories: inc_dir)
executable('demo',
'src/main.c',
dependencies: [my_fancy_calculator_dep])
test_lib = library('demo_test',
'test/my_fancy_test.c',
dependencies: [cgreen_dep, my_fancy_calculator_dep])
test('my_fancy_test', cgreen_runner, args: test_lib, verbose: true)
cmake = import('cmake')
paho = cmake.subproject('paho')
# message('CMaket targets:\n - ' + '\n - '.join(paho.target_list()))
paho_dep = paho.dependency('paho-mqtt3c')
mqtt_example_lib = library('mqtt_example',
'src/mqtt_example.c',
include_directories: inc_dir,
dependencies: paho_dep)
mqtt_example_dep = declare_dependency(
link_with: mqtt_example_lib,
include_directories: inc_dir)
mqtt_test_lib = library('mqtt_example_test',
'test/mqtt_example_test.c',
dependencies: [cgreen_dep, mqtt_example_dep, paho_dep])
test('mqtt_example_test', cgreen_runner, args: mqtt_test_lib, verbose: true)

View File

@ -0,0 +1,718 @@
---
excalidraw-plugin: parsed
tags: [excalidraw]
---
==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠==
# Text Elements
write new
test ^31RJbRzp
fix
test ^x2un00k7
refactor ^V9qI8eSy
test fails
🤯 ^xb3ww1lS
make it pretty (again)
😍 ^0L1dSJHQ
all tests
succeed
😏 ^JNw8thXz
oopsie
🤐 ^HXlpOaXQ
%%
# Drawing
```json
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
{
"type": "ellipse",
"version": 110,
"versionNonce": 758728130,
"isDeleted": false,
"id": "nm8llW-iN1JxRANjFSrGv",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -75.95181274414062,
"y": -156.13854598999023,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 117.204833984375,
"height": 94.84332275390625,
"seed": 212816862,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [
{
"id": "qHUXBL3kqOHKLZngkm4ok",
"type": "arrow"
},
{
"id": "UV6QbBSQAtpZZg4Zm-w6c",
"type": "arrow"
},
{
"id": "a0mIB9EfpNafcfLmdcqH9",
"type": "arrow"
}
],
"updated": 1661195729947,
"link": null,
"locked": false
},
{
"type": "text",
"version": 56,
"versionNonce": 1379430238,
"isDeleted": false,
"id": "31RJbRzp",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -62.295318603515625,
"y": -134.29519271850586,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 89,
"height": 51,
"seed": 1026440770,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1661195729947,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "write new\ntest",
"rawText": "write new\ntest",
"baseline": 43,
"textAlign": "center",
"verticalAlign": "top",
"containerId": null,
"originalText": "write new\ntest"
},
{
"type": "ellipse",
"version": 143,
"versionNonce": 1599028610,
"isDeleted": false,
"id": "ffIeVGDcam9mozesZO-yG",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 71.79519653320312,
"y": 42.283206939697266,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 117.204833984375,
"height": 94.84332275390625,
"seed": 1111461314,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [
{
"id": "qHUXBL3kqOHKLZngkm4ok",
"type": "arrow"
},
{
"id": "5Jvur5K1uFj1Lf8UvIXrM",
"type": "arrow"
},
{
"id": "2kGIqpVENtTePrqLMxrKl",
"type": "arrow"
}
],
"updated": 1661195729947,
"link": null,
"locked": false
},
{
"type": "text",
"version": 105,
"versionNonce": 1852756894,
"isDeleted": false,
"id": "x2un00k7",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 107.45169067382812,
"y": 64.12656021118164,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 45,
"height": 51,
"seed": 765110110,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1661195729947,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "fix\ntest",
"rawText": "fix\ntest",
"baseline": 43,
"textAlign": "center",
"verticalAlign": "top",
"containerId": null,
"originalText": "fix\ntest"
},
{
"type": "ellipse",
"version": 187,
"versionNonce": 324025666,
"isDeleted": false,
"id": "pbqynQdNj5Xhkqe8REuu-",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -210.96389770507812,
"y": 40.234928131103516,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 117.204833984375,
"height": 94.84332275390625,
"seed": 1898572802,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [
{
"id": "5Jvur5K1uFj1Lf8UvIXrM",
"type": "arrow"
},
{
"id": "UV6QbBSQAtpZZg4Zm-w6c",
"type": "arrow"
},
{
"id": "2kGIqpVENtTePrqLMxrKl",
"type": "arrow"
}
],
"updated": 1661195729947,
"link": null,
"locked": false
},
{
"type": "text",
"version": 187,
"versionNonce": 919515102,
"isDeleted": false,
"id": "V9qI8eSy",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -190.68099975585938,
"y": 74.41568374633789,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 85,
"height": 25,
"seed": 2143046942,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1661195729947,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "refactor",
"rawText": "refactor",
"baseline": 18,
"textAlign": "center",
"verticalAlign": "top",
"containerId": null,
"originalText": "refactor"
},
{
"type": "arrow",
"version": 116,
"versionNonce": 1721451778,
"isDeleted": false,
"id": "qHUXBL3kqOHKLZngkm4ok",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 43.566253662109375,
"y": -91.36748886108398,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 93.30120849609375,
"height": 128.77108764648438,
"seed": 258980958,
"groupIds": [],
"strokeSharpness": "round",
"boundElements": [],
"updated": 1661195729947,
"link": null,
"locked": false,
"startBinding": {
"elementId": "nm8llW-iN1JxRANjFSrGv",
"focus": -0.21315250130441088,
"gap": 5.816058173350605
},
"endBinding": {
"elementId": "ffIeVGDcam9mozesZO-yG",
"focus": 0.1882171945657398,
"gap": 5.150127976620205
},
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
70.1685791015625,
45.494049072265625
],
[
93.30120849609375,
128.77108764648438
]
]
},
{
"type": "text",
"version": 70,
"versionNonce": 1466500126,
"isDeleted": false,
"id": "xb3ww1lS",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 114.08432006835938,
"y": -81.86144638061523,
"strokeColor": "#c92a2a",
"backgroundColor": "transparent",
"width": 98,
"height": 51,
"seed": 1111312478,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1661195729947,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "test fails\n🤯",
"rawText": "test fails\n🤯",
"baseline": 43,
"textAlign": "center",
"verticalAlign": "top",
"containerId": null,
"originalText": "test fails\n🤯"
},
{
"type": "arrow",
"version": 82,
"versionNonce": 1924093122,
"isDeleted": false,
"id": "5Jvur5K1uFj1Lf8UvIXrM",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 73.63851928710938,
"y": 109.11441421508789,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 170.40960693359375,
"height": 38.55426025390625,
"seed": 1043236126,
"groupIds": [],
"strokeSharpness": "round",
"boundElements": [],
"updated": 1661195729947,
"link": null,
"locked": false,
"startBinding": {
"elementId": "ffIeVGDcam9mozesZO-yG",
"focus": 0.19507025668637445,
"gap": 2.905120406102732
},
"endBinding": {
"elementId": "pbqynQdNj5Xhkqe8REuu-",
"focus": 0.2320589051112453,
"gap": 7.1341351282762915
},
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
-71.7108154296875,
38.55426025390625
],
[
-170.40960693359375,
7.71087646484375
]
]
},
{
"type": "arrow",
"version": 136,
"versionNonce": 1378799710,
"isDeleted": false,
"id": "UV6QbBSQAtpZZg4Zm-w6c",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -173.87954711914062,
"y": 32.00601577758789,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 93.3011474609375,
"height": 134.16867065429688,
"seed": 1336603010,
"groupIds": [],
"strokeSharpness": "round",
"boundElements": [],
"updated": 1661195729947,
"link": null,
"locked": false,
"startBinding": {
"elementId": "pbqynQdNj5Xhkqe8REuu-",
"focus": -0.3141133788555912,
"gap": 11.072311496473674
},
"endBinding": {
"elementId": "nm8llW-iN1JxRANjFSrGv",
"focus": 0.46674145632334696,
"gap": 5.122361175655115
},
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
6.16876220703125,
-74.0240478515625
],
[
93.3011474609375,
-134.16867065429688
]
]
},
{
"type": "ellipse",
"version": 52,
"versionNonce": 1227582594,
"isDeleted": false,
"id": "dFDi8T5y9nKejgjaW9prK",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -32.771087646484375,
"y": -248.66868209838867,
"strokeColor": "#000000",
"backgroundColor": "#7950f2",
"width": 23.90362548828125,
"height": 25.445770263671875,
"seed": 1789075230,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [
{
"id": "a0mIB9EfpNafcfLmdcqH9",
"type": "arrow"
}
],
"updated": 1661195729947,
"link": null,
"locked": false
},
{
"type": "arrow",
"version": 146,
"versionNonce": 1123095710,
"isDeleted": false,
"id": "a0mIB9EfpNafcfLmdcqH9",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -20.433746337890625,
"y": -218.59638595581055,
"strokeColor": "#000000",
"backgroundColor": "#7950f2",
"width": 0.7711181640625,
"height": 56.28913879394531,
"seed": 298864990,
"groupIds": [],
"strokeSharpness": "round",
"boundElements": [],
"updated": 1661195729947,
"link": null,
"locked": false,
"startBinding": {
"elementId": "dFDi8T5y9nKejgjaW9prK",
"focus": -0.052137360879168294,
"gap": 4.631212661626893
},
"endBinding": {
"elementId": "nm8llW-iN1JxRANjFSrGv",
"focus": -0.07831304571460417,
"gap": 6.263245110012669
},
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
-0.7711181640625,
56.28913879394531
]
]
},
{
"type": "arrow",
"version": 136,
"versionNonce": 1875807298,
"isDeleted": false,
"id": "2kGIqpVENtTePrqLMxrKl",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "dashed",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -119.13253784179688,
"y": 38.174686431884766,
"strokeColor": "#495057",
"backgroundColor": "#7950f2",
"width": 210.50592041015625,
"height": 34.698760986328125,
"seed": 1122211330,
"groupIds": [],
"strokeSharpness": "round",
"boundElements": [],
"updated": 1661195729947,
"link": null,
"locked": false,
"startBinding": {
"elementId": "pbqynQdNj5Xhkqe8REuu-",
"focus": -0.7851553882292053,
"gap": 9.285189272758508
},
"endBinding": {
"elementId": "ffIeVGDcam9mozesZO-yG",
"focus": 0.5729609672764691,
"gap": 5.722658768651257
},
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
107.18072509765625,
-25.44573974609375
],
[
210.50592041015625,
9.253021240234375
]
]
},
{
"type": "text",
"version": 67,
"versionNonce": 593547486,
"isDeleted": false,
"id": "0L1dSJHQ",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "dashed",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -114.0300064086914,
"y": 155.6324806213379,
"strokeColor": "#862e9c",
"backgroundColor": "#7950f2",
"width": 219,
"height": 51,
"seed": 63733058,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1661195729947,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "make it pretty (again)\n😍",
"rawText": "make it pretty (again)\n😍",
"baseline": 43,
"textAlign": "center",
"verticalAlign": "top",
"containerId": null,
"originalText": "make it pretty (again)\n😍"
},
{
"type": "text",
"version": 268,
"versionNonce": 156456030,
"isDeleted": false,
"id": "JNw8thXz",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -245.39144897460938,
"y": -100.33730697631836,
"strokeColor": "#2b8a3e",
"backgroundColor": "transparent",
"width": 89,
"height": 76,
"seed": 156038466,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1661195742018,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "all tests\nsucceed\n😏",
"rawText": "all tests\nsucceed\n😏",
"baseline": 69,
"textAlign": "center",
"verticalAlign": "top",
"containerId": null,
"originalText": "all tests\nsucceed\n😏"
},
{
"type": "text",
"version": 132,
"versionNonce": 252642590,
"isDeleted": false,
"id": "HXlpOaXQ",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -44.289093017578125,
"y": 18.831424713134766,
"strokeColor": "#c92a2a",
"backgroundColor": "transparent",
"width": 58,
"height": 51,
"seed": 431525890,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElements": [],
"updated": 1661195729948,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 1,
"text": "oopsie\n🤐",
"rawText": "oopsie\n🤐",
"baseline": 43,
"textAlign": "center",
"verticalAlign": "top",
"containerId": null,
"originalText": "oopsie\n🤐"
}
],
"appState": {
"theme": "dark",
"viewBackgroundColor": "#2220",
"currentItemStrokeColor": "#862e9c",
"currentItemBackgroundColor": "#7950f2",
"currentItemFillStyle": "hachure",
"currentItemStrokeWidth": 1,
"currentItemStrokeStyle": "dashed",
"currentItemRoughness": 1,
"currentItemOpacity": 100,
"currentItemFontFamily": 1,
"currentItemFontSize": 20,
"currentItemTextAlign": "center",
"currentItemStrokeSharpness": "sharp",
"currentItemStartArrowhead": null,
"currentItemEndArrowhead": "arrow",
"currentItemLinearStrokeSharpness": "round",
"gridSize": null,
"colorPalette": {}
},
"files": {}
}
```
%%

269
slides/TDD in C.md Normal file
View File

@ -0,0 +1,269 @@
# Test Driven Development in C
---
### TDD recap
![[TDD cycle.excalidraw]]
* good for unit tests, if requirements are known (specification exists)
---
### cgreen testing framework
* readable
* mocking support
* different test reporters
* align nicely in CI (`cobertura`/`xJunit`)
* user friendly (`pretty`)
* test discovery (`cgreen-runner`)
---
### Example: "Calculator"
`inc/my_fancy_calculator.h`:
```c
int add(int a, int b);
```
`src/my_fancy_calculator.c`:
```c
#include "my_fancy_calculator.h"
int add(int a, int b) {
return a - b;
}
```
---
### Unit tests with cgreen
`test/my_fancy_test.c`:
```c
#include <cgreen/cgreen.h>
#include <my_fancy_calculator.h>
Ensure(my_fancy_calculator_can_do_addition) {
assert_that(add(2, 3), is_equal_to(5));
}
```
---
* automatic test discovery
`cgreen_runner libdemo_test.so`:
```
Running "libdemo_test" (1 test)...
../test/my_fancy_test.c:5: Failure: default -> my_fancy_calculator_can_do_addition
Expected [add(2, 3)] to [equal] [5]
actual value: [-1]
expected value: [5]
"default": 1 failure in 30ms.
Completed "libdemo_test": 1 failure in 30ms.
```
---
### BDD
Behavioral Driven Development
* isolated tests
* via mocking
* SDKs
* Network
* etc
* via test fixtures (`BeforeEach`/`AfterEach`)
* context setup and tear down
* good for reproducing bugs
---
### Example: MQTT client
```c
#include "mqtt_example.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <MQTTClient.h>
#define ADDRESS "tcp://localhost:1883"
#define CLIENTID "ExampleClientPub"
#define TOPIC "MQTT Examples"
#define QOS 1
#define TIMEOUT 10000L
int send_mqtt_msg(char *payload, size_t payload_len) {
MQTTClient client;
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
MQTTClient_message pubmsg = MQTTClient_message_initializer;
MQTTClient_deliveryToken token;
int rc;
MQTTClient_create(&client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL);
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) {
printf("Failed to connect, return code %d\n", rc);
exit(-1);
}
pubmsg.payload = payload;
pubmsg.payloadlen = payload_len;
pubmsg.qos = QOS;
pubmsg.retained = 0;
MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token);
printf("Waiting for up to %d seconds for publication of %s\n"
"on topic %s for client with ClientID: %s\n",
(int)(TIMEOUT/1000), payload, TOPIC, CLIENTID);
rc = MQTTClient_waitForCompletion(client, token, TIMEOUT);
printf("Message with delivery token %d delivered\n", token);
MQTTClient_disconnect(client, 10000);
MQTTClient_destroy(&client);
return rc;
}
```
[Eclipse Paho](https://www.eclipse.org/paho/index.php?page=clients/c/index.php)
---
### Mocks in cgreen
```c
#include <MQTTClient.h>
#include <cgreen/mocks.h>
LIBMQTT_API int MQTTClient_create(MQTTClient* handle, const char* serverURI, const char* clientId, int persistence_type, void* persistence_context) {
return (int)mock(handle);
}
LIBMQTT_API int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options) {
return (int)mock(handle);
}
LIBMQTT_API int MQTTClient_publishMessage(MQTTClient handle, const char* topicName, MQTTClient_message* msg, MQTTClient_deliveryToken* dt) {
return (int)mock(msg->payload, msg->payloadlen);
}
LIBMQTT_API int MQTTClient_waitForCompletion(MQTTClient handle, MQTTClient_deliveryToken dt, unsigned long timeout) {
return (int)mock();
}
LIBMQTT_API int MQTTClient_disconnect(MQTTClient handle, int timeout) {
return (int)mock();
}
LIBMQTT_API void MQTTClient_destroy(MQTTClient* handle) {
mock(handle);
}
```
---
### BDD-style unit test with cgreen
```c
Describe(mqtt_example);
BeforeEach(mqtt_example) {}
AfterEach(mqtt_example) {}
Ensure(mqtt_example, payload_is_sent) {
// Arrange
MQTTClient mock_handle = NOT_NULL;
char *payload = "Hello World.";
size_t payload_len = strlen(payload);
// Expect
expect(MQTTClient_create,
will_set_contents_of_parameter(handle,
&mock_handle,
sizeof(mock_handle)));
expect(MQTTClient_connect,
when(handle,
is_equal_to(mock_handle)),
will_return(MQTTCLIENT_SUCCESS));
expect(MQTTClient_publishMessage,
when(msg->payload,
is_equal_to_string(payload)),
when(msg->payloadlen,
is_equal_to(payload_len)));
expect(MQTTClient_waitForCompletion,
will_return(MQTTCLIENT_SUCCESS));
expect(MQTTClient_disconnect);
expect(MQTTClient_destroy);
// Act
send_mqtt_msg(payload, payload_len);
}
```
---
### cgreen learning mock mode
```c
#include <cgreen/cgreen.h>
#include <mqtt_example.h>
// [mock definitions]
Describe(mqtt_example);
BeforeEach(mqtt_example) {
cgreen_mocks_are(learning_mocks);
}
AfterEach(mqtt_example) {}
Ensure(mqtt_example, payload_is_sent) {
// Arrange
char *payload = "Hello World.";
size_t payload_len = strlen(payload);
// Expect
// TODO
// Act
send_mqtt_msg(payload, payload_len);
}
```
---
### learned mocks
```c
stdout:
Running "libmqtt_example_test" (1 test)...
Waiting for up to 10 seconds for publication of Hello World.
on topic MQTT Examples for client with ClientID: ExampleClientPub
Message with delivery token 0 delivered
"mqtt_example": No assertions.
Completed "libmqtt_example_test": No assertions.
stderr:
mqtt_example -> payload_is_sent : Learned mocks are
expect(MQTTClient_create, when(handle, is_equal_to(140735561153976)));
expect(MQTTClient_connect, when(handle, is_equal_to(0)));
expect(MQTTClient_publishMessage, when(msg->payload, is_equal_to(139884358905951)), when(msg->payloadlen, is_equal_to(12)));
expect(MQTTClient_waitForCompletion);
expect(MQTTClient_disconnect);
expect(MQTTClient_destroy, when(handle, is_equal_to(140735561153976)));
```
---
### Whats next?
Try it out!
![cgreen logo](https://github.com/cgreen-devs/cgreen/raw/master/doc/logo.png?s=300)
* [cgreen tutorial](https://cgreen-devs.github.io/cgreen/cgreen-guide-en.html)
* [cgreen repo](https://github.com/cgreen-devs/cgreen)
* [cgreen cheat sheet](https://cgreen-devs.github.io/cgreen/cheat-sheet.html)
---
### Some disclaimers
* cgreen's buildsystem is `CMAKE`
* `meson` usage with `cgreen-runner` does not work out of the box
* see [repo of this talk](https://git.dezentrale.cloud/oniboni/tdd-in-c.git) for example setup
* but.. cgreen is availabe on most common linux distros (`-lcgreen`, `find_program`, etc should work just fine)
---
### [backup] TDD test structure
* arrange
* act -- test function
* assert
---
### [backup] BDD test structure
* arrange (via test fixtures)
* expect -- setup behavior of mocks
* act -- entry function to trigger behavior
* (assert) -- check post conditions

10
src/main.c Normal file
View File

@ -0,0 +1,10 @@
#include <stdio.h>
#include "my_fancy_calculator.h"
int main(int argc, char **argv) {
int a = 2;
int b = 3;
printf("a[%d] + b[%d] = [%d]\n", a, b, add(a,b));
return 0;
}

44
src/mqtt_example.c Normal file
View File

@ -0,0 +1,44 @@
#include "mqtt_example.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <MQTTClient.h>
#define ADDRESS "tcp://localhost:1883"
#define CLIENTID "ExampleClientPub"
#define TOPIC "MQTT Examples"
#define QOS 1
#define TIMEOUT 10000L
int send_mqtt_msg(char *payload, size_t payload_len) {
MQTTClient client;
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
MQTTClient_message pubmsg = MQTTClient_message_initializer;
MQTTClient_deliveryToken token;
int rc;
MQTTClient_create(&client, ADDRESS, CLIENTID,
MQTTCLIENT_PERSISTENCE_NONE, NULL);
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
{
printf("Failed to connect, return code %d\n", rc);
exit(-1);
}
pubmsg.payload = payload;
pubmsg.payloadlen = payload_len;
pubmsg.qos = QOS;
pubmsg.retained = 0;
MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token);
printf("Waiting for up to %d seconds for publication of %s\n"
"on topic %s for client with ClientID: %s\n",
(int)(TIMEOUT/1000), payload, TOPIC, CLIENTID);
rc = MQTTClient_waitForCompletion(client, token, TIMEOUT);
printf("Message with delivery token %d delivered\n", token);
MQTTClient_disconnect(client, 10000);
MQTTClient_destroy(&client);
return rc;
}

View File

@ -0,0 +1,5 @@
#include "my_fancy_calculator.h"
int add(int a, int b) {
return a - b;
}

9
subprojects/cgreen.wrap Normal file
View File

@ -0,0 +1,9 @@
[wrap-git]
url = https://github.com/cgreen-devs/cgreen.git
revision = master
depth = 1
patch_directory = cgreen
[provides]
dependency_names = cgreen
program_names = cgreen-runner

View File

@ -0,0 +1,162 @@
project('cgreen', 'c', 'cpp')
configure_file(
output : 'config.h',
configuration: {
'PACKAGE': '"cgreen"',
'VERSION': '"2.8.12"'
})
cgreen_shared_dir = include_directories('src', '.', 'include')
cgreen_shared_sys = include_directories(is_system : true)
cgreen_shared_inc = [cgreen_shared_dir, cgreen_shared_sys]
cgreen_shared_src = files(
'src/assertions.c',
'src/boxed_double.c',
'src/breadcrumb.c',
'src/cgreen_time.c',
'src/cgreen_value.c',
'src/constraint.c',
'src/cute_reporter.c',
'src/cdash_reporter.c',
'src/messaging.c',
'src/message_formatting.c',
'src/mocks.c',
'src/parameters.c',
'src/reporter.c',
'src/runner.c',
'src/string_comparison.c',
'src/suite.c',
'src/text_reporter.c',
'src/utils.c',
'src/vector.c',
'src/xml_reporter.c',
'src/posix_cgreen_pipe.c',
'src/posix_cgreen_time.c',
'src/posix_runner_platform.c',
'src/cpp_assertions.cpp',
'src/cpp_constraint.cpp',
'gitrevision.h'
)
cgreen_shared = shared_library(
'cgreen_shared',
cgreen_shared_src,
build_by_default : true,
link_args : [
'-lstdc++',
'-lm',
'-lgcc_s',
'-lgcc',
'-lc',
'-lgcc_s',
'-lgcc',
'-lc'
],
link_with : [],
include_directories : cgreen_shared_inc,
install : true,
override_options : ['c_std=c99', 'cpp_std=c++98'],
objects : [],
install_dir : 'lib64',
c_args : [
'-Wstrict-prototypes',
'-Wunused',
'-O',
'-DFILENAME="src/assertions.c"',
'-DVERSION="1.6.0"',
'-D_FORTIFY_SOURCE=2',
'-D_REENTRANT',
'-D_XOPEN_SOURCE',
'-D_XOPEN_SOURCE_EXTENDED',
'-D__STDC_FORMAT_MACROS',
'-Dcgreen_shared_EXPORTS',
],
cpp_args : [
'-Weffc++',
'-Wunused',
'-O',
'-DFILENAME="src/cpp_assertions.cpp"',
'-DVERSION="1.6.0"',
'-D_FORTIFY_SOURCE=2',
'-D_REENTRANT',
'-D_XOPEN_SOURCE',
'-D_XOPEN_SOURCE_EXTENDED',
'-D__STDC_FORMAT_MACROS',
'-Dcgreen_shared_EXPORTS',
'-DFILENAME="src/cpp_constraint.cpp"'
]
)
cgreen_shared_dep = declare_dependency(
link_args : [
'-lstdc++',
'-lm',
'-lgcc_s',
'-lgcc',
'-lc',
'-lgcc_s',
'-lgcc',
'-lc'
],
link_with : cgreen_shared,
compile_args : [],
include_directories : cgreen_shared_inc
)
meson.override_dependency('cgreen', cgreen_shared_dep)
cgreen_runner_dir = include_directories(
'tools',
'.',
'include',
'src',
)
cgreen_runner_sys = include_directories(is_system : true)
cgreen_runner_inc = [cgreen_runner_dir, cgreen_runner_sys]
cgreen_runner_src = files(
'tools/cgreen-runner.c',
'tools/gopt.c',
'tools/gopt-errors.c',
'tools/runner.c',
'tools/discoverer.c',
'tools/test_item.c',
'tools/io.c'
)
cgreen_runner = executable(
'cgreen_runner',
cgreen_runner_src,
build_by_default : true,
link_args : [
'-std=c99',
'-Wstrict-prototypes',
'-rdynamic',
'-ldl',
'-lstdc++',
'-lm',
'-lgcc_s',
'-lgcc',
'-lc',
'-lgcc_s',
'-lgcc',
'-lc'
],
link_with : [cgreen_shared],
include_directories : cgreen_runner_inc,
install : true,
override_options : ['c_std=c99'],
objects : [],
install_dir : 'bin',
c_args : [
'-Wstrict-prototypes',
'-Wunused',
'-O',
'-DFILENAME="tools/cgreen-runner.c"',
'-DVERSION="1.6.0"',
'-D_FORTIFY_SOURCE=2',
'-D_REENTRANT',
'-D_XOPEN_SOURCE',
'-D_XOPEN_SOURCE_EXTENDED',
'-D__STDC_FORMAT_MACROS',
'-DNM_EXECUTABLE="/usr/bin/nm"',
],
pie : true
)
meson.override_find_program('cgreen-runner', cgreen_runner)

4
subprojects/paho.wrap Normal file
View File

@ -0,0 +1,4 @@
[wrap-git]
url = https://github.com/eclipse/paho.mqtt.c.git
revision = master
depth = 1

50
test/mqtt_example_test.c Normal file
View File

@ -0,0 +1,50 @@
#include <cgreen/cgreen.h>
#include <cgreen/mocks.h>
#include <mqtt_example.h>
#include <MQTTClient.h>
#define NOT_NULL (void*)1
LIBMQTT_API int MQTTClient_create(MQTTClient* handle, const char* serverURI, const char* clientId,
int persistence_type, void* persistence_context) {
return (int)mock(handle);
}
LIBMQTT_API int MQTTClient_connect(MQTTClient handle, MQTTClient_connectOptions* options) {
return (int)mock(handle);
}
LIBMQTT_API int MQTTClient_publishMessage(MQTTClient handle, const char* topicName, MQTTClient_message* msg, MQTTClient_deliveryToken* dt) {
return (int)mock(msg->payload, msg->payloadlen);
}
LIBMQTT_API int MQTTClient_waitForCompletion(MQTTClient handle, MQTTClient_deliveryToken dt, unsigned long timeout) {
return (int)mock();
}
LIBMQTT_API int MQTTClient_disconnect(MQTTClient handle, int timeout) {
return (int)mock();
}
LIBMQTT_API void MQTTClient_destroy(MQTTClient* handle) {
mock(handle);
}
Describe(mqtt_example);
BeforeEach(mqtt_example) {
// cgreen_mocks_are(learning_mocks);
}
AfterEach(mqtt_example) {}
Ensure(mqtt_example, payload_is_sent) {
// Arrange
MQTTClient mock_handle = NOT_NULL;
char *payload = "Hello World.";
size_t payload_len = strlen(payload);
// Expect
expect(MQTTClient_create, will_set_contents_of_parameter(handle, &mock_handle, sizeof(mock_handle)));
expect(MQTTClient_connect, when(handle, is_equal_to(mock_handle)), will_return(MQTTCLIENT_SUCCESS));
expect(MQTTClient_publishMessage, when(msg->payload, is_equal_to_string(payload)), when(msg->payloadlen, is_equal_to(payload_len)));
expect(MQTTClient_waitForCompletion, will_return(MQTTCLIENT_SUCCESS));
expect(MQTTClient_disconnect);
expect(MQTTClient_destroy);
// Act
send_mqtt_msg(payload, payload_len);
}

6
test/my_fancy_test.c Normal file
View File

@ -0,0 +1,6 @@
#include <cgreen/cgreen.h>
#include <my_fancy_calculator.h>
Ensure(my_fancy_calculator_can_do_addition) {
assert_that(add(2, 3), is_equal_to(5));
}