diff options
author | Heikki Kallasjoki <fis@zem.fi> | 2018-12-07 00:17:09 +0000 |
---|---|---|
committer | Heikki Kallasjoki <fis+github@zem.fi> | 2018-12-12 19:10:51 +0000 |
commit | 8569c2c85bec19daa36e013448399c42cf75a257 (patch) | |
tree | 7444488e4e5ae5c98f575a2eafd522c6427ffbdf /test | |
parent | d03dea4d7dbfb62ed0c3c21cdd9ad350f0526432 (diff) | |
download | nano-exporter-8569c2c85bec19daa36e013448399c42cf75a257.tar.gz nano-exporter-8569c2c85bec19daa36e013448399c42cf75a257.tar.xz nano-exporter-8569c2c85bec19daa36e013448399c42cf75a257.zip |
Add a simple test harness and tests for the cpu collector.
Diffstat (limited to 'test')
-rw-r--r-- | test/Makefile | 47 | ||||
-rw-r--r-- | test/cpu_test.c | 76 | ||||
-rw-r--r-- | test/harness.c | 241 | ||||
-rw-r--r-- | test/harness.h | 55 | ||||
-rw-r--r-- | test/mock_scrape.c | 210 | ||||
-rw-r--r-- | test/mock_scrape.h | 30 | ||||
-rwxr-xr-x | test/run_tests.sh | 21 | ||||
-rw-r--r-- | test/stub.h | 25 |
8 files changed, 705 insertions, 0 deletions
diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..fdc89e7 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,47 @@ +# Copyright 2018 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +COLLECTOR_TESTS := cpu + +COLLECTOR_TEST_PROGS := $(foreach c,$(COLLECTOR_TESTS),$(c)_test) +COLLECTOR_TEST_OBJS := $(foreach p,$(COLLECTOR_TEST_PROGS),$(p).o) +COLLECTOR_TEST_IMPLS := $(foreach p,$(COLLECTOR_TEST_PROGS),$(p).impl.o) + +CFLAGS = -std=c11 -Wall -Wextra -pedantic -Wno-format-truncation -Os + +# test execution + +run_all: $(COLLECTOR_TEST_PROGS) run_tests.sh + @./run_tests.sh $(COLLECTOR_TEST_PROGS) + +.PHONY: run_all + +$(COLLECTOR_TEST_OBJS): %.o: %.c harness.h mock_scrape.h + $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< + +$(COLLECTOR_TEST_IMPLS): %_test.impl.o: ../%.c stub.h + $(CC) $(CFLAGS) $(CPPFLAGS) -include stub.h -c -o $@ $< + +$(COLLECTOR_TEST_PROGS): %: %.o %.impl.o harness.o mock_scrape.o util.o + $(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS) + +util.o: ../util.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< + +# make clean + +.PHONY: clean +clean: + $(RM) $(COLLECTOR_TEST_PROGS) $(COLLECTOR_TEST_OBJS) $(COLLECTOR_TEST_IMPLS) + $(RM) harness.o mock_scrape.o util.o diff --git a/test/cpu_test.c b/test/cpu_test.c new file mode 100644 index 0000000..4698236 --- /dev/null +++ b/test/cpu_test.c @@ -0,0 +1,76 @@ +/* + * Copyright 2018 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "harness.h" +#include "mock_scrape.h" +#include "../collector.h" + +extern const struct collector cpu_collector; +void cpu_test_override_tick(void *ctx, long tick); + +TEST(cpu_metrics) { + test_write_file( + env, + "proc/stat", + "cpu 1222 2444 3666 4888 6110 7332 8554 9776\n" + "cpu0 1111 2222 3333 4444 5555 6666 7777 8888\n" + "cpu1 111 222 333 444 555 666 777 888\n"); + scrape_req *req = mock_scrape_start(env); + + void *ctx = cpu_collector.init(0, 0); + cpu_test_override_tick(ctx, 100); + cpu_collector.collect(req, ctx); + + mock_scrape_expect(req, "node_cpu_seconds_total", LABEL_LIST({"cpu", "0"}, {"mode", "user"}), 11.11); + mock_scrape_expect(req, "node_cpu_seconds_total", LABEL_LIST({"cpu", "0"}, {"mode", "nice"}), 22.22); + mock_scrape_expect(req, "node_cpu_seconds_total", LABEL_LIST({"cpu", "0"}, {"mode", "system"}), 33.33); + mock_scrape_expect(req, "node_cpu_seconds_total", LABEL_LIST({"cpu", "0"}, {"mode", "idle"}), 44.44); + mock_scrape_expect(req, "node_cpu_seconds_total", LABEL_LIST({"cpu", "0"}, {"mode", "iowait"}), 55.55); + mock_scrape_expect(req, "node_cpu_seconds_total", LABEL_LIST({"cpu", "0"}, {"mode", "irq"}), 66.66); + mock_scrape_expect(req, "node_cpu_seconds_total", LABEL_LIST({"cpu", "0"}, {"mode", "softirq"}), 77.77); + mock_scrape_expect(req, "node_cpu_seconds_total", LABEL_LIST({"cpu", "0"}, {"mode", "steal"}), 88.88); + mock_scrape_expect(req, "node_cpu_seconds_total", LABEL_LIST({"cpu", "1"}, {"mode", "user"}), 1.11); + mock_scrape_expect(req, "node_cpu_seconds_total", LABEL_LIST({"cpu", "1"}, {"mode", "nice"}), 2.22); + mock_scrape_expect(req, "node_cpu_seconds_total", LABEL_LIST({"cpu", "1"}, {"mode", "system"}), 3.33); + mock_scrape_expect(req, "node_cpu_seconds_total", LABEL_LIST({"cpu", "1"}, {"mode", "idle"}), 4.44); + mock_scrape_expect(req, "node_cpu_seconds_total", LABEL_LIST({"cpu", "1"}, {"mode", "iowait"}), 5.55); + mock_scrape_expect(req, "node_cpu_seconds_total", LABEL_LIST({"cpu", "1"}, {"mode", "irq"}), 6.66); + mock_scrape_expect(req, "node_cpu_seconds_total", LABEL_LIST({"cpu", "1"}, {"mode", "softirq"}), 7.77); + mock_scrape_expect(req, "node_cpu_seconds_total", LABEL_LIST({"cpu", "1"}, {"mode", "steal"}), 8.88); + mock_scrape_expect_no_more(req); + mock_scrape_free(req); +} + +TEST(cpufreq_metrics) { + test_write_file(env, "sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq", "1234567\n"); + test_write_file(env, "sys/devices/system/cpu/cpu1/cpufreq/scaling_cur_freq", "987654\n"); + scrape_req *req = mock_scrape_start(env); + + void *ctx = cpu_collector.init(0, 0); + cpu_collector.collect(req, ctx); + + mock_scrape_expect(req, "node_cpu_frequency_hertz", LABEL_LIST({"cpu", "0"}), 1234567000.0); + mock_scrape_expect(req, "node_cpu_frequency_hertz", LABEL_LIST({"cpu", "1"}), 987654000.0); + mock_scrape_expect_no_more(req); + mock_scrape_free(req); +} + +TEST_SUITE { + TEST_SUITE_START; + RUN_TEST(cpu_metrics); + RUN_TEST(cpufreq_metrics); + TEST_SUITE_END; +} diff --git a/test/harness.c b/test/harness.c new file mode 100644 index 0000000..d88a092 --- /dev/null +++ b/test/harness.c @@ -0,0 +1,241 @@ +/* + * Copyright 2018 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _POSIX_C_SOURCE 200809L + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <setjmp.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "harness.h" +#include "../util.h" + +struct test_env { + const char *current_testcase; + jmp_buf escape_env; + + int tmpdir_fd; + const char *tmpdir_name; + + int testdir_fd; + char *testdir_name; +}; + +bool test_main(test_env *env); + +static void testdir_setup(test_env *env); +static int testdir_getdir(test_env *env, const char *path); +static void testdir_closedir(test_env *env, int dir_fd); +static bool testdir_rmtree(int parent_fd, int dir_fd, const char *dir_name); + +void test_write_file(test_env *env, const char *path, const char *contents) { + if (*path == '/') + test_fail(env, "test_write_file path is absolute: %s", path); + const char *file = strrchr(path, '/'); + file = file ? file + 1 : path; + + testdir_setup(env); + int dir_fd = testdir_getdir(env, path); + int fd = openat(dir_fd, file, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd == -1) + test_fail(env, "in %s: %s: unable to open for writing: %s", path, file, strerror(errno)); + testdir_closedir(env, dir_fd); + + if (write_all(fd, contents, strlen(contents)) == -1) + test_fail(env, "in %s: %s: failed write: %s", path, file, strerror(errno)); + + close(fd); +} + +void test_fail(test_env *env, const char *err, ...) { + va_list ap; + + printf("FAILED: "); + va_start(ap, err); + vprintf(err, ap); + va_end(ap); + printf("\n"); + + longjmp(env->escape_env, 1); +} + +bool test_start(test_env *env, const char *name) { + printf("test: %s\n", name); + env->current_testcase = name; + + env->testdir_fd = -1; + env->testdir_name = 0; + + return true; +} + +jmp_buf *test_escape(test_env *env) { + return &env->escape_env; +} + +bool test_cleanup(test_env *env) { + int success = true; + + if (env->testdir_name) { + success = success && testdir_rmtree(env->tmpdir_fd, env->testdir_fd, env->testdir_name); + free(env->testdir_name); + } + + if (!success) + printf("FAILED: test cleanup failed\n"); + + env->current_testcase = 0; + return success; +} + +int main(void) { + test_env env; + env.current_testcase = 0; + env.tmpdir_fd = -1; + + bool success = test_main(&env); + + if (env.tmpdir_fd != -1) + close(env.tmpdir_fd); + + return success ? EXIT_SUCCESS : EXIT_FAILURE; +} + +static void testdir_setup(test_env *env) { + char buf[64]; + + if (env->testdir_name) + return; // already done + + if (env->tmpdir_fd == -1) { + env->tmpdir_name = getenv("TMPDIR"); + if (!env->tmpdir_name) + env->tmpdir_name = "/tmp"; + env->tmpdir_fd = open(env->tmpdir_name, O_RDONLY | O_DIRECTORY); + if (env->tmpdir_fd == -1) + test_fail(env, "failed to open temporary file directory: %s: %s", env->tmpdir_name, strerror(errno)); + } + + for (unsigned attempt = 0; attempt < 100; attempt++) { + unsigned base = 0; + for (const char *p = env->current_testcase; p && *p; p++) + base = (base << 5) + base + (unsigned char) *p; + snprintf(buf, sizeof buf, "nano_exporter_test_%x", base + attempt); + if (mkdirat(env->tmpdir_fd, buf, 0700) == 0) { + env->testdir_fd = openat(env->tmpdir_fd, buf, O_RDONLY | O_DIRECTORY); + if (env->testdir_fd == -1) + test_fail(env, "failed to open test work directory in %s: %s: %s", env->tmpdir_name, buf, strerror(errno)); + env->testdir_name = must_strdup(buf); + if (fchdir(env->testdir_fd) == -1) + test_fail(env, "unable to change to test work directory in %s: %s: %s", env->tmpdir_name, buf, strerror(errno)); + return; + } + } + + test_fail(env, "failed to create test work directory in %s: %s: %s", env->tmpdir_name, buf, strerror(errno)); +} + +static int testdir_getdir(test_env *env, const char *orig_path) { + char buf[NAME_MAX + 1]; + struct stat st; + + int parent_fd = env->testdir_fd; + + const char *path = orig_path; + char *slash; + while ((slash = strchr(path, '/'))) { + if (slash - path > (ptrdiff_t) sizeof buf - 1) + test_fail(env, "path name component too long: %s", path); + snprintf(buf, sizeof buf, "%.*s", (int)(slash - path), path); + + if (fstatat(parent_fd, buf, &st, AT_SYMLINK_NOFOLLOW) == 0) { + if (!S_ISDIR(st.st_mode)) + test_fail(env, "in %s: %s: exists and not a directory", orig_path, buf); + // already a directory, just try to open it + } else if (errno != ENOENT) { + test_fail(env, "in %s: %s: failed to stat: %s", orig_path, buf, strerror(errno)); + } else { + // doesn't exist: make a new directory + if (mkdirat(parent_fd, buf, 0700) == -1) + test_fail(env, "in %s: %s: failed to mkdir: %s", orig_path, buf, strerror(errno)); + } + + int fd = openat(parent_fd, buf, O_RDONLY | O_DIRECTORY); + if (fd == -1) + test_fail(env, "in %s: %s: failed to open: %s", orig_path, buf, strerror(errno)); + + if (parent_fd != env->testdir_fd) + close(parent_fd); + parent_fd = fd; + + path = slash + 1; + } + + return parent_fd; +} + +static void testdir_closedir(test_env *env, int dir_fd) { + if (dir_fd != env->testdir_fd) + close(dir_fd); +} + +static bool testdir_rmtree(int parent_fd, int dir_fd, const char *dir_name) { + DIR *d = fdopendir(dir_fd); + if (!d) { + close(dir_fd); + return false; + } + + bool success = true; + + struct dirent *dent; + while ((dent = readdir(d))) { + if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) + continue; + + struct stat st; + if (fstatat(dir_fd, dent->d_name, &st, AT_SYMLINK_NOFOLLOW) == -1) { + success = false; + continue; + } + + if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { + if (unlinkat(dir_fd, dent->d_name, 0) == -1) + success = false; + } else if (S_ISDIR(st.st_mode)) { + int fd = openat(dir_fd, dent->d_name, O_RDONLY | O_DIRECTORY); + if (fd == -1 || !testdir_rmtree(dir_fd, fd, dent->d_name)) + success = false; + } else { + printf("unexpected item in bagging area: %s: not file, link or directory\n", dent->d_name); + success = false; + } + } + + closedir(d); + if (success) + success = unlinkat(parent_fd, dir_name, AT_REMOVEDIR) == 0; + + return success; +} diff --git a/test/harness.h b/test/harness.h new file mode 100644 index 0000000..e7013a7 --- /dev/null +++ b/test/harness.h @@ -0,0 +1,55 @@ +/* + * Copyright 2018 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NANO_EXPORTER_TEST_HARNESS_H_ +#define NANO_EXPORTER_TEST_HARNESS_H_ 1 + +#include <setjmp.h> +#include <stdbool.h> +#include <stdlib.h> + +typedef struct test_env test_env; + +// functions for use from tests + +void test_write_file(test_env *env, const char *path, const char *contents); + +void test_fail(test_env *env, const char *err, ...); + +// macros for defining test cases + +#define TEST(name) void testcase_##name(test_env *env) + +#define TEST_SUITE bool test_main(test_env *env) + +#define TEST_SUITE_START volatile bool success = true + +#define RUN_TEST(name) do { \ + if (!test_start(env, #name)) return false; \ + if (setjmp(*test_escape(env)) != 0) { success = false; break; } \ + testcase_##name(env); \ + if (!test_cleanup(env)) return false; \ + } while (0) + +#define TEST_SUITE_END return success + +// internal functions used by the macros + +bool test_start(test_env *env, const char *name); +jmp_buf *test_escape(test_env *env); +bool test_cleanup(test_env *env); + +#endif // NANO_EXPORTER_TEST_HARNESS_H_ diff --git a/test/mock_scrape.c b/test/mock_scrape.c new file mode 100644 index 0000000..82a17d7 --- /dev/null +++ b/test/mock_scrape.c @@ -0,0 +1,210 @@ +/* + * Copyright 2018 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <math.h> +#include <string.h> + +#include "harness.h" +#include "../scrape.h" +#include "../util.h" + +#define MAX_METRICS 128 +#define MAX_RAWS 16 + +struct scrape_metric { + char *metric; + struct label *labels; + double value; +}; + +struct scrape_req { + test_env *env; + + struct scrape_metric metrics[MAX_METRICS]; + unsigned metrics_written; + unsigned metrics_tested; + + const char *raws[MAX_RAWS]; + unsigned raws_written; + unsigned raws_tested; +}; + +static void dump_metrics(scrape_req *req); +static void dump_metric(const char *metric, const struct label *labels, double value); + +static struct label *copy_labels(const struct label *labels); +static void free_labels(struct label *labels); +static void compare_labels(scrape_req *req, const struct label *got, const struct label *expected); + +void scrape_write(scrape_req *req, const char *metric, const struct label *labels, double value) { + if (req->metrics_written >= MAX_METRICS) + test_fail(req->env, "exceeded MAX_METRICS: %u metrics already written", req->metrics_written); + + struct scrape_metric *rec = &req->metrics[req->metrics_written++]; + rec->metric = must_strdup(metric); + rec->labels = copy_labels(labels); + rec->value = value; +} + +void scrape_write_raw(scrape_req *req, const void *buf, size_t len) { + if (req->metrics_written >= MAX_RAWS) + test_fail(req->env, "exceeded MAX_RAWS: %u raw blocks already written", req->raws_written); + + test_fail(req->env, "TODO: implement scrape_write_raw"); +} + +scrape_req *mock_scrape_start(test_env *env) { + scrape_req *req = must_malloc(sizeof *req); + req->env = env; + req->metrics_written = req->metrics_tested = 0; + req->raws_written = req->raws_tested = 0; + return req; +} + +void mock_scrape_free(scrape_req *req) { + for (unsigned i = 0; i < req->metrics_written; i++) { + free(req->metrics[i].metric); + free_labels(req->metrics[i].labels); + } + free(req); +} + +void mock_scrape_expect(scrape_req *req, const char *metric, const struct label *labels, double value) { + if (req->metrics_tested >= req->metrics_written) { + dump_metrics(req); + test_fail(req->env, "got no more metrics, expected %s", metric); + } + struct scrape_metric *got = &req->metrics[req->metrics_tested]; + + if (strcmp(got->metric, metric) != 0) { + dump_metrics(req); + test_fail(req->env, "got metric %s, expected %s", got->metric, metric); + } + compare_labels(req, got->labels, labels); + if (fabs(got->value - value) >= 1e-6) { + dump_metrics(req); + test_fail(req->env, "got metric value %.16g, expected %.16g", got->value, value); + } + + req->metrics_tested++; +} + +void mock_scrape_expect_raw(scrape_req *req, const char *str) { + test_fail(req->env, "TODO: implement"); +} + +void mock_scrape_expect_no_more(scrape_req *req) { + if (req->metrics_tested < req->metrics_written) { + dump_metrics(req); + test_fail(req->env, "got metric %s, expected no more metrics", req->metrics[req->metrics_tested].metric); + } + if (req->raws_tested < req->raws_written) { + test_fail(req->env, "TODO: raw stuff"); + } +} + +static void dump_metrics(scrape_req *req) { + if (req->metrics_tested > 0) { + printf("expected metrics:\n"); + for (unsigned i = 0; i < req->metrics_tested; i++) { + struct scrape_metric *m = &req->metrics[i]; + dump_metric(m->metric, m->labels, m->value); + } + } + + if (req->metrics_written > req->metrics_tested) { + printf("unexpected metrics:\n"); + for (unsigned i = req->metrics_tested; i < req->metrics_written; i++) { + struct scrape_metric *m = &req->metrics[i]; + dump_metric(m->metric, m->labels, m->value); + } + } + + // TODO: dump raw +} + +static void dump_metric(const char *metric, const struct label *labels, double value) { + fputs(" ", stdout); + fputs(metric, stdout); + if (labels && labels->key) { + fputc('{', stdout); + for (const struct label *l = labels; l->key; l++) { + if (l != labels) + fputc(',', stdout); + printf("%s=\"%s\"", l->key, l->value); + } + fputc('}', stdout); + } + printf(" %.16g\n", value); +} + + +static struct label *copy_labels(const struct label *labels) { + if (!labels) + return 0; + + size_t count = 0; + for (const struct label *p = labels; p->key; p++) + count++; + if (!count) + return 0; + + struct label *copy = must_malloc((count + 1) * sizeof *copy); + for (size_t i = 0; i < count; i++) { + copy[i].key = must_strdup(labels[i].key); + copy[i].value = must_strdup(labels[i].value); + } + copy[count] = LABEL_END; + + return copy; +} + +static void free_labels(struct label *labels) { + if (!labels) + return; + for (struct label *p = labels; p->key; p++) { + free(p->key); + free(p->value); + } + free(labels); +} + +static void compare_labels(scrape_req *req, const struct label *got, const struct label *expected) { + if (!got || !got->key) { + if (!expected || !expected->key) + return; + dump_metrics(req); + test_fail(req->env, "got no more labels, expected %s=\"%s\"", expected->key, expected->value); + } + + while (got->key && expected->key) { + if (strcmp(got->key, expected->key) != 0 || strcmp(got->value, expected->value) != 0) { + dump_metrics(req); + test_fail(req->env, "got label %s=\"%s\", expected %s=\"%s\"", got->key, got->value, expected->key, expected->value); + } + got++; + expected++; + } + + if (got->key) { + dump_metrics(req); + test_fail(req->env, "got label %s=\"%s\", expected no more labels", got->key, got->value); + } + if (expected->key) { + dump_metrics(req); + test_fail(req->env, "got no more labels, expected %s=\"%s\"", expected->key, expected->value); + } +} diff --git a/test/mock_scrape.h b/test/mock_scrape.h new file mode 100644 index 0000000..bbb3767 --- /dev/null +++ b/test/mock_scrape.h @@ -0,0 +1,30 @@ +/* + * Copyright 2018 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NANO_EXPORTER_TEST_MOCK_SCRAPE_H_ +#define NANO_EXPORTER_TEST_MOCK_SCRAPE_H_ 1 + +#include "harness.h" +#include "../scrape.h" + +scrape_req *mock_scrape_start(test_env *env); +void mock_scrape_free(scrape_req *req); + +void mock_scrape_expect(scrape_req *req, const char *metric, const struct label *labels, double value); +void mock_scrape_expect_raw(scrape_req *req, const char *str); +void mock_scrape_expect_no_more(scrape_req *req); + +#endif // NANO_EXPORTER_TEST_MOCK_SCRAPE_H_ diff --git a/test/run_tests.sh b/test/run_tests.sh new file mode 100755 index 0000000..12a0e1a --- /dev/null +++ b/test/run_tests.sh @@ -0,0 +1,21 @@ +#! /bin/bash + +failed=0 + +for suite in "$@"; do + echo "$suite:" + if ./$suite; then + echo "...pass" + else + echo "...FAIL" + failed=1 + fi +done + +if (($failed)); then + printf "\x1b[31;1mTESTS FAILED\x1b[0m\n" + exit 1 +fi + +printf "\x1b[32;1mall tests passed\x1b[0m\n" +exit 0 diff --git a/test/stub.h b/test/stub.h new file mode 100644 index 0000000..7aee0b3 --- /dev/null +++ b/test/stub.h @@ -0,0 +1,25 @@ +/* + * Copyright 2018 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NANO_EXPORTER_TEST_STUB_H_ +#define NANO_EXPORTER_TEST_STUB_H_ 1 + +// This file is prepended in front of collector implementations (via +// -include stub.h) when compiling them for tests. + +#define PATH(p) ("." p) + +#endif // NANO_EXPORTER_TEST_STUB_H_ |