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

mendersoftware / mender / 992981781

05 Sep 2023 08:55AM UTC coverage: 79.541% (+0.3%) from 79.264%
992981781

push

gitlab-ci

lluiscampos
chore: Clean-up `gmock` use in tests

Many tests had the header and include path while only using GTest.

Completely unnecessary clean-up...

Signed-off-by: Lluis Campos <lluis.campos@northern.tech>

5719 of 7190 relevant lines covered (79.54%)

290.46 hits per line

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

84.17
/common/io/io.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/io.hpp>
16

17
#include <config.h>
18

19
#include <cerrno>
20
#include <cstdint>
21
#include <cstring>
22
#include <istream>
23
#include <memory>
24
#include <streambuf>
25
#include <vector>
26
#include <fstream>
27

28
namespace mender {
29
namespace common {
30
namespace io {
31

32
namespace error = mender::common::error;
33
namespace expected = mender::common::expected;
34

35
void AsyncReader::RepeatedAsyncRead(
4✔
36
        vector<uint8_t>::iterator start,
37
        vector<uint8_t>::iterator end,
38
        RepeatedAsyncIoHandler handler) {
39
        class Functor {
40
        public:
41
                AsyncReader &reader;
42
                vector<uint8_t>::iterator start;
43
                vector<uint8_t>::iterator end;
44
                RepeatedAsyncIoHandler handler;
45
                void ScheduleNextRead(Repeat repeat) {
4,227✔
46
                        while (repeat == Repeat::Yes) {
4,227✔
47
                                auto err = reader.AsyncRead(start, end, *this);
8,450✔
48
                                if (err == error::NoError) {
4,225✔
49
                                        break;
4,225✔
50
                                } else {
51
                                        repeat = handler(expected::unexpected(err));
×
52
                                }
53
                        }
54
                }
4,227✔
55
                void operator()(ExpectedSize num_read) {
4,223✔
56
                        auto repeat = handler(num_read);
4,223✔
57
                        ScheduleNextRead(repeat);
4,223✔
58
                }
4,223✔
59
        };
60
        Functor func {*this, start, end, handler};
8✔
61
        func.ScheduleNextRead(Repeat::Yes);
4✔
62
}
4✔
63

64
Error Copy(Writer &dst, Reader &src) {
168✔
65
        vector<uint8_t> buffer(MENDER_BUFSIZE);
336✔
66
        return Copy(dst, src, buffer);
336✔
67
}
68

69
Error Copy(Writer &dst, Reader &src, vector<uint8_t> &buffer) {
814✔
70
        while (true) {
71
                auto r_result = src.Read(buffer.begin(), buffer.end());
814✔
72
                if (!r_result) {
814✔
73
                        return r_result.error();
3✔
74
                } else if (r_result.value() == 0) {
811✔
75
                        return NoError;
162✔
76
                } else if (r_result.value() > buffer.size()) {
649✔
77
                        return error::MakeError(
78
                                error::ProgrammingError,
79
                                "Read returned more bytes than requested. This is a bug in the Read function.");
×
80
                }
81

82
                auto w_result = dst.Write(buffer.cbegin(), buffer.cbegin() + r_result.value());
649✔
83
                if (!w_result) {
649✔
84
                        return w_result.error();
1✔
85
                } else if (w_result.value() == 0) {
648✔
86
                        // Should this even happen?
87
                        return Error(std::error_condition(std::errc::io_error), "Zero write when copying data");
2✔
88
                } else if (r_result.value() != w_result.value()) {
647✔
89
                        return Error(
90
                                std::error_condition(std::errc::io_error), "Short write when copying data");
2✔
91
                }
92
        }
646✔
93
}
94

95
void ByteWriter::SetUnlimited(bool enabled) {
149✔
96
        unlimited_ = enabled;
149✔
97
}
149✔
98

99
ExpectedSize ByteWriter::Write(
4,843✔
100
        vector<uint8_t>::const_iterator start, vector<uint8_t>::const_iterator end) {
101
        assert(end > start);
4,843✔
102
        Vsize max_write {receiver_->size() - bytes_written_};
4,843✔
103
        if (max_write == 0 && !unlimited_) {
4,843✔
104
                return expected::unexpected(Error(make_error_condition(errc::no_space_on_device), ""));
×
105
        }
106
        Vsize iterator_size {static_cast<Vsize>(end - start)};
4,843✔
107
        Vsize bytes_to_write;
108
        if (unlimited_) {
4,843✔
109
                bytes_to_write = iterator_size;
4,810✔
110
                if (max_write < bytes_to_write) {
4,810✔
111
                        receiver_->resize(bytes_written_ + bytes_to_write);
4,810✔
112
                        max_write = bytes_to_write;
4,810✔
113
                }
114
        } else {
115
                bytes_to_write = min(iterator_size, max_write);
33✔
116
        }
117
        auto it = next(receiver_->begin(), bytes_written_);
4,843✔
118
        std::copy_n(start, bytes_to_write, it);
4,843✔
119
        bytes_written_ += bytes_to_write;
4,843✔
120
        return bytes_to_write;
4,843✔
121
}
122

123

124
ExpectedSize StreamWriter::Write(
12✔
125
        vector<uint8_t>::const_iterator start, vector<uint8_t>::const_iterator end) {
126
        os_->write(reinterpret_cast<const char *>(&*start), end - start);
12✔
127
        if (!(*(os_.get()))) {
12✔
128
                return expected::unexpected(Error(make_error_condition(errc::io_error), ""));
×
129
        }
130
        return end - start;
24✔
131
}
132

133
class ReaderStreamBuffer : public streambuf {
134
public:
135
        ReaderStreamBuffer(Reader &reader) :
394✔
136
                reader_ {reader},
137
                buf_(buf_size_) {};
788✔
138
        streambuf::int_type underflow() override;
139

140
private:
141
        static const Vsize buf_size_ = MENDER_BUFSIZE;
142
        Reader &reader_;
143
        vector<uint8_t> buf_;
144
};
145

146
streambuf::int_type ReaderStreamBuffer::underflow() {
735✔
147
        // eback -- pointer to the first char (byte)
148
        // gptr  -- pointer to the current char (byte)
149
        // egptr -- pointer past the last char (byte)
150

151
        // This function is only called if gptr() == nullptr or gptr() >= egptr(),
152
        // i.e. if there's nothing more to read.
153
        if (this->gptr() >= this->egptr()) {
735✔
154
                errno = 0;
735✔
155
                auto ex_n_read = reader_.Read(buf_.begin(), buf_.end());
1,470✔
156
                streamsize n_read;
157
                if (ex_n_read) {
735✔
158
                        n_read = ex_n_read.value();
735✔
159
                } else {
160
                        // There is no way to return an error from underflow(), generally
161
                        // the streams only care about how much data was read. No data or
162
                        // less data then requested by the caller of istream.read() means
163
                        // eofbit and failbit are set. If the user code wants to get the
164
                        // error or check if there was an error, it needs to check errno.
165
                        //
166
                        // So as long as we don't clear errno after a failure in the
167
                        // reader_.Read() above, error handling works as usual and returning
168
                        // eof below is all that needs to happen here.
169
                        //
170
                        // In case errno is not set for some reason, let's try to get it
171
                        // from the error with a fallback to a generic I/O error.
172
                        if (errno == 0) {
×
173
                                if (ex_n_read.error().code.category() == generic_category()) {
×
174
                                        errno = ex_n_read.error().code.value();
×
175
                                } else {
176
                                        errno = EIO;
×
177
                                }
178
                        }
179
                        n_read = 0;
×
180
                }
181

182
                streambuf::char_type *first = reinterpret_cast<streambuf::char_type *>(buf_.data());
735✔
183

184
                // set eback, gptr, egptr
185
                this->setg(first, first, first + n_read);
735✔
186
        }
187

188
        return this->gptr() == this->egptr() ? std::char_traits<char>::eof()
735✔
189
                                                                                 : std::char_traits<char>::to_int_type(*this->gptr());
735✔
190
};
191

192
/**
193
 * A variant of the #istream class that takes ownership of the #streambuf buffer
194
 * created for it.
195
 *
196
 * @note Base #istream is designed to work on shared buffers so it doesn't
197
 *       destruct/delete the buffer.
198
 */
199
class istreamWithUniqueBuffer : public istream {
200
public:
201
        // The unique_ptr, &&buf and std::move() model this really nicely -- a
202
        // unique_ptr rvalue (i.e. temporary) is required and it's moved into the
203
        // object. The default destructor then takes care of cleaning up properly.
204
        istreamWithUniqueBuffer(unique_ptr<streambuf> &&buf) :
394✔
205
                istream(buf.get()),
206
                buf_ {std::move(buf)} {};
394✔
207

208
private:
209
        unique_ptr<streambuf> buf_;
210
};
211

212
unique_ptr<istream> Reader::GetStream() {
394✔
213
        return unique_ptr<istream>(
214
                new istreamWithUniqueBuffer(unique_ptr<ReaderStreamBuffer>(new ReaderStreamBuffer(*this))));
394✔
215
};
216

217
ExpectedIfstream OpenIfstream(const string &path) {
201✔
218
        ifstream is;
402✔
219
        errno = 0;
201✔
220
        is.open(path);
201✔
221
        if (!is) {
201✔
222
                int io_errno = errno;
7✔
223
                return ExpectedIfstream(expected::unexpected(error::Error(
7✔
224
                        generic_category().default_error_condition(io_errno),
7✔
225
                        "Failed to open '" + path + "' for reading")));
21✔
226
        }
227
        return ExpectedIfstream(std::move(is));
194✔
228
}
229

230
ExpectedSharedIfstream OpenSharedIfstream(const string &path) {
7✔
231
        auto exp_is = OpenIfstream(path);
14✔
232
        if (!exp_is) {
7✔
233
                return expected::unexpected(exp_is.error());
×
234
        }
235
        return make_shared<ifstream>(std::move(exp_is.value()));
14✔
236
}
237

238
ExpectedOfstream OpenOfstream(const string &path, bool append) {
737✔
239
        ofstream os;
1,474✔
240
        errno = 0;
737✔
241
        os.open(path, append ? ios::app : ios::out);
737✔
242
        if (!os) {
737✔
243
                int io_errno = errno;
1✔
244
                return ExpectedOfstream(expected::unexpected(error::Error(
1✔
245
                        generic_category().default_error_condition(io_errno),
1✔
246
                        "Failed to open '" + path + "' for writing")));
3✔
247
        }
248
        return os;
736✔
249
}
250

251
ExpectedSharedOfstream OpenSharedOfstream(const string &path, bool append) {
×
252
        auto exp_is = OpenOfstream(path, append);
×
253
        if (!exp_is) {
×
254
                return expected::unexpected(exp_is.error());
×
255
        }
256
        return make_shared<ofstream>(std::move(exp_is.value()));
×
257
}
258

259
error::Error WriteStringIntoOfstream(ofstream &os, const string &data) {
522✔
260
        errno = 0;
522✔
261
        os.write(data.data(), data.size());
522✔
262
        if (os.bad() || os.fail()) {
522✔
263
                int io_errno = errno;
1✔
264
                return error::Error(
265
                        std::generic_category().default_error_condition(io_errno),
1✔
266
                        "Failed to write data into the stream");
3✔
267
        }
268

269
        return error::NoError;
521✔
270
}
271

272
ExpectedSize StreamReader::Read(vector<uint8_t>::iterator start, vector<uint8_t>::iterator end) {
1,453✔
273
        is_->read(reinterpret_cast<char *>(&*start), end - start);
1,453✔
274
        if (!is_) {
1,453✔
275
                int io_error = errno;
×
276
                return expected::unexpected(
×
277
                        Error(std::generic_category().default_error_condition(io_error), ""));
×
278
        }
279
        return is_->gcount();
2,906✔
280
}
281

282
error::Error FileReader::Rewind() {
4✔
283
        if (!is_) {
4✔
284
                auto ex_is = OpenSharedIfstream(path_);
3✔
285
                if (!ex_is) {
3✔
286
                        return ex_is.error();
×
287
                }
288
                is_ = ex_is.value();
3✔
289
        }
290
        if (!(*is_)) {
4✔
291
                return Error(std::error_condition(std::errc::io_error), "Bad stream, cannot rewind");
×
292
        }
293
        errno = 0;
4✔
294
        is_->seekg(0, ios::beg);
4✔
295
        int io_errno = errno;
4✔
296
        if (!(*is_)) {
4✔
297
                return Error(
298
                        generic_category().default_error_condition(io_errno),
×
299
                        "Failed to seek to the beginning of the stream");
×
300
        }
301
        return error::NoError;
4✔
302
}
303

304
} // namespace io
305
} // namespace common
306
} // 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