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

mendersoftware / mender / 950534094

pending completion
950534094

push

gitlab-ci

kacf
chore: Add `StartsWith` and `EndsWith` generic utility functions.

Also use the latter in `states.cpp`.

Signed-off-by: Kristian Amlie <kristian.amlie@northern.tech>

1 of 1 new or added line in 1 file covered. (100.0%)

4931 of 6276 relevant lines covered (78.57%)

196.18 hits per line

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

69.61
/common/testing.cpp
1
// Copyright 2023 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
#include <common/testing.hpp>
16

17
#include <cstdlib>
18
#include <filesystem>
19
#include <random>
20
#include <iostream>
21

22
#include <common/json.hpp>
23
#include <common/log.hpp>
24
#include <common/path.hpp>
25
#include <common/processes.hpp>
26

27
namespace mender {
28
namespace common {
29
namespace testing {
30

31
namespace fs = std::filesystem;
32

33
namespace log = mender::common::log;
34
namespace path = mender::common::path;
35

36
shared_ptr<ostream> AssertInDeathTestHelper(const char *func, const char *file, int line) {
×
37
        // Unsuccessful assert. Return a stream which prints to stderr, and which aborts when it is
38
        // destroyed (at the end of the statement evaluation).
39
        cerr << "Assert at " << func << " in " << file << ":" << line << endl;
×
40
        return shared_ptr<ostream>(new ostream(cerr.rdbuf()), [](ostream *) { std::abort(); });
×
41
}
42

43
TemporaryDirectory::TemporaryDirectory() {
317✔
44
        fs::path path = fs::temp_directory_path();
317✔
45
        path.append("mender-test-" + std::to_string(std::random_device()()));
317✔
46
        fs::create_directories(path);
317✔
47
        path_ = path;
317✔
48
}
317✔
49

50
TemporaryDirectory::~TemporaryDirectory() {
951✔
51
        fs::remove_all(path_);
317✔
52
}
317✔
53

54
std::string TemporaryDirectory::Path() const {
935✔
55
        return path_;
935✔
56
}
57

58
::testing::AssertionResult FileContains(const string &filename, const string &expected_content) {
98✔
59
        ifstream is {filename};
196✔
60
        ostringstream contents_s;
196✔
61
        contents_s << is.rdbuf();
98✔
62
        string contents {contents_s.str()};
196✔
63
        if (contents == expected_content) {
98✔
64
                return ::testing::AssertionSuccess();
98✔
65
        }
66
        return ::testing::AssertionFailure()
×
67
                   << "Expected: '" << expected_content << "' Got: '" << contents << "'";
×
68
}
69

70

71
::testing::AssertionResult FileJsonEquals(const string &filename, const string &expected_content) {
2✔
72
        ifstream is {filename};
4✔
73
        json::Json contents = json::Load(is).value();
4✔
74
        json::Json expected_contents = json::Load(expected_content).value();
6✔
75
        if (contents.Dump() == expected_contents.Dump()) {
2✔
76
                return ::testing::AssertionSuccess();
2✔
77
        }
78
        return ::testing::AssertionFailure()
×
79
                   << "Expected: '" << contents.Dump() << "' Got: '" << expected_contents.Dump() << "'";
×
80
}
81

82
::testing::AssertionResult FilesEqual(const string &filename1, const string &filename2) {
6✔
83
        processes::Process proc({"diff", "-u", filename1, filename2});
48✔
84
        auto err = proc.Run();
12✔
85
        if (err == error::NoError) {
6✔
86
                return ::testing::AssertionSuccess();
6✔
87
        }
88
        // Some extra information in case of failure.
89
        cout << "ls -l " << filename1 << " " << filename2 << endl;
×
90
        processes::Process listdir({"ls", "-l", filename1, filename2});
×
91
        listdir.Run();
×
92
        return ::testing::AssertionFailure() << filename1 << " and " << filename2 << " differ";
×
93
}
94

95
const string HttpFileServer::serve_address_ {"http://127.0.0.1:53272"};
96

97
HttpFileServer::HttpFileServer(const string &dir) :
33✔
98
        dir_(dir),
99
        server_(http::ServerConfig {}, loop_) {
33✔
100
        // The reason we need this synchronization is because of the thread sanitizer and
101
        // logging. AsyncServeUrl uses the logger internally, and the log level is also set by
102
        // certain tests. Since these things happen in two separate threads, we need to make sure
103
        // that AsyncServeUrl has returned before we leave this function.
104
        promise<bool> running;
66✔
105
        auto maybe_running = running.get_future();
66✔
106

107
        thread_ = thread([this, &running]() {
33✔
108
                auto err = server_.AsyncServeUrl(
109
                        serve_address_,
110
                        [](http::ExpectedIncomingRequestPtr exp_req) {
20✔
111
                                if (!exp_req) {
20✔
112
                                        log::Warning("HttpFileServer: " + exp_req.error().String());
×
113
                                }
114
                        },
20✔
115
                        [this](http::ExpectedIncomingRequestPtr exp_req) { Serve(exp_req); });
86✔
116
                if (err != error::NoError) {
33✔
117
                        log::Error("HttpFileServer: " + err.String());
×
118
                        return;
×
119
                }
120

121
                running.set_value(true);
33✔
122
                loop_.Run();
33✔
123
        });
33✔
124

125
        maybe_running.wait();
33✔
126
}
33✔
127

128
HttpFileServer::~HttpFileServer() {
1✔
129
        loop_.Stop();
1✔
130
        thread_.join();
1✔
131
}
1✔
132

133
void HttpFileServer::Serve(http::ExpectedIncomingRequestPtr exp_req) {
20✔
134
        if (!exp_req) {
20✔
135
                log::Warning("HttpFileServer: " + exp_req.error().String());
×
136
                return;
×
137
        }
138

139
        auto req = exp_req.value();
20✔
140

141
        if (req->GetMethod() != http::Method::GET) {
20✔
142
                log::Warning(
×
143
                        "HttpFileServer: Expected HTTP GET method, but got "
144
                        + http::MethodToString(req->GetMethod()));
×
145
                return;
×
146
        }
147

148
        auto exp_resp = req->MakeResponse();
20✔
149
        if (!exp_resp) {
20✔
150
                log::Warning("HttpFileServer: " + exp_resp.error().String());
×
151
                return;
×
152
        }
153
        auto resp = exp_resp.value();
20✔
154

155
        auto path = req->GetPath();
20✔
156
        while (path.size() > 0 && path[0] == '/') {
40✔
157
                path = string {path.begin() + 1, path.end()};
20✔
158
        }
159

160
        string file_path = path::Join(dir_, path);
20✔
161

162
        auto exp_stream = io::OpenIfstream(file_path);
20✔
163
        if (!exp_stream) {
20✔
164
                resp->SetStatusCodeAndMessage(http::StatusNotFound, exp_stream.error().String());
×
165
                resp->SetHeader("Content-Length", "0");
×
166
                resp->SetBodyReader(make_shared<io::StringReader>(""));
×
167
        } else {
168
                auto exp_size = io::FileSize(file_path);
20✔
169
                if (!exp_size) {
20✔
170
                        log::Warning("HttpFileServer: " + exp_size.error().String());
×
171
                        resp->SetStatusCodeAndMessage(
×
172
                                http::StatusInternalServerError, exp_size.error().String());
×
173
                        resp->SetHeader("Content-Length", "0");
×
174
                        return;
×
175
                }
176

177
                resp->SetStatusCodeAndMessage(http::StatusOK, "");
20✔
178
                resp->SetBodyReader(
40✔
179
                        make_shared<io::StreamReader>((make_shared<ifstream>(std::move(exp_stream.value())))));
40✔
180

181
                resp->SetHeader("Content-Length", to_string(exp_size.value()));
20✔
182
        }
183

184
        auto err = resp->AsyncReply([](error::Error err) {
20✔
185
                if (err != error::NoError) {
20✔
186
                        log::Warning("HttpFileServer: " + err.String());
×
187
                }
188
        });
60✔
189
        if (err != error::NoError) {
20✔
190
                log::Warning("HttpFileServer: " + err.String());
×
191
        }
192
}
193

194
} // namespace testing
195
} // namespace common
196
} // namespace mender
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

© 2025 Coveralls, Inc