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

mendersoftware / mender / 974575668

21 Aug 2023 12:04PM UTC coverage: 78.829% (-0.05%) from 78.877%
974575668

push

gitlab-ci

kacf
chore: Implement pushing of logs to the server.

Ticket: MEN-6581

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

18 of 18 new or added lines in 2 files covered. (100.0%)

5492 of 6967 relevant lines covered (78.83%)

238.75 hits per line

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

72.22
/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() {
357✔
44
        fs::path path = fs::temp_directory_path();
357✔
45
        path.append("mender-test-" + std::to_string(std::random_device()()));
357✔
46
        if (!fs::create_directories(path)) {
357✔
47
                throw runtime_error("Failed to create the temporary directory: " + string(path));
×
48
        }
49
        path_ = path;
357✔
50
}
357✔
51

52
TemporaryDirectory::~TemporaryDirectory() {
1,071✔
53
        fs::remove_all(path_);
357✔
54
}
357✔
55

56
std::string TemporaryDirectory::Path() const {
1,190✔
57
        return path_;
1,190✔
58
}
59

60
void TemporaryDirectory::CreateSubDirectory(const string &dirname) {
8✔
61
        fs::path sub_path {path_};
8✔
62
        sub_path.append(dirname);
8✔
63
        ASSERT_TRUE(fs::create_directory(sub_path));
8✔
64
}
65

66
::testing::AssertionResult FileContains(const string &filename, const string &expected_content) {
7✔
67
        ifstream is {filename};
14✔
68
        if (!is) {
7✔
69
                auto errnum {errno};
1✔
70
                return ::testing::AssertionFailure()
2✔
71
                           << "Cannot open `" << filename
1✔
72
                           << "`: " << generic_category().default_error_condition(errnum).message();
2✔
73
        }
74
        ostringstream contents_s;
12✔
75
        contents_s << is.rdbuf();
6✔
76
        string contents {contents_s.str()};
12✔
77
        if (contents.find(expected_content) != contents.npos) {
6✔
78
                return ::testing::AssertionSuccess();
6✔
79
        }
80
        return ::testing::AssertionFailure()
×
81
                   << "'" << contents << "' does not contain '" << expected_content << "'";
×
82
}
83

84
::testing::AssertionResult FileContainsExactly(
123✔
85
        const string &filename, const string &expected_content) {
86
        ifstream is {filename};
246✔
87
        if (!is) {
123✔
88
                auto errnum {errno};
×
89
                return ::testing::AssertionFailure()
×
90
                           << "Cannot open `" << filename
×
91
                           << "`: " << generic_category().default_error_condition(errnum).message();
×
92
        }
93
        ostringstream contents_s;
246✔
94
        contents_s << is.rdbuf();
123✔
95
        string contents {contents_s.str()};
246✔
96
        if (contents == expected_content) {
123✔
97
                return ::testing::AssertionSuccess();
123✔
98
        }
99
        return ::testing::AssertionFailure()
×
100
                   << "Expected: '" << expected_content << "' Got: '" << contents << "'";
×
101
}
102

103

104
::testing::AssertionResult FileJsonEquals(const string &filename, const string &expected_content) {
2✔
105
        ifstream is {filename};
4✔
106
        json::Json contents = json::Load(is).value();
4✔
107
        json::Json expected_contents = json::Load(expected_content).value();
6✔
108
        if (contents.Dump() == expected_contents.Dump()) {
2✔
109
                return ::testing::AssertionSuccess();
2✔
110
        }
111
        return ::testing::AssertionFailure()
×
112
                   << "Expected: '" << contents.Dump() << "' Got: '" << expected_contents.Dump() << "'";
×
113
}
114

115
::testing::AssertionResult FilesEqual(const string &filename1, const string &filename2) {
6✔
116
        processes::Process proc({"diff", "-u", filename1, filename2});
48✔
117
        auto err = proc.Run();
12✔
118
        if (err == error::NoError) {
6✔
119
                return ::testing::AssertionSuccess();
6✔
120
        }
121
        // Some extra information in case of failure.
122
        cout << "ls -l " << filename1 << " " << filename2 << endl;
×
123
        processes::Process listdir({"ls", "-l", filename1, filename2});
×
124
        listdir.Run();
×
125
        return ::testing::AssertionFailure() << filename1 << " and " << filename2 << " differ";
×
126
}
127

