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

mendersoftware / mender / 968408678

pending completion
968408678

push

gitlab-ci

oleorhagen
feat(artifact/scripts): Add support for executing state scripts

Ticket: MEN-6636
Changelog: None

Signed-off-by: Ole Petter <ole.orhagen@northern.tech>

126 of 126 new or added lines in 4 files covered. (100.0%)

5405 of 6857 relevant lines covered (78.82%)

195.75 hits per line

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

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

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

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

60
void TemporaryDirectory::CreateSubDirectory(const string &dirname) {
8✔
61
        fs::path sub_path {path_};
16✔
62
        sub_path.append(dirname);
8✔
63
        if (!fs::create_directory(sub_path)) {
8✔
64
                throw runtime_error("Failed to create the temporary subdirectory: " + dirname);
×
65
        }
66
}
8✔
67

68
::testing::AssertionResult FileContains(const string &filename, const string &expected_content) {
121✔
69
        ifstream is {filename};
242✔
70
        ostringstream contents_s;
242✔
71
        contents_s << is.rdbuf();
121✔
72
        string contents {contents_s.str()};
242✔
73
        if (contents == expected_content) {
121✔
74
                return ::testing::AssertionSuccess();
121✔
75
        }
76
        return ::testing::AssertionFailure()
×
77
                   << "Expected: '" << expected_content << "' Got: '" << contents << "'";
×
78
}
79

80

81
::testing::AssertionResult FileJsonEquals(const string &filename, const string &expected_content) {
2✔
82
        ifstream is {filename};
4✔
83
        json::Json contents = json::Load(is).value();
4✔
84
        json::Json expected_contents = json::Load(expected_content).value();
6✔
85
        if (contents.Dump() == expected_contents.Dump()) {
2✔
86
                return ::testing::AssertionSuccess();
2✔
87
        }
88
        return ::testing::AssertionFailure()
×
89
                   << "Expected: '" << contents.Dump() << "' Got: '" << expected_contents.Dump() << "'";
×
90
}
91

92
::testing::AssertionResult FilesEqual(const string &filename1, const string &filename2) {
6✔
93
        processes::Process proc({"diff", "-u", filename1, filename2});
48✔
94
        auto err = proc.Run();
12✔
95
        if (err == error::NoError) {
6✔
96
                return ::testing::AssertionSuccess();
6✔
97
        }
98
        // Some extra information in case of failure.
99
        cout << "ls -l " << filename1 << " " << filename2 << endl;
×
100
        processes::Process listdir({"ls", "-l", filename1, filename2});
×
101
        listdir.Run();
×
102
        return ::testing::AssertionFailure() << filename1 << " and " << filename2 << " differ";
×
103
}
104

105
const string HttpFileServer::serve_address_ {"http://127.0.0.1:53272"};
106

107
HttpFileServer::HttpFileServer(const string &dir) :
44✔
108
        dir_ {dir},
109
        server_(http::ServerConfig {}, loop_) {
44✔
110
        // The reason we need this synchronization is because of the thread sanitizer and
111
        // logging. AsyncServeUrl uses the logger internally, and the log level is also set by
112
        // certain tests. Since these things happen in two separate threads, we need to make sure
113
        // that AsyncServeUrl has returned before we leave this function.
114
        promise<bool> running;
88✔
115
        auto maybe_running = running.get_future();
88✔
116

117
        thread_ = thread([this, &running]() {
44✔
118
                auto err = server_.AsyncServeUrl(
119
                        serve_address_,
120
                        [](http::ExpectedIncomingRequestPtr exp_req) {
28✔
121
                                if (!exp_req) {
28✔
122
                                        log::Warning("HttpFileServer: " + exp_req.error().String());
×
123
                                }
124
                        },
28✔
125
                        [this](http::ExpectedIncomingRequestPtr exp_req) { Serve(exp_req); });
116✔
126
                if (err != error::NoError) {
44✔
127
                        log::Error("HttpFileServer: " + err.String());
×
128
                        return;
×
129
                }
130

131
                running.set_value(true);
44✔
132
                loop_.Run();
44✔
133
        });
44✔
134

135
        maybe_running.wait();
44✔
136
}
44✔
137

138
HttpFileServer::~HttpFileServer() {
44✔
139
        loop_.Stop();
44✔
140
        thread_.join();
44✔
141
}
44✔
142

143
void HttpFileServer::Serve(http::ExpectedIncomingRequestPtr exp_req) {
28✔
144
        if (!exp_req) {
28✔
145
                log::Warning("HttpFileServer: " + exp_req.error().String());
×
146
                return;
×
147
        }
148

149
        auto req = exp_req.value();
28✔
150

151
        if (req->GetMethod() != http::Method::GET) {
28✔
152
                log::Warning(
×
153
                        "HttpFileServer: Expected HTTP GET method, but got "
154
                        + http::MethodToString(req->GetMethod()));
×
155
                return;
×
156
        }
157

158
        auto exp_resp = req->MakeResponse();
28✔
159
        if (!exp_resp) {
28✔
160
                log::Warning("HttpFileServer: " + exp_resp.error().String());
×
161
                return;
×
162
        }
163
        auto resp = exp_resp.value();
28✔
164

165
        auto path = req->GetPath();
28✔
166
        while (path.size() > 0 && path[0] == '/') {
56✔
167
                path = string {path.begin() + 1, path.end()};
28✔
168
        }
169

170
        string file_path = path::Join(dir_, path);
28✔
171

172
        auto exp_stream = io::OpenIfstream(file_path);
28✔
173
        if (!exp_stream) {
28✔
174
                resp->SetStatusCodeAndMessage(http::StatusNotFound, exp_stream.error().String());
1✔
175
                resp->SetHeader("Content-Length", "0");
1✔
176
                resp->SetBodyReader(make_shared<io::StringReader>(""));
1✔
177
        } else {
178
                auto exp_size = io::FileSize(file_path);
27✔
179
                if (!exp_size) {
27✔
180
                        log::Warning("HttpFileServer: " + exp_size.error().String());
×
181
                        resp->SetStatusCodeAndMessage(
×
182
                                http::StatusInternalServerError, exp_size.error().String());
×
183
                        resp->SetHeader("Content-Length", "0");
×
184
                        return;
×
185
                }
186

187
                resp->SetStatusCodeAndMessage(http::StatusOK, "");
27✔
188
                resp->SetBodyReader(
54✔
189
                        make_shared<io::StreamReader>((make_shared<ifstream>(std::move(exp_stream.value())))));
54✔
190

191
                resp->SetHeader("Content-Length", to_string(exp_size.value()));
27✔
192
        }
193

194
        auto err = resp->AsyncReply([](error::Error err) {
28✔
195
                if (err != error::NoError) {
28✔
196
                        log::Warning("HttpFileServer: " + err.String());
×
197
                }
198
        });
84✔
199
        if (err != error::NoError) {
28✔
200
                log::Warning("HttpFileServer: " + err.String());
×
201
        }
202
}
203

204
} // namespace testing
205
} // namespace common
206
} // 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