Add sandbox_c cmake project + ccache workflow for pool testing
This commit is contained in:
1 parent
fb253f9b27
commit
ba41deb9ec
9 files changed
+160
No files matched your search
@@ -0,0 +1,61 @@
|
|||||||
|
name: Sandbox - cmake + ccache on the pool
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ci/sandbox]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
# Inside the workspace so actions/cache can store it. The path is identical on
|
||||||
|
# every run, which matters because ccache hashes the compiler command line.
|
||||||
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: cmake
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
working-directory: sandbox_c
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Toolchain
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
command -v cmake >/dev/null && command -v ccache >/dev/null && exit 0
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y -qq --no-install-recommends \
|
||||||
|
cmake ninja-build gcc libc6-dev ccache ca-certificates >/dev/null
|
||||||
|
cmake --version | head -1
|
||||||
|
ccache --version | head -1
|
||||||
|
|
||||||
|
# Cache the compiler cache, never build/: CMake bakes absolute paths into
|
||||||
|
# CMakeCache.txt, so a restored build tree breaks if the workspace moves.
|
||||||
|
- name: Cache ccache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ env.CCACHE_DIR }}
|
||||||
|
key: ccache-${{ hashFiles('sandbox_c/**') }}
|
||||||
|
restore-keys: ccache-
|
||||||
|
|
||||||
|
- name: ccache before build
|
||||||
|
run: ccache --show-stats | grep -E "cache (hits|misses|read)" || ccache --show-stats
|
||||||
|
|
||||||
|
- name: Configure
|
||||||
|
run: |
|
||||||
|
cmake -S . -B build -G Ninja \
|
||||||
|
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build build --parallel
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: ctest --test-dir build --output-on-failure
|
||||||
|
|
||||||
|
- name: ccache after build
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
ccache --show-stats
|
||||||
|
set -o pipefail
|
||||||
|
ctest --test-dir build 2>&1 | grep -E "tests passed"
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/build
|
||||||
|
/.ccache
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles/
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.22)
|
||||||
|
project(sandbox_c C)
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
|
||||||
|
add_library(tempsense STATIC src/temperature.c src/convert.c src/parse.c src/format.c)
|
||||||
|
target_include_directories(tempsense PUBLIC include)
|
||||||
|
|
||||||
|
enable_testing()
|
||||||
|
add_executable(test_tempsense tests/test_temperature.c)
|
||||||
|
target_link_libraries(test_tempsense PRIVATE tempsense)
|
||||||
|
add_test(NAME tempsense COMMAND test_tempsense)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#ifndef TEMPSENSE_H
|
||||||
|
#define TEMPSENSE_H
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
typedef struct { char sensor[32]; double celsius; } Reading;
|
||||||
|
|
||||||
|
double to_fahrenheit(double celsius);
|
||||||
|
int parse_reading(const char *json, Reading *out); /* 0 on success */
|
||||||
|
int format_reading(const Reading *in, char *buf, size_t len);
|
||||||
|
double clamp_to_absolute_zero(double celsius);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#include "temperature.h"
|
||||||
|
double to_fahrenheit(double celsius) { return celsius * 9.0 / 5.0 + 32.0; }
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#include "temperature.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
int format_reading(const Reading *in, char *buf, size_t len) {
|
||||||
|
if (!in || !buf || len < 16) return 1;
|
||||||
|
int n = snprintf(buf, len, "{\"sensor\":\"%s\",\"celsius\":%.2f}", in->sensor, in->celsius);
|
||||||
|
return (n < 0 || (size_t)n >= len) ? 2 : 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#include "temperature.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* Deliberately tiny parser: finds "celsius":<num> and an optional
|
||||||
|
"sensor":"..." so the test can assert on both fields. */
|
||||||
|
int parse_reading(const char *json, Reading *out) {
|
||||||
|
if (!json || !out) return 1;
|
||||||
|
const char *c = strstr(json, "\"celsius\"");
|
||||||
|
const char *s = strstr(json, "\"sensor\"");
|
||||||
|
if (!c) return 2;
|
||||||
|
c = strchr(c, ':');
|
||||||
|
if (!c) return 3;
|
||||||
|
char *end = NULL;
|
||||||
|
out->celsius = strtod(c + 1, &end);
|
||||||
|
if (end == c + 1) return 4;
|
||||||
|
out->sensor[0] = '\0';
|
||||||
|
if (s) {
|
||||||
|
s = strchr(s, ':');
|
||||||
|
if (s) {
|
||||||
|
const char *open = strchr(s, '"');
|
||||||
|
if (open) {
|
||||||
|
const char *close = strchr(open + 1, '"');
|
||||||
|
if (close) {
|
||||||
|
size_t n = (size_t)(close - open - 1);
|
||||||
|
if (n >= sizeof out->sensor) n = sizeof out->sensor - 1;
|
||||||
|
memcpy(out->sensor, open + 1, n);
|
||||||
|
out->sensor[n] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#include "temperature.h"
|
||||||
|
#define ABSOLUTE_ZERO_C (-273.15)
|
||||||
|
double clamp_to_absolute_zero(double celsius) { return celsius < ABSOLUTE_ZERO_C ? ABSOLUTE_ZERO_C : celsius; }
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#include "temperature.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
assert(fabs(to_fahrenheit(0.0) - 32.0) < 1e-9);
|
||||||
|
assert(fabs(to_fahrenheit(100.0) - 212.0) < 1e-9);
|
||||||
|
|
||||||
|
Reading r;
|
||||||
|
assert(parse_reading("{\"sensor\":\"attic\",\"celsius\":21.50}", &r) == 0);
|
||||||
|
assert(strcmp(r.sensor, "attic") == 0);
|
||||||
|
assert(fabs(r.celsius - 21.5) < 1e-9);
|
||||||
|
assert(parse_reading("{\"celsius\":-300.0}", &r) == 0);
|
||||||
|
assert(fabs(clamp_to_absolute_zero(r.celsius) + 273.15) < 1e-9);
|
||||||
|
assert(parse_reading("nonsense", &r) != 0);
|
||||||
|
|
||||||
|
char buf[128];
|
||||||
|
Reading in = { "boiler", 65.25 };
|
||||||
|
assert(format_reading(&in, buf, sizeof buf) == 0);
|
||||||
|
assert(strstr(buf, "\"sensor\":\"boiler\"") != NULL);
|
||||||
|
|
||||||
|
puts("all tempsense assertions passed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user