понедельник, 12 декабря 2016 г.

C++ command line helper

Иногда надо исполнить какую-то команду ОС. Это легко делается с помощью создания нового процесса и получение данных от него через пайп:

#include <iostream>
#include <stdio.h>
#include <memory>
 
bool run_shell_cmd(const std::string & cmd, std::string & outputStr)
{
    int closePipeResult = -1;
    // deleter to close pipe
    auto pipeDeleter = [&closePipeResult](FILE* pipe)
    {
        if(pipe != NULL)
            closePipeResult = pclose(pipe);
    };
    // create smart poinetr with custom deleter
    std::unique_ptr<FILE, decltype(pipeDeleter)> pipe(popen(cmd.c_str(), "r"), pipeDeleter);
 
    if(pipe.get() == NULL) 
        return false;
 
    // read output and save it to string
    char buffer[128];
    while (!feof(pipe.get()))
    {
        if (fgets(buffer, 128, pipe.get()) != NULL)
            outputStr += buffer;
    }
 
    // reset pointer to get result of pclose before exit of function
    pipe.reset();
 
    return (closePipeResult == 0);
}

среда, 7 декабря 2016 г.

Google Test Gmock

Предположим, мы разрабатываем клиента для некоего сервера, а сервер разрабатывается совсем другой командой и на данный момент он либо неработоспособен, либо вообще недоступен (очень частая ситуация на самом деле). Единственное, что у нас есть - это интерфейс этого сервера без имплементации в виде исходных файлов или бинарей. В таких случаях обычно делают какой-то простой сервер-заглушку, который по крайней мере позволяет проводить на нем отладку клиентской части. Это достаточно трудоемко и именно для того, чтобы избавить программиста от ненужной работы, и был создан gmock как часть Google Test Framework.
Лучше всего показать на примере.

// our server (we have interface only)
class IMyCalcServer
{
public:
    virtual int add(int a, int b) = 0;
    virtual int mult(int a, int b) = 0;
    virtual ~IMyCalcServer() {}
};
 
// client class
class MyClient
{
public:
    MyClient(IMyCalcServer * server) : srv(server) {}
    void doMath()
    {
        std::cout << "add " << srv->add(2, 2) << std::endl;
        std::cout << "mult " << srv->mult(1, 2) << std::endl;
    }
private:
    IMyCalcServer * srv;
};
 
// implementation of IMyCalcServer interface by gmock
class CalcServerMock : public IMyCalcServer
{
public:
    MOCK_METHOD2(add, int(int a, int b));
    MOCK_METHOD2(mult, int(int a, int b));
};
 
TEST(MyGmockTest, test1)
{
    // create instance of "InSequence" class to force gmock expect function calls in stict order
    ::testing::InSequence dummy;
 
    // here we create server instance, define calls order, parameters and return values
    CalcServerMock calcSrvMock;
    // we expect add() is called with parameters (2,2) and returns 4
    EXPECT_CALL(calcSrvMock, add(2,2)).WillOnce(testing::Return(4));
    // we expect mult() is called with parameters (2,2) and returns 4
    EXPECT_CALL(calcSrvMock, mult(2,2)).WillOnce(testing::Return(4));
 
    // do calculations
    MyClient cl(&calcSrvMock);
    cl.doMath();
}