128
const string HttpFileServer::serve_address_ {"http://127.0.0.1:53272"};
129

130
HttpFileServer::HttpFileServer(const string &dir) :
46✔
131
        dir_ {dir},
132
        server_(http::ServerConfig {}, loop_) {
46✔
133
        // The reason we need this synchronization is because of the thread sanitizer and
134
        // logging. AsyncServeUrl uses the logger internally, and the log level is also set by
135
        // certain tests. Since these things happen in two separate threads, we need to make sure
136
        // that AsyncServeUrl has returned before we leave this function.
137
        promise<bool> running;
92✔
138
        auto maybe_running = running.get_future();
92✔
139

140
        thread_ = thread([this, &running]() {
46✔
141
                auto err = server_.AsyncServeUrl(
142
                        serve_address_,
143
                        [](http::ExpectedIncomingRequestPtr exp_req) {
31✔
144
                                if (!exp_req) {
31✔
145
                                        log::Warning("HttpFileServer: " + exp_req.error().String());
×
146
                                }
147
                        },
31✔
148
                        [this](http::ExpectedIncomingRequestPtr exp_req) { Serve(exp_req); });
123✔
149
                if (err != error::NoError) {
46✔
150
                        log::Error("HttpFileServer: " + err.String());
×
151
                        return;
×
152
                }
153

154
                running.set_value(true);
46✔
155
                loop_.Run();
46✔
156
        });
46✔
157

158
        maybe_running.wait();
46✔
159
}
46✔
160

161
HttpFileServer::~HttpFileServer() {
46✔
162
        loop_.Stop();
46✔
163
        thread_.join();
46✔
164
}
46✔
165

166
void HttpFileServer::Serve(http::ExpectedIncomingRequestPtr exp_req) {
31✔
167
        if (!exp_req) {
31✔
168
                log::Warning("HttpFileServer: " + exp_req.error().String());
×
169
                return;
×
170
        }
171

172
        auto req = exp_req.value();
31✔
173

174
        if (req->GetMethod() != http::Method::GET) {
31✔
175
                log::Warning(
×
176
                        "HttpFileServer: Expected HTTP GET method, but got "
177
                        + http::MethodToString(req->GetMethod()));
×
178
                return;
×
179
        }
180

181
        auto exp_resp = req->MakeResponse();
31✔
182
        if (!exp_resp) {
31✔
183
                log::Warning("HttpFileServer: " + exp_resp.error().String());
×
184
                return;
×
185
        }
186
        auto resp = exp_resp.value();
31✔
187

188
        auto path = req->GetPath();
31✔
189
        while (path.size() > 0 && path[0] == '/') {
62✔
190
                path = string {path.begin() + 1, path.end()};
31✔
191
        }
192

193
        string file_path = path::Join(dir_, path);
31✔
194

195
        auto exp_stream = io::OpenIfstream(file_path);
31✔
196
        if (!exp_stream) {
31✔
197
                resp->SetStatusCodeAndMessage(http::StatusNotFound, exp_stream.error().String());
1✔
198
                resp->SetHeader("Content-Length", "0");
1✔
199
                resp->SetBodyReader(make_shared<io::StringReader>(""));
1✔
200
        } else {
201
                auto exp_size = io::FileSize(file_path);
30✔
202
                if (!exp_size) {
30✔
203
                        log::Warning("HttpFileServer: " + exp_size.error().String());
×
204
                        resp->SetStatusCodeAndMessage(
×
205
                                http::StatusInternalServerError, exp_size.error().String());
×
206
                        resp->SetHeader("Content-Length", "0");
×
207
                        return;
×
208
                }
209

210
                resp->SetStatusCodeAndMessage(http::StatusOK, "");
30✔
211
                resp->SetBodyReader(
60✔
212
                        make_shared<io::StreamReader>((make_shared<ifstream>(std::move(exp_stream.value())))));
60✔
213

214
                resp->SetHeader("Content-Length", to_string(exp_size.value()));
30✔
215
        }
216

217
        auto err = resp->AsyncReply([](error::Error err) {
31✔
218
                if (err != error::NoError) {
31✔
219
                        log::Warning("HttpFileServer: " + err.String());
×
220
                }
221
        });
93✔
222
        if (err != error::NoError) {
31✔
223
                log::Warning("HttpFileServer: " + err.String());
×
224
        }
225
}
226

227
} // namespace testing
228
} // namespace common
229
} // 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