Following is the function which I want to unit test:
void sampleFunc()
{
FILE *file = fopen(path, "rb");
if (!file) {
std::cout << "File couldn't be opened!" << std::endl;
}
const int bufSize = 32768;
unsigned char *buffer = (unsigned char*) malloc(bufSize);
if (!buffer) {
fclose(file);
std::cout << "Failed to allocate buffer for SHA256 computation." << std::endl;
}
// ..
// Read file into buffer
// ..
}
As shown in the code, my function uses many standard library functions. So am I supposed to mock standard library functions or make actual calls to functions?
What you might do to test your function:
class IFileManager
{
public:
virtual ~IFileManager() = default;
// You could even improve interface by returning RAII object
virtual FILE* fopen(const char* path, const char* mode) = 0;
virtual void fclose(FILE*) = 0;
// ...
};
class FileManager : public IFileManager
{
public:
FILE* fopen(const char* path, const char* mode) override { return ::fopen(path, mode); }
int fclose(FILE* f) override { return ::fclose(f); }
// ...
};
class IAllocator
{
public:
virtual ~IAllocator() = default;
virtual void* allocate(std::size_t) = 0;
virtual void deallocate(void*) = 0;
};
class Allocator : public IAllocator
{
public:
void* allocate(std::size_t size) override { return malloc(size); }
void deallocate(void* p) override { free(p); }
};
Your function becomes:
void sampleFunc(IFileManager& fileManager, IAllocator& allocator)
{
FILE *file = fileManager.fopen(path, "rb");
if(!file) {
std::cout << "File couldn't be opened!" << endl;
return;
}
const int bufSize = 32768;
unsigned char *buffer = (unsigned char*) allocator.allocate(bufSize);
if(!buffer) {
fileManager.fclose(file);
std::cout << "Failed to allocate buffer for SHA256 computation." << std::endl;
return;
}
// Read file into buffer
// ...
}
Finally, you can easily mock IFileManager and IAllocator.
Without that interface, you would have to make standard function behave as you expect, which is not necessary simple feasible (wrong path, limit memory (which limit))
Care also that implementation might have more limitations. that the one from interface (MAX_PATH, non-regular files, UNC path)
Related
I have a module written in C. In which I have API defined as follow.
int ReadData(void* data, int data_size, char* filename, int offset)
When performing unit test in order to cover full coverage I need to hit at error condition for fseek and fread system call.
Can somebody explain me How can I override fseek/fread during particular test case so instead of system call it will call mock fseek/fread for that test case ?
ReadAPI.c
#include<stdio.h>
#include<stdlib.h>
int readdata(void* data,int size, int offset, char* filename)
{
if(data == NULL || size == 0 || filename == NULL)
{
return -1;
}
FILE* fp = fopen(filename,"rb");
if(fp == NULL)
{
return -1;
}
if(fseek(fp,offset,SEEK_SET) != 0)
{
fclose(fp);
return -1;
}
fread(data,size,1,fp);
if(ferror(fp))
{
fclose(fp);
return -1;
}
fclose(fp);
return 1;
}
readapi.h
#include<stdio.h>
#include<stdlib.h>
int readdata(void* data,int size, int offset, char* filename)
Test.cpp
#include<gtest/gtest.h>
#include"readapi.h"
TEST(test, Test1fseek)
{
// When fseek called in readdata API call mock fseek to hit fseek fail
// fseek(){return -1;}
char data[10] = {0};
int status = readdata(data,sizeof(data),0,"test.txt");
EXPECT_EQ(status, -1);
}
TEST(test, Test2fread)
{
//When fread called in readdata API call mock fread to hit read fail
// fread(){return -1;}
char data[10] = {0};
int status = readdata(data,sizeof(data),0,"test.txt");
EXPECT_EQ(status, -1);
}
int main()
{
return RUN_ALL_TEST();
}
You can refer to mocking-free-functions in Gmock's documentation.
Unfortunately, using the recommended method means you will have to change your code, which may not work if your code need to strictly be a C code.
However, if you are willing to accept this, based on the documentation, you will have to create a wrapper around all the system functions that you are using and then mock that wrapper.
Also, don't forget that you will have to add EXPECT_CALL or ON_CALL for all functions that are expected to be called and return a non-default (i.e. 0) value. For example, in your first test, you should also provide EXPECT_CALL or ON_CALL for fopen.
Here is an example implementation:
// System wrapper interface class.
class MySystemWrapperInterface {
public:
virtual FILE* fopen(const char* filename, const char* mode) = 0;
virtual int fseek(FILE* stream, long int offset, int whence) = 0;
virtual size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream) = 0;
virtual int ferror(FILE* stream) = 0;
virtual int fclose(FILE* stream) = 0;
};
// System wrapper actual class used in production code.
class MySystemWrapperClass : public MySystemWrapperInterface {
public:
FILE* fopen(const char* filename, const char* mode) {
return ::fopen(filename, mode);
}
int fseek(FILE* stream, long int offset, int whence) {
return ::fseek(stream, offset, whence);
}
size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream) {
return ::fread(ptr, size, nmemb, stream);
}
int ferror(FILE* stream) { return ::ferror(stream); }
int fclose(FILE* stream) { return ::fclose(stream); }
};
// Mocked System wrapper used for testint.
class MySystemWrapperMockClass : public MySystemWrapperInterface {
public:
MOCK_METHOD(FILE*, fopen, (const char*, const char*), (override));
MOCK_METHOD(int, fseek, (FILE*, long int, int), (override));
MOCK_METHOD(size_t, fread, (void*, size_t, size_t, FILE*), (override));
MOCK_METHOD(int, ferror, (FILE*), (override));
MOCK_METHOD(int, fclose, (FILE*), (override));
};
// Wrapper class for your own readdata function.
class MyClass {
// The system wrapper passed by dependency injection through constructor.
MySystemWrapperInterface* system_wrapper_;
public:
// Initialize the system wrapper in constructor.
MyClass(MySystemWrapperInterface* system_wrapper)
: system_wrapper_(system_wrapper) {}
int readdata(void* data, int size, int offset, char* filename) {
if (data == NULL || size == 0 || filename == NULL) {
return -1;
}
FILE* fp = system_wrapper_->fopen(filename, "rb");
if (fp == NULL) {
return -1;
}
if (system_wrapper_->fseek(fp, offset, SEEK_SET) != 0) {
system_wrapper_->fclose(fp);
return -1;
}
system_wrapper_->fread(data, size, 1, fp);
if (system_wrapper_->ferror(fp)) {
system_wrapper_->fclose(fp);
return -1;
}
system_wrapper_->fclose(fp);
return 1;
}
};
TEST(test, Test1fseek) {
// Create the mock object and inject it into your class.
MySystemWrapperMockClass mock_system_wrapper;
MyClass my_object(&mock_system_wrapper);
// When fseek called in readdata API call mock fseek to hit fseek fail
// fseek(){return -1;}
// IMPORTANT: Don't forget to add EXPECT_CALL or ON_CALL for all functions
// that are expected to be called.
EXPECT_CALL(mock_system_wrapper, fopen)
.Times(1)
.WillOnce(Return(reinterpret_cast<FILE*>(0x1)));
EXPECT_CALL(mock_system_wrapper, fseek).Times(1).WillOnce(Return(1));
EXPECT_CALL(mock_system_wrapper, fclose).Times(1).WillOnce(Return(1));
char data[10] = {0};
int status = my_object.readdata(data, sizeof(data), 0, "test.txt");
EXPECT_EQ(status, -1);
}
TEST(test, Test2fread) {
// Create the mock object and inject it into your class.
MySystemWrapperMockClass mock_system_wrapper;
MyClass my_object(&mock_system_wrapper);
// When fread called in readdata API call mock fread to hit read fail
// fread(){return -1;}
// IMPORTANT: Don't forget to add EXPECT_CALL or ON_CALL for all functions
// that are expected to be called.
EXPECT_CALL(mock_system_wrapper, fopen)
.Times(1)
.WillOnce(Return(reinterpret_cast<FILE*>(0x1)));
EXPECT_CALL(mock_system_wrapper, fseek).Times(1).WillOnce(Return(0));
EXPECT_CALL(mock_system_wrapper, fread).Times(1).WillOnce(Return(-1));
EXPECT_CALL(mock_system_wrapper, ferror).Times(1).WillOnce(Return(-1));
EXPECT_CALL(mock_system_wrapper, fclose).Times(1).WillOnce(Return(1));
char data[10] = {0};
int status = my_object.readdata(data, sizeof(data), 0, "test.txt");
EXPECT_EQ(status, -1);
}
Live example: https://godbolt.org/z/qxf74fWGh
If you are using gcc/g++ or clang/clang++ you can use the linker option --wrap=symbol to redirect calls to your own versions of these functions.
Your wrapper functions will have the name __wrap_symbol and the real functions will be named __real_symbol.
Example:
int __wrap_fseek(FILE *stream, long offset, int whence) {
printf("__wrap_fseek: %ld %d\n", offset, whence);
return __real_fseek(stream, offset, whence);
}
size_t __wrap_fread(void *restrict ptr, size_t size, size_t nmemb,
FILE *restrict stream)
{
return __real_fread(ptr, size, nmemb, stream);
}
Compile with g++ ... -Wl,--wrap=fseek,--wrap=fread
values on the output are duplicated
CODE:
enum class status_t
{
_success,
};
const char* set_status(status_t status)
{
switch(status)
{
case status_t::_success: return xorstr_("[SUCCESS]");
}
}
enum class action_t
{
_launch,
};
const char* set_action(action_t action)
{
switch (action)
{
case action_t::_launch: return xorstr_("[LAUNCH]");
}
}
void record(const char* status, const char* action, char* message)
{
const char* directory = "C:\\log.txt";
std::ofstream out(directory, std::ios_base::out | std::ios_base::app);
out << status << char(0x20) << action << char(0x20) << message << '\n';
out.close();
}
int main()
{
record(set_status(status_t::_success), set_action(action_t::_launch), "success");
}
output: [SUCCESS] [SUCCESS] success
what can cause such an unusual problem?
I do not know what stackoverflow wants me to write here, enough to understand the problem
I want to capture the libcurl error output that is normally sent to stderr into a std::string instead.
This is the code I have come up with so far:
#include <windows.h>
#include <streambuf>
#include <iostream>
#include <io.h>
#include <sstream>
#include <fstream>
#include <stdlib.h>
#include <stdio.h>
#include <fstream>
#include <string>
#include "curl\curl.h"
std::string data; //will hold the url's contents
// callback function for curl
size_t writeCallback(char* buf, size_t size, size_t nmemb, void* up)
{ //callback must have this declaration
//buf is a pointer to the data that curl has for us
//size*nmemb is the size of the buffer
for (int c = 0; c < size * nmemb; c++)
{
data.push_back(buf[c]);
}
return size * nmemb; //tell curl how many bytes we handled
}
int main()
{
CURL* curl; //our curl object
curl_global_init(CURL_GLOBAL_ALL); //pretty obvious
curl = curl_easy_init();
CURLcode res;
char errbuf[CURL_ERROR_SIZE];
curl_easy_setopt(curl, CURLOPT_URL, "http://ww.example.com/path");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCallback);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); //tell curl to output its progress
int old;
FILE* DataFile;
old = _dup(2); // redirect stderr to "old"
if (fopen_s(&DataFile, "data", "w") != 0)
{
puts("Can't open file 'Datafile'\n");
exit(1);
}
// stderr now refers to file "Datafile"
if (-1 == _dup2(_fileno(DataFile), 2))
{
perror("Can't _dup2 stderr");
exit(1);
}
res = curl_easy_perform(curl);
// Flush stderr stream buffer so it goes to correct file
fflush(stderr);
fclose(DataFile);
// Restore original stderr
_dup2(old, 2);
curl_easy_cleanup(curl);
curl_global_cleanup();
std::ifstream filetoread(DataFile);
if (filetoread)
{
// get length of file:
filetoread.seekg(0, filetoread.end);
int length = filetoread.tellg();
filetoread.seekg(0, filetoread.beg);
char* buffer = new (std::nothrow) char[length];
if (buffer == nullptr)
{
std::cout << "Error allocating buffer.\nvariable 'length' is: " << length << std::endl;
return 0;
}
std::cout << "Reading " << length << " characters... ";
// read data as a block:
filetoread.read(buffer, length);
if (filetoread)
std::cout << "all characters read successfully.";
else
std::cout << "error: only " << filetoread.gcount() << " could be read";
filetoread.close();
// ...buffer contains the entire file...
std::string buffertext(buffer);
delete[] buffer;
}
_flushall();
}
It seems to me that the file Datafile is empty since the variable length is -1. Can anybody help with this code? The examples in redirect stdout/stderr to a string haven't proved to be a solution to my question so far.
I'd use the FUNCTION/DATA pairs in a wrapper class to be able to create event handlers that are called automatically for the events you'd like to handle. Here's an example with comments in the code:
#include "curl/curl.h"
#include <iostream>
#include <memory>
#include <stdexcept>
#include <utility>
// A simple wrapper class for the C functions in libcurl
class EasyCurly {
public:
// default constructor
EasyCurly() : handle(curl_easy_init()) {
if(handle == nullptr) throw std::runtime_error("curl_easy_init failed");
// Set "this" as data pointer in callbacks to be able to make a call to the
// correct EasyCurly object. There are a lot more callback functions you
// could add here if you need them.
setopt(CURLOPT_WRITEDATA, this);
setopt(CURLOPT_DEBUGDATA, this);
setopt(CURLOPT_XFERINFODATA, this);
// Setup of proxy/callback functions. There should be one for each function
// above.
setopt(CURLOPT_WRITEFUNCTION, write_callback);
setopt(CURLOPT_DEBUGFUNCTION, debug_callback);
setopt(CURLOPT_XFERINFOFUNCTION, progress_callback);
// some default options, remove those you usually don't want
setopt(CURLOPT_NOPROGRESS, 0); // turn on progress callbacks
setopt(CURLOPT_FOLLOWLOCATION, 1L); // redirects
setopt(CURLOPT_HTTPPROXYTUNNEL, 1L); // corp. proxies etc.
// setopt(CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
setopt(CURLOPT_VERBOSE, 1L); // we want it all
}
// copy constructor
EasyCurly(const EasyCurly& other) : handle(curl_easy_duphandle(other.handle)) {
if(handle == nullptr) throw std::runtime_error("curl_easy_duphandle failed");
// State information is not shared when using curl_easy_duphandle. Only the
// options you've set (so you can create one CURL object, set its options and
// then use as a template for other objects. The document and debug data are
// therefor also not copied.
}
// move constructor
EasyCurly(EasyCurly&& other) :
handle(std::exchange(other.handle, nullptr)),
m_document(std::move(other.m_document)), m_debug(std::move(other.m_debug)) {}
// copy assignment
EasyCurly& operator=(const EasyCurly& other) {
CURL* tmp_handle = curl_easy_duphandle(other.handle);
if(handle == nullptr) throw std::runtime_error("curl_easy_duphandle failed");
// dup succeeded, now destroy any handle we might have and copy the tmp
curl_easy_cleanup(handle);
handle = tmp_handle;
return *this;
}
// move assignment
EasyCurly& operator=(EasyCurly&& other) {
std::swap(handle, other.handle);
std::swap(m_document, other.m_document);
std::swap(m_debug, other.m_debug);
return *this;
}
virtual ~EasyCurly() { curl_easy_cleanup(handle); }
// To be able to use an instance of EasyCurly with C interfaces if you don't add
// a function to this class for it, this operator will help
operator CURL*() { return handle; }
// generic curl_easy_setopt wrapper
template<typename T>
CURLcode setopt(CURLoption option, T v) {
return curl_easy_setopt(handle, option, v);
}
// perform by supplying url
CURLcode perform(std::string_view url) {
setopt(CURLOPT_URL, url.data());
return perform();
}
// perform with a previously supplied url
CURLcode perform() {
m_document.clear();
m_debug.clear();
return curl_easy_perform(handle);
}
// get collected data
std::string const& document() const { return m_document; }
std::string const& debug() const { return m_debug; }
// callbacks from proxy functions
virtual size_t on_write(char* ptr, size_t total_size) {
m_document.insert(m_document.end(), ptr, ptr + total_size);
// std::cout << "<write_callback> size " << total_size << "\n";
return total_size;
}
virtual int on_debug(curl_infotype /*type*/, char* data, size_t size) {
m_debug.insert(m_debug.end(), data, data + size);
// std::cout << "<debug>\n";
return 0; // must return 0
}
// override this to make a progress bar ...
virtual int on_progress(curl_off_t /*dltotal*/, curl_off_t /*dlnow*/,
curl_off_t /*ultotal*/, curl_off_t /*ulnow*/) {
// std::cout << "<progress>\n";
return 0;
}
private:
// a private class to initialize and cleanup once
class CurlGlobalInit {
public:
CurlGlobalInit() {
// std::cout << "CurlGlobalInit\n";
CURLcode res = curl_global_init(CURL_GLOBAL_ALL);
if(res) throw std::runtime_error("curl_global_init failed");
}
~CurlGlobalInit() {
// std::cout << "~CurlGlobalInit\n";
curl_global_cleanup();
}
};
// callback functions - has to be static to work with the C interface in curl
// use the data pointer (this) that we set in the constructor and cast it back
// to a EasyCurly* and call the event handler in the correct object.
static size_t write_callback(char* ptr, size_t size, size_t nmemb,
void* userdata) {
EasyCurly* ecurly = static_cast<EasyCurly*>(userdata);
return ecurly->on_write(ptr, nmemb * size); // size==1 really
}
static int debug_callback(CURL* /*handle*/, curl_infotype type, char* data,
size_t size, void* userptr) {
EasyCurly* ecurly = static_cast<EasyCurly*>(userptr);
return ecurly->on_debug(type, data, size);
}
static int progress_callback(void* clientp, curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow) {
EasyCurly* ecurly = static_cast<EasyCurly*>(clientp);
return ecurly->on_progress(dltotal, dlnow, ultotal, ulnow);
}
// resources
CURL* handle;
std::string m_document{};
std::string m_debug{};
// a static initializer object
static CurlGlobalInit setup_and_teardown;
};
// This must be defined in your .cpp file if you use this and split it into header
// and implementation parts.
EasyCurly::CurlGlobalInit EasyCurly::setup_and_teardown{};
// end of wrapper class
// --------------------------------------------------------------------------
// with that in place, the rest becomes something like this:
int main() {
EasyCurly curl; // our curl object
CURLcode res = curl.perform("http://www.google.com/");
std::cout << "result: " << res << "\n";
std::cout << "--- document size " << curl.document().size() << " captured ---\n"
<< curl.document() << "---------\n\n";
std::cout << "--- debug size " << curl.debug().size() << " captured ---\n"
<< curl.debug() << "---------\n";
}
I am using qDebug(), qInfo() and so on in combination of qInstallMessageHandler to write my logfiles. I also get an output on my batch screen, when executing my application.
I only found QT_NO_DEBUG_OUTPUT, but I want to toggle this configuration while run-time. Is there a way to prevent Qt from writing to std output?
sadly no, you only get access to the messages, but cannot prevent from beeing written to std output.
That's false in Qt 5 at the very least. The message printing code looks as follows: you can clearly see that only the message handler is being used:
if (grabMessageHandler()) {
// prefer new message handler over the old one
if (msgHandler.load() == qDefaultMsgHandler
|| messageHandler.load() != qDefaultMessageHandler) {
(*messageHandler.load())(msgType, context, message);
} else {
(*msgHandler.load())(msgType, message.toLocal8Bit().constData());
}
ungrabMessageHandler();
} else {
fprintf(stderr, "%s\n", message.toLocal8Bit().constData());
}
As you can see, the fprintf(stderr, ...) is a fallback only if recursion is detected from within the messageHandler itself.
Thus, all you need to prevent any debug output is to implement and set your own messageHandler.
To completely turn off all the debug output in Qt, the following works:
#include <QtCore>
int main() {
qDebug() << "I'm not quiet at all yet";
qInstallMessageHandler(+[](QtMsgType, const QMessageLogContext &, const QString &){});
qDebug() << "I'm very, very quiet";
}
In any case, a sensible implementation of an application-wide file logger might look as follows - it doesn't recreate the QTextStream unnecessarily; it uses QString::toUtf8() instead, and explicitly writes line endings.
#include <QtCore>
class Logger {
static struct Data {
Logger *instance;
QtMessageHandler chainedHandler;
} d;
bool m_isOpen;
QFile m_logFile;
QtMessageHandler m_oldHandler = {};
static void handler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
if (d.instance)
d.instance->log(msg);
if (d.chainedHandler)
d.chainedHandler(type, context, msg);
}
public:
enum ChainMode { DontChain, Chain };
Logger() {
Q_ASSERT(!instance());
m_logFile.setFileName("myLog.txt");
m_isOpen = m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
d.instance = this;
}
~Logger() { uninstallHandler(); }
bool isOpen() const { return m_isOpen; }
void installHandler(ChainMode mode) {
Q_ASSERT(!m_oldHandler);
m_oldHandler = qInstallMessageHandler(handler);
if (mode == Chain)
d.chainedHandler = m_oldHandler;
}
void uninstallHandler() {
if (m_oldHandler) {
m_oldHandler = nullptr;
d.chainedHandler = nullptr;
qInstallMessageHandler(m_oldHandler);
}
}
/// This method is *not* thread-safe. Use with a thread-safe wrapper such as `qDebug`.
void log(const QString & msg) {
if (isOpen()) {
m_logFile.write(msg.toUtf8());
m_logFile.write("\n", 1);
}
}
/// Closes the log file early - this is mostly used for testing.
void endLog() {
uninstallHandler();
m_logFile.close();
}
static Logger *instance() { return d.instance; }
};
Logger::Data Logger::d;
template <typename T> QByteArray streamOutputFor(const T &data) {
QBuffer buf;
buf.open(QIODevice::ReadWrite | QIODevice::Text);
QTextStream s(&buf);
s << data << endl;
buf.close();
return buf.data();
}
QByteArray readEnd(const QString &fileName, int count) {
QFile file(fileName);
if (file.open(QIODevice::ReadOnly | QIODevice::Text) && file.size() >= count) {
file.seek(file.size() - count);
return file.readAll();
}
return {};
}
void test() {
auto const entry = QDateTime::currentDateTime().toString().toUtf8();
Q_ASSERT(Logger::instance()->isOpen());
qDebug() << entry.data();
Logger::instance()->endLog();
auto reference = streamOutputFor(entry.data());
auto readback = readEnd("myLog.txt", reference.size());
Q_ASSERT(!reference.isEmpty());
Q_ASSERT(readback == reference);
}
int main() {
Logger logger;
logger.installHandler(Logger::DontChain);
qDebug() << "Something or else";
test();
}
I would like to load a wxXmlDocument from a std::istream but unfortunately, there's no Load(std::istream&) member function, even if wxWidget is compiled using standard input/output streams.
For what it's worth, I'm using wxWidgets 3.1.0 on macOS.
I don't know if there are alternatives but, since wxXmlDocument provides a Load(wxInputStream&), a solution can be to define an adapter like this one:
class myStdInputStreamAdapter : public wxInputStream {
public:
myStdInputStreamAdapter(std::istream &s): stream{s} {}
protected:
std::istream &stream;
virtual size_t OnSysRead(void *buffer, size_t bufsize) {
std::streamsize size = 0;
stream.peek();
if (stream.fail() || stream.bad()) {
m_lasterror = wxSTREAM_READ_ERROR;
} else if (stream.eof()) {
m_lasterror = wxSTREAM_EOF;
} else {
size = stream.readsome(static_cast<std::istream::char_type *>(buffer),
bufsize);
}
return size;
}
};
And then use it to load the xml:
void f(std::istream &istream) {
wxXmlDocument xml;
myStdInputStreamAdapter inputStreamAdapter(istream);
xml.Load(inputStreamAdapter);
}