• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Alan-Jowett / bpf_conformance / 18478363399

10 Oct 2025 09:57PM UTC coverage: 95.284% (-0.3%) from 95.546%
18478363399

push

github

web-flow
Bump the actions group across 1 directory with 4 updates (#415)

Bumps the actions group with 4 updates in the / directory: [docker/login-action](https://github.com/docker/login-action), [github/codeql-action](https://github.com/github/codeql-action), [actions/dependency-review-action](https://github.com/actions/dependency-review-action) and [ossf/scorecard-action](https://github.com/ossf/scorecard-action).


Updates `docker/login-action` from 3.5.0 to 3.6.0
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/184bdaa07...5e57cd118)

Updates `github/codeql-action` from 3.30.5 to 4.30.8
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/3599b3baa...f443b600d)

Updates `actions/dependency-review-action` from 4.8.0 to 4.8.1
- [Release notes](https://github.com/actions/dependency-review-action/releases)
- [Commits](https://github.com/actions/dependency-review-action/compare/56339e523...40c09b7dc)

Updates `ossf/scorecard-action` from 2.4.2 to 2.4.3
- [Release notes](https://github.com/ossf/scorecard-action/releases)
- [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md)
- [Commits](https://github.com/ossf/scorecard-action/compare/05b42c624...4eaacf054)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 3.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: github/codeql-action
  dependency-version: 4.30.8
  dependency-type: direct:production
  update-typ... (continued)

1091 of 1145 relevant lines covered (95.28%)

8516.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.02
/src/runner.cc
1
// Copyright (c) Microsoft Corporation
2
// SPDX-License-Identifier: MIT
3

4
#include <filesystem>
5
#include <iostream>
6
#include <sstream>
7
#include <boost/program_options.hpp>
8

9
#include "../include/bpf_conformance.h"
10

11
#if defined(__linux__)
12
#include <signal.h>
13

14
static void
15
sig_handler(int signo)
×
16
{
17
    if (signo != SIGPIPE) {
×
18
        std::cerr << "Received signal " << signo << std::endl;
×
19
        exit(1);
×
20
    }
21
}
×
22

23
static void
24
install_sigpipe_handler()
68✔
25
{
26
    struct sigaction sa;
27
    sa.sa_handler = sig_handler;
68✔
28
    sigemptyset(&sa.sa_mask);
68✔
29
    sa.sa_flags = 0;
68✔
30
    sigaction(SIGPIPE, &sa, NULL);
68✔
31
}
68✔
32
#else
33
static void
34
install_sigpipe_handler()
35
{
36
}
37
#endif
38

39
// This program reads a collection of BPF test programs from the test folder,
40
// assembles the BPF programs to byte code, calls the plugin to execute the
41
// BPF programs, and compares the results with the expected results.
42
// If the test program has memory contents, the program will also pass the
43
// memory contents to the plugin.
44

45
/**
46
 * @brief Read the list of test files from the provided directory.
47
 *
48
 * @param[in] test_file_directory Path to the collection of test files.
49
 * @return Vector of test files names.
50
 */
51
static std::vector<std::filesystem::path>
52
_get_test_files(const std::filesystem::path& test_file_directory)
14✔
53
{
54
    std::vector<std::filesystem::path> result;
7✔
55
    for (auto& p : std::filesystem::directory_iterator(test_file_directory)) {
4,027✔
56
        if (p.path().extension() == ".data") {
3,996✔
57
            result.push_back(p.path());
2,664✔
58
        }
59
    }
6✔
60
    return result;
12✔
61
}
2✔
62

63
static const std::map<std::string, bpf_conformance_groups_t> _conformance_groups = {
64
    {"atomic32", bpf_conformance_groups_t::atomic32},
65
    {"atomic64", bpf_conformance_groups_t::atomic32 | bpf_conformance_groups_t::atomic64},
66
    {"base32", bpf_conformance_groups_t::base32},
67
    {"base64", bpf_conformance_groups_t::base32 | bpf_conformance_groups_t::base64},
68
    {"callx", bpf_conformance_groups_t::callx},
69
    {"divmul32", bpf_conformance_groups_t::divmul32},
70
    {"divmul64", bpf_conformance_groups_t::divmul32 | bpf_conformance_groups_t::divmul64},
71
    {"packet", bpf_conformance_groups_t::packet}};
72

73
static std::optional<bpf_conformance_groups_t>
74
_get_conformance_group_by_name(std::string group)
10✔
75
{
76
    if (!_conformance_groups.contains(group)) {
10✔
77
        return {};
×
78
    }
79
    return _conformance_groups.find(group)->second;
10✔
80
}
81

82
static std::string
83
_get_conformance_group_names()
68✔
84
{
85
    std::string result;
34✔
86
    for (const auto& entry : _conformance_groups) {
612✔
87
        if (!result.empty()) {
544✔
88
            result += ", ";
238✔
89
        }
90
        result += entry.first;
272✔
91
    }
92
    return result;
68✔
93
}
94

95
int
96
main(int argc, char** argv)
68✔
97
{
98
    // Handle sigpipe handler to avoid crashing when writing to a closed pipe.
99
    install_sigpipe_handler();
68✔
100

101
    try {
102
        boost::program_options::options_description desc("Options");
102✔
103
        desc.add_options()("help", "Print help messages")(
102✔
104
            "test_file_path", boost::program_options::value<std::string>(), "Path to test file")(
68✔
105
            "test_file_directory", boost::program_options::value<std::string>(), "Path to test file directory")(
68✔
106
            "plugin_path", boost::program_options::value<std::string>(), "Path to plugin")(
68✔
107
            "plugin_options", boost::program_options::value<std::string>(), "Options to pass to plugin")(
68✔
108
            "list_instructions", boost::program_options::value<bool>(), "List instructions used and not used in tests")(
68✔
109
            "list_used_instructions", boost::program_options::value<bool>(), "List instructions used in tests")(
68✔
110
            "list_unused_instructions", boost::program_options::value<bool>(), "List instructions not used in tests")(
68✔
111
            "debug", boost::program_options::value<bool>(), "Print debug information")(
68✔
112
            "xdp_prolog", boost::program_options::value<bool>(), "XDP prolog")(
68✔
113
            "elf", boost::program_options::value<bool>(), "ELF format")(
68✔
114
            "cpu_version", boost::program_options::value<std::string>(), "CPU version (valid values: v1, v2, v3, v4), default is v3")(
136✔
115
            "include_groups",
116
            boost::program_options::value<std::vector<std::string>>()->multitoken(),
34✔
117
            ("Include conformance groups (valid group names: " + _get_conformance_group_names() + ")").c_str())(
204✔
118
            "exclude_groups",
119
            boost::program_options::value<std::vector<std::string>>()->multitoken(),
34✔
120
            "Exclude conformance groups, where callx and packet are excluded by default")(
102✔
121
            "include_regex", boost::program_options::value<std::string>(), "Include regex")(
68✔
122
            "exclude_regex", boost::program_options::value<std::string>(), "Exclude regex");
34✔
123

124
        boost::program_options::variables_map vm;
68✔
125
        boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm);
102✔
126
        boost::program_options::notify(vm);
68✔
127

128
        if (vm.count("help")) {
102✔
129
            std::cout << "Usage: bpf_conformance_runner [options]" << std::endl;
1✔
130
            std::cout << std::endl << desc;
2✔
131
            std::cout << R"(
2✔
132
Examples:
133
  bpf_conformance_runner --test_file_directory ./tests --plugin_path ./my_plugin --exclude_groups atomic64
134
    Run all tests in the ./tests directory, allowing instructions in cpu version 3
135
    but without the atomic64 (or atomic32 which is a subset of atomic64), callx, or packet
136
    conformance groups.
137

138
  bpf_conformance_runner --test_file_directory ./tests --plugin_path ./my_plugin --cpu_version v2 --include_groups callx atomic64 --exclude_groups atomic32
139
    Run all tests in the ./tests directory, allowing instructions in cpu version 2
140
    plus the callx and atomic64 conformance groups, except for those in atomic32 or packet.
141

142
  bpf_conformance_runner --test_file_directory ./tests --plugin_path ./my_plugin --exclude_regex "lock+"
143
    Run all tests in the ./tests directory, allowing instructions in cpu version 3
144
    but without the callx or packet conformance group, skipping any tests whose
145
    filenames contain "loc" followed by one or more "k"s.
146
)";
147
            return 1;
1✔
148
        }
149

150
        if ((vm.count("test_file_path") == 0) && (vm.count("test_file_directory") == 0)) {
115✔
151
            std::cout << "test_file_path or test_file_directory is required" << std::endl;
1✔
152
            return 1;
1✔
153
        }
154

155
        if (!vm.count("plugin_path")) {
96✔
156
            std::cout << "plugin_path is required" << std::endl;
1✔
157
            return 1;
1✔
158
        }
159

160
        std::string plugin_path = vm["plugin_path"].as<std::string>();
93✔
161
        std::stringstream plugin_options_stream(
162
            vm.count("plugin_options") ? vm["plugin_options"].as<std::string>() : "");
260✔
163

164
        std::vector<std::string> plugin_options;
31✔
165
        std::string option;
31✔
166
        while (std::getline(plugin_options_stream, option, ' ')) {
108✔
167
            plugin_options.push_back(option);
46✔
168
        }
169

170
        // Assume version 3 if not specified.
171
        bpf_conformance_test_cpu_version_t cpu_version = bpf_conformance_test_cpu_version_t::v3;
31✔
172
        if (vm.count("cpu_version")) {
93✔
173
            std::string cpu_version_string = vm["cpu_version"].as<std::string>();
36✔
174
            if (cpu_version_string == "v1") {
18✔
175
                cpu_version = bpf_conformance_test_cpu_version_t::v1;
4✔
176
            } else if (cpu_version_string == "v2") {
10✔
177
                cpu_version = bpf_conformance_test_cpu_version_t::v2;
1✔
178
            } else if (cpu_version_string == "v3") {
8✔
179
                cpu_version = bpf_conformance_test_cpu_version_t::v3;
3✔
180
            } else if (cpu_version_string == "v4") {
2✔
181
                cpu_version = bpf_conformance_test_cpu_version_t::v4;
182
            } else {
183
                std::cout << "Invalid CPU version" << std::endl;
1✔
184
                return 1;
1✔
185
            }
186
        }
9✔
187

188
        // Enable default conformance groups, which don't include callx or packet.
189
        bpf_conformance_groups_t groups = bpf_conformance_groups_t::default_groups;
30✔
190
        if (vm.count("include_groups")) {
90✔
191
            auto include_groups = vm["include_groups"].as<std::vector<std::string>>();
×
192
            for (std::string group_name : include_groups) {
×
193
                if (auto group = _get_conformance_group_by_name(group_name)) {
×
194
                    groups |= *group;
×
195
                } else {
196
                    std::cout << "Invalid group: " << group_name << std::endl;
197
                    return 1;
×
198
                }
199
            }
200
        }
×
201
        if (vm.count("exclude_groups")) {
90✔
202
            auto exclude_groups = vm["exclude_groups"].as<std::vector<std::string>>();
25✔
203
            for (std::string group_name : exclude_groups) {
20✔
204
                if (auto group = _get_conformance_group_by_name(group_name)) {
10✔
205
                    groups &= ~(*group);
10✔
206
                } else {
207
                    std::cout << "Invalid group: " << group_name << std::endl;
208
                    return 1;
×
209
                }
210
            }
5✔
211
        }
10✔
212

213
        std::optional<std::string> include_regex = vm.count("include_regex") ? std::make_optional(vm["include_regex"].as<std::string>()) : std::nullopt;
104✔
214
        std::optional<std::string> exclude_regex = vm.count("exclude_regex") ? std::make_optional(vm["exclude_regex"].as<std::string>()) : std::nullopt;
122✔
215

216
        std::vector<std::filesystem::path> tests;
30✔
217
        if (vm.count("test_file_path")) {
90✔
218
            tests.push_back(vm["test_file_path"].as<std::string>());
92✔
219
        } else if (vm.count("test_file_directory")) {
21✔
220
            tests = _get_test_files(vm["test_file_directory"].as<std::string>());
31✔
221
        }
222
        std::sort(tests.begin(), tests.end());
58✔
223

224
        size_t tests_passed = 0;
29✔
225
        size_t tests_run = 0;
29✔
226
        bool show_instructions = vm.count("list_instructions") ? vm["list_instructions"].as<bool>() : false;
91✔
227
        bool debug = vm.count("debug") ? vm["debug"].as<bool>() : false;
131✔
228
        bool list_used_instructions = vm.count("list_used_instructions") ? vm["list_used_instructions"].as<bool>() : false;
89✔
229
        bool list_unused_instructions = vm.count("list_unused_instructions") ? vm["list_unused_instructions"].as<bool>() : false;
89✔
230
        bool xdp_prolog = vm.count("xdp_prolog") ? vm["xdp_prolog"].as<bool>() : false;
129✔
231
        bool elf_format = vm.count("elf") ? vm["elf"].as<bool>() : false;
89✔
232
        bpf_conformance_list_instructions_t list_instructions = bpf_conformance_list_instructions_t::LIST_INSTRUCTIONS_NONE;
29✔
233
        if (show_instructions) {
58✔
234
            list_instructions = bpf_conformance_list_instructions_t::LIST_INSTRUCTIONS_ALL;
2✔
235
        } else if (list_used_instructions) {
54✔
236
            list_instructions = bpf_conformance_list_instructions_t::LIST_INSTRUCTIONS_USED;
1✔
237
        } else if (list_unused_instructions) {
52✔
238
            list_instructions = bpf_conformance_list_instructions_t::LIST_INSTRUCTIONS_UNUSED;
1✔
239
        }
240

241
        bpf_conformance_options_t options;
29✔
242
        options.include_test_regex = include_regex;
29✔
243
        options.exclude_test_regex = exclude_regex;
29✔
244
        options.cpu_version = cpu_version;
58✔
245
        options.groups = groups;
58✔
246
        options.list_instructions_option = list_instructions;
58✔
247
        options.debug = debug;
58✔
248
        options.xdp_prolog = xdp_prolog;
58✔
249
        options.elf_format = elf_format;
58✔
250

251
        std::map<std::filesystem::path, std::tuple<bpf_conformance_test_result_t, std::string>> test_results;
29✔
252
        test_results = bpf_conformance_options(tests, plugin_path, plugin_options, options);
94✔
253

254
        // At the end of all the tests, print a summary of the results.
255
        std::cout << "Test results:" << std::endl;
22✔
256
        for (auto& test : test_results) {
2,740✔
257
            auto [result, message] = test.second;
1,348✔
258
            switch (result) {
2,696✔
259
            case bpf_conformance_test_result_t::TEST_RESULT_PASS:
731✔
260
                std::cout << "PASS: " << test.first << std::endl;
1,462✔
261
                tests_passed++;
1,462✔
262
                tests_run++;
1,462✔
263
                break;
1,462✔
264
            case bpf_conformance_test_result_t::TEST_RESULT_FAIL:
6✔
265
                std::cout << "FAIL: " << test.first << " " << message << std::endl;
12✔
266
                tests_run++;
12✔
267
                break;
12✔
268
            case bpf_conformance_test_result_t::TEST_RESULT_ERROR:
59✔
269
                std::cout << "ERROR: " << test.first << " " << message << std::endl;
118✔
270
                tests_run++;
118✔
271
                break;
118✔
272
            case bpf_conformance_test_result_t::TEST_RESULT_SKIP:
552✔
273
                std::cout << "SKIP: " << test.first << " " << message << std::endl;
1,104✔
274
                break;
552✔
275
            case bpf_conformance_test_result_t::TEST_RESULT_UNKNOWN:
276
                std::cout << "UNKNOWN: " << test.first << " " << message << std::endl;
×
277
                break;
278
            }
279
        }
1,348✔
280

281
        std::cout << "Passed " << tests_passed << " out of " << tests_run << " tests." << std::endl;
22✔
282

283
        return tests_passed == tests_run ? 0 : 1;
44✔
284
    } catch (std::filesystem::filesystem_error& e) {
239✔
285
        std::cerr << "Error reading test files: " << e.what() << std::endl;
2✔
286
        return 2;
1✔
287
    } catch (std::exception& e) {
16✔
288
        std::cerr << "Unhandled Exception reached the top of main: " << e.what() << ", application will now exit"
14✔
289
                  << std::endl;
7✔
290
        return 2;
7✔
291
    } catch (...) {
14✔
292
        std::cerr << "Unhandled Exception reached the top of main: "
293
                  << ", application will now exit" << std::endl;
294
        return 2;
295
    }
×
296
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc