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

mendersoftware / mender / 951615729

pending completion
951615729

push

gitlab-ci

larsewi
fix: Add 'build/' to '.gitignore'

Ticket: None
Changelog: None
Signed-off-by: Lars Erik Wik <lars.erik.wik@northern.tech>

4199 of 5286 relevant lines covered (79.44%)

166.43 hits per line

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

76.34
/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 <filesystem>
18
#include <random>
19
#include <iostream>
20

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

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

30
namespace fs = std::filesystem;
31

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

35
TemporaryDirectory::TemporaryDirectory() {
216✔
36
        fs::path path = fs::temp_directory_path();
216✔
37
        path.append("mender-test-" + std::to_string(std::random_device()()));
216✔
38
        fs::create_directories(path);
216✔
39
        path_ = path;
216✔
40
}
216✔
41

42
TemporaryDirectory::~TemporaryDirectory() {
648✔
43
        fs::remove_all(path_);
216✔
44
}
216✔
45

46
std::string TemporaryDirectory::Path() {
607✔
47
        return path_;
607✔
48
}
49

50
::testing::AssertionResult FileContains(const string &filename, const string &expected_content) {
34✔
51
        ifstream is {filename};
68✔
52
        ostringstream contents_s;
68✔
53
        contents_s << is.rdbuf();
34✔
54
        string contents {contents_s.str()};
68✔
55
        if (contents == expected_content) {
34✔
56
                return ::testing::AssertionSuccess();
34✔
57
        }
58
        return ::testing::AssertionFailure()
×
59
                   << "Expected: '" << expected_content << "' Got: '" << contents << "'";
×
60
}
61

62

63
::testing::AssertionResult FileJsonEquals(const string &filename, const string &expected_content) {
2✔
64
        ifstream is {filename};
4✔
65
        json::Json contents = json::Load(is).value();
4✔
66
        json::Json expected_contents = json::Load(expected_content).value();
6✔
67
        if (contents.Dump() == expected_contents.Dump()) {
2✔
68
                return ::testing::AssertionSuccess();
2✔
69
        }
70
        return ::testing::AssertionFailure()
×
71
                   << "Expected: '" << contents.Dump() << "' Got: '" << expected_contents.Dump() << "'";
×
72
}
73

74
::testing::AssertionResult FilesEqual(const string &filename1, const string &filename2) {
6✔
75
        processes::Process proc({"diff", "-u", filename1, filename2});
48✔
76
        auto err = proc.Run();
12✔
77
        if (err == error::NoError) {
6✔
78
                return ::testing::AssertionSuccess();
6✔
79
        }
80
        // Some extra information in case of failure.
81
        cout << "ls -l " << filename1 << " " << filename2 << endl;
×
82
        processes::Process listdir({"ls", "-l", filename1, filename2});
×
83
        listdir.Run();
×
84
        return ::testing::AssertionFailure() << filename1 << " and " << filename2 << " differ";
×
85
}
86

87
const string HttpFileServer::serve_address_ {"http://127.0.0.1:53272"};
88

89
HttpFileServer::HttpFileServer(const string &dir) :
1✔
90
        dir_(dir),
91
        server_(http::ServerConfig {}, loop_) {
1✔
92
        // The reason we need this synchronization is because of the thread sanitizer and
93
        // logging. AsyncServeUrl uses the logger internally, and the log level is also set by
94
        // certain tests. Since these things happen in two separate threads, we need to make sure
95
        // that AsyncServeUrl has returned before we leave this function.
96
        promise<bool> running;
2✔
97
        auto maybe_running = running.get_future();
2✔
98

99
        thread_ = thread([this, &running]() {
1✔
100
                auto err = server_.AsyncServeUrl(
101
                        serve_address_,
102
                        [](http::ExpectedIncomingRequestPtr exp_req) {
1✔
103
                                if (!exp_req) {
1✔
104
                                        log::Warning("HttpFileServer: " + exp_req.error().String());
×
105
                                }
106
                        },
1✔
107
                        [this](http::ExpectedIncomingRequestPtr exp_req) { Serve(exp_req); });
3✔
108
                if (err != error::NoError) {
1✔
109
                        log::Error("HttpFileServer: " + err.String());
×
110
                        return;
×
111
                }
112

113
                running.set_value(true);
1✔
114
                loop_.Run();
1✔
115
        });
1✔
116

117
        maybe_running.wait();
1✔
118
}
1✔
119

120
HttpFileServer::~HttpFileServer() {
1✔
121
        loop_.Stop();
1✔
122
        thread_.join();
1✔
123
}
1✔
124

125
void HttpFileServer::Serve(http::ExpectedIncomingRequestPtr exp_req) {
1✔
126
        if (!exp_req) {
1✔
127
                log::Warning("HttpFileServer: " + exp_req.error().String());
×
128
                return;
×
129
        }
130

131
        auto req = exp_req.value();
1✔
132

133
        if (req->GetMethod() != http::Method::GET) {
1✔
134
                log::Warning(
×
135
                        "HttpFileServer: Expected HTTP GET method, but got "
136
                        + http::MethodToString(req->GetMethod()));
×
137
                return;
×
138
        }
139

140
        auto exp_resp = req->MakeResponse();
1✔
141
        if (!exp_resp) {
1✔
142
                log::Warning("HttpFileServer: " + exp_resp.error().String());
×
143
                return;
×
144
        }
145
        auto resp = exp_resp.value();
2✔
146

147
        auto path = req->GetPath();
2✔
148
        while (path.size() > 0 && path[0] == '/') {
2✔
149
                path = string {path.begin() + 1, path.end()};
1✔
150
        }
151

152
        string file_path = path::Join(dir_, path);
2✔
153

154
        auto exp_stream = io::OpenIfstream(file_path);
2✔
155
        if (!exp_stream) {
1✔
156
                resp->SetStatusCodeAndMessage(http::StatusNotFound, exp_stream.error().String());
×
157
        } else {
158
                resp->SetStatusCodeAndMessage(http::StatusOK, "");
1✔
159
                resp->SetBodyReader(
2✔
160
                        make_shared<io::StreamReader>((make_shared<ifstream>(std::move(exp_stream.value())))));
2✔
161
        }
162

163
        auto exp_size = io::FileSize(file_path);
2✔
164
        if (!exp_size) {
1✔
165
                log::Warning("HttpFileServer: " + exp_size.error().String());
×
166
        }
167
        resp->SetHeader("Content-Length", to_string(exp_size.value()));
1✔
168

169
        auto err = resp->AsyncReply([](error::Error err) {
1✔
170
                if (err != error::NoError) {
1✔
171
                        log::Warning("HttpFileServer: " + err.String());
×
172
                }
173
        });
3✔
174
        if (err != error::NoError) {
1✔
175
                log::Warning("HttpFileServer: " + err.String());
×
176
        }
177
}
178

179
} // namespace testing
180
} // namespace common
181
} // 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