Customizing I/O of CoverageScanner library

Custom I/O using C file access

The following example shows how to generate the execution report using the standard C file API.

To compile the example on Microsoft® Windows:

cscl customiofile.c

To compile the example on Linux™ or macOS:

csgcc customiofile.c -o customiofile

Source code:

#include <stdio.h>
#include <string.h>
#define VERBOSE 1

static int csfputs(const char *s, void *stream) {
#if VERBOSE
    fprintf(stderr, "csfputs:%s\n", s);
#endif
    return fputs(s, (FILE *)stream);
}

static void *csfopenappend(const char *path) {
#if VERBOSE
    fprintf(stderr, "csfopenappend:%s\n", path);
#endif
    return (void *)fopen(path, "a+");
}

static void *csfopenread(const char *path) {
#if VERBOSE
    fprintf(stderr, "csfopenread:%s\n", path);
#endif
    return (void *)fopen(path, "r");
}

static void *csfopenwrite(const char *path) {
#if VERBOSE
    fprintf(stderr, "csfopenwrite:%s\n", path);
#endif
    return (void *)fopen(path, "w");
}

static char *csfgets(char *s, int size, void *stream) {
    char *ret;
    ret = fgets(s, size, (FILE *)stream);
#if VERBOSE
    fprintf(stderr, "csfgets:%s\n", s);
#endif
    return ret;
}

static int csremove(const char *path) {
#if VERBOSE
    fprintf(stderr, "csremove:%s\n", path);
#endif
    return remove(path);
}

static int csfclose(void *stream) {
#if VERBOSE
    fprintf(stderr, "csfclose\n");
#endif
    return fclose((FILE *)stream);
}

int main(int argc, char* argv[]) {
    char location[256];
    int lg_location;

    printf(".csexe file name (without extension}:");
    fflush(stdout);
    fgets(location, sizeof(location), stdin);
    lg_location = strlen(location);
    if (lg_location)
        location[lg_location - 1] = '\0'; // strip \n
#ifdef __COVERAGESCANNER__
    __coveragescanner_set_custom_io(csfgets, csfputs, csfopenappend,
                                    csfopenread, csfopenwrite, csfclose,
                                    csremove);
    __coveragescanner_install(location);
#endif
}

Custom I/O using a UNix TCP/IP server

The following example shows how to generate the execution report using the standard Unix socket API. The application intergrates then a TCP/IP server that permits to send the code coderage counters through the local network.

The simple socat command can be used to fetch the data. For example:

$ socat -dd - TCP4:localhost:4321 > app.csexe

This command retrieves from localhost on the port 4321 the code coverage and store it into the file app.csexe. This can then be imported using cmcsexeimport:

$ socat -dd - TCP4:localhost:4321 > app.csexe && cmcsexeimport -m app.csmes -t test –delete app.csexe

CoverageBrowser supports also supports the possibility to integrate this command into the import dialog. This permits to do interactive manual tests on which CoverageBrowser fetches and imports the coverage data after pressing on the "Load Execution Report" button.

For this, just select the Script mode and enter the command socat -dd - TCP4:localhost:4321. Deactivate the Delete execution report after loading because it requires a specific command to delete remotely a .csexe file.

Clicking on Next permits to display a terminal window which permits to follow the progress.

This view switches automatically to the final import dialog on success:

The server code need to be integrated into the target application. To do it, it is necessary to compile coverage.c and activate the server. For this, it is necessary to place the following code snippet at the beginning of the program:

#ifdef __COVERAGESCANNER__
        start_coverage_server( 4321 );
#endif

The number passed on start_coverage_server() function is the TCP port number to server.

To compile the example on Linux™ or macOS:

csgcc coverage.c -o coverage.o
csgcc app.c -o app.o
csgcc app.o cooverage.o -o app.exe

Source code of the application sample:

#include "coverage.h"
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main()
{
#ifdef __COVERAGESCANNER__
    start_coverage_server( 4321 );
#endif

    srand( time(NULL) );

    static char *banner =
        "Hello,\n"
        "please use the following command to dump the code coverage:\n"
        "$ socat -dd - TCP4:<IP address from this computer>:4321\n"
        ;
    int line = 1;
    while ( 1 )
    {
        char *c = banner;
        fprintf( stderr, "%4i: ", line ); line++;
        while ( *c != '\0' )
        {
            fputc( *c, stderr );
            if ( *c == '\n' )
            {
                fprintf( stderr, "%4i: ", line ); line++;
            }
            c++;
            usleep( rand()%200000 );
        }
        fputc( '\n', stderr );
        sleep( 1 );
    }
    return 0;
}

Source code of the server code:

#ifndef COVERAGE_H
#define COVERAGE_H
#ifdef __COVERAGESCANNER__
void start_coverage_server( int port );
#endif
#endif
#ifdef __COVERAGESCANNER__
#include "coverage.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>

typedef struct
{
    int conn ;
} cov_ctx_t;

static int csfputs(const char *s, void *stream) {
    ssize_t ret;
    cov_ctx_t *ctx = (cov_ctx_t*)stream;
    ret = write( ctx->conn, s, strlen(s) );
    if ( ret < 0 )
        perror( "TCP writing error" );
    return (int)ret;
}

static void *csfopenappend(const char *s) {
    cov_ctx_t *ctx = (cov_ctx_t*)malloc( sizeof(cov_ctx_t) );
    ctx->conn = atoi( s );
    return (void *)ctx;
}

static int csfclose(void *stream) {
    cov_ctx_t *ctx = (cov_ctx_t*)stream;
    close(ctx->conn);
    free(ctx);
    return 1;
}

static void coverage_server( int port )
{
    int clintListn = 0, clintConnt = 0;
    struct sockaddr_in ipOfServer;
    clintListn = socket(AF_INET, SOCK_STREAM, 0);
    memset(&ipOfServer, '0', sizeof(ipOfServer));
    ipOfServer.sin_family = AF_INET;
    ipOfServer.sin_addr.s_addr = htonl(INADDR_ANY);
    ipOfServer.sin_port = htons( port );
    bind(clintListn, (struct sockaddr*)&ipOfServer , sizeof(ipOfServer));
    listen(clintListn , 20);

    __coveragescanner_set_custom_io(NULL, csfputs, csfopenappend, NULL, NULL, csfclose, NULL);
    while(1)
    {
        clintConnt = accept(clintListn, (struct sockaddr*)NULL, NULL);
        static char location[20];
        sprintf( location, "%i", clintConnt );
        __coveragescanner_filename( location );
        __coveragescanner_save();
    }

}

static void* coverage_thread( void *port_p )
{
    int port = *(int*)port_p;
    coverage_server( port );
}

void start_coverage_server( int port )
{
    static int p ;
    p = port;
    pthread_t thread;
    pthread_create( &thread, NULL, coverage_thread, &p );
}
#endif

Custom I/O using SFTP protocol

The following example shows how to generate the execution report directly on a SFTP server. The SFTP server is part of SSH v2 and is available on most Unix platforms. On Windows, a free SSH server can be downloaded from freeSSHd and freeFTPd.

To compile the example on Windows:

  1. Download libSSH2 from libssh2. Build the library, and set the environment variable LIBSSH2 to the build/install location
  2. To compile the example:
    cscl %LIBSSH2%\win32\debug_dll\libssh2.lib -DWIN32 --cs-libgen=/MTd
         /MTd -I %LIBSSH2%\include ws2_32.lib customiosftp.c
  3. Execute custom_io_sftp.exe.

To compile the example on Linux™:

  1. Install the development package of libssh2.
  2. To compile the example:
    csgcc -lssh2 customiosftp.c -o customiosftp
  3. Execute custom_io_sftp.

Source code:

#define VERBOSE 1

#ifdef WIN32
#include <winsock2.h>
#define LIBSSH2_WIN32
#define LIBSSH2_API
#else
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#endif
#include <libssh2.h>
#include <libssh2_sftp.h>

#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>

static LIBSSH2_SESSION *session = NULL;
static LIBSSH2_SFTP *sftp_session = NULL;
static int sock = 0;

static void extract_location(const char *location, char *server, char *user,
                             char *passwd, char *file) {
    int i, j;
    int lg_location;
    lg_location = strlen(location);
    for (i = 0; i < lg_location; i++) {
        if (location[i] == '\n')
            break;
        server[i] = location[i];
    }
    server[i] = '\0';
    i++;

    for (j = 0; i < lg_location; i++, j++) {
        if (location[i] == '\n')
            break;
        user[j] = location[i];
    }
    user[j] = '\0';
    i++;

    for (j = 0; i < lg_location; i++, j++) {
        if (location[i] == '\n')
            break;
        passwd[j] = location[i];
    }
    passwd[j] = '\0';
    i++;

    for (j = 0; i < lg_location; i++, j++) {
        if (location[i] == '\0')
            break;
        file[j] = location[i];
    }
    file[j] = '\0';
}

static void close_sftp_session() {
    if (sftp_session)
        libssh2_sftp_shutdown(sftp_session);
    sftp_session = NULL;

    if (session) {
        libssh2_session_disconnect(session,
                                   "Normal Shutdown, Thank you for playing");
        libssh2_session_free(session);
    }
    session = NULL;

    if (sock) {
#ifdef WIN32
        Sleep(1000);
        closesocket(sock);
#else
        sleep(1);
        close(sock);
#endif
    }
    sock = 0;
}

static int open_sftp_session(const char *server, const char *user,
                             const char *passwd) {
    struct sockaddr_in sin;
    int rc;

    sock = socket(AF_INET, SOCK_STREAM, 0);

    sin.sin_family = AF_INET;
    sin.sin_port = htons(22);
    sin.sin_addr.s_addr = inet_addr(server);
    if (connect(sock, (struct sockaddr *)(&sin), sizeof(struct sockaddr_in)) !=
        0) {
        close_sftp_session();
        return 0;
    }

    /* Create a session instance
     */
    session = libssh2_session_init();
    if (!session) {
        close_sftp_session();
        return 0;
    }

    /* ... start it up. This will trade welcome banners, exchange keys,
     * and setup crypto, compression, and MAC layers
     */
    rc = libssh2_session_startup(session, sock);
    if (rc) {
        close_sftp_session();
        return 0;
    }

    libssh2_session_set_blocking(session, 1);

    if (libssh2_userauth_password(session, user, passwd)) {
        close_sftp_session();
        return 0;
    }

    sftp_session = libssh2_sftp_init(session);

    if (!sftp_session) {
        close_sftp_session();
        return 0;
    }
    return 1;
}

static int csfputs(const char *s, void *stream) {
#if VERBOSE
    fprintf(stderr, "csfputs:%s\n", s);
#endif
    return libssh2_sftp_write((LIBSSH2_SFTP_HANDLE *)stream, s, strlen(s));
}

static void *csfopenappend(const char *location) {
    LIBSSH2_SFTP_HANDLE *handle;
    char server[1024];
    char user[1024];
    char passwd[1024];
    char file[1024];
    LIBSSH2_SFTP_ATTRIBUTES attrs;

    extract_location(location, server, user, passwd, file);
#if VERBOSE
    fprintf(stderr, "csfopenappend %s:%s:%s\n", server, user, file);
#endif
    if (open_sftp_session(server, user, passwd)) {
        handle = libssh2_sftp_open(
            sftp_session, file, LIBSSH2_FXF_CREAT | LIBSSH2_FXF_WRITE,
            LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR | LIBSSH2_SFTP_S_IRGRP |
                LIBSSH2_SFTP_S_IROTH);
    } else
        return NULL;

    if (handle) {
        if (libssh2_sftp_fstat(handle, &attrs) ==
            0) { /* Go to the end of the file */
            libssh2_sftp_seek(handle, attrs.filesize);
        }
    }
    return handle;
}

static void *csfopenread(const char *location) {
    char server[1024];
    char user[1024];
    char passwd[1024];
    char file[1024];

    extract_location(location, server, user, passwd, file);
#if VERBOSE
    fprintf(stderr, "csfopenread %s:%s:%s\n", server, user, file);
#endif
    if (open_sftp_session(server, user, passwd))
        return (void *)libssh2_sftp_open(sftp_session, file, LIBSSH2_FXF_READ,
                                         0);
    else
        return NULL;
}

static void *csfopenwrite(const char *location) {
    char server[1024];
    char user[1024];
    char passwd[1024];
    char file[1024];

    extract_location(location, server, user, passwd, file);
#if VERBOSE
    fprintf(stderr, "csfopenwrite %s:%s:%s\n", server, user, file);
#endif
    if (open_sftp_session(server, user, passwd))
        return (void *)libssh2_sftp_open(
            sftp_session, file, LIBSSH2_FXF_CREAT | LIBSSH2_FXF_WRITE,
            LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR | LIBSSH2_SFTP_S_IRGRP |
                LIBSSH2_SFTP_S_IROTH);
    else
        return NULL;
}

static char *csfgets(char *s, int size, void *stream) {
    size_t ss;
    ss = libssh2_sftp_read((LIBSSH2_SFTP_HANDLE *)stream, s, size - 1);
    if (ss) {
        s[ss] = '\0';
#if VERBOSE
        fprintf(stderr, "csfgets:%s\n", s);
#endif
        return s;
    } else
        return NULL;
}

static int csremove(const char *location) {
    int ret;
    char server[1024];
    char user[1024];
    char passwd[1024];
    char file[1024];

    extract_location(location, server, user, passwd, file);
#if VERBOSE
    fprintf(stderr, "csremove %s:%s:%s\n", server, user, file);
#endif
    if (open_sftp_session(server, user, passwd)) {
        ret = libssh2_sftp_unlink(sftp_session, file);
        close_sftp_session();
        return ret;
    } else
        return -1;
}

static int csfclose(void *fp) {
#if VERBOSE
    fprintf(stderr, "csfclose\n");
#endif
    return libssh2_sftp_close((LIBSSH2_SFTP_HANDLE *)fp);
}

int main() {
    char location[1024];
    char tmp[1024];
#ifdef WIN32
    WSADATA wsadata;

    WSAStartup(WINSOCK_VERSION, &wsadata);
#endif

    location[0] = '\0';

    printf("server IP:");
    fflush(stdout);
    fgets(tmp, sizeof(tmp), stdin);
    strcat(location, tmp);

    printf("user:");
    fflush(stdout);
    fgets(tmp, sizeof(tmp), stdin);
    strcat(location, tmp);

    printf("passwd:");
    fflush(stdout);
    fgets(tmp, sizeof(tmp), stdin);
    strcat(location, tmp);

    printf(".csexe file name (without extension}:");
    fflush(stdout);
    fgets(tmp, sizeof(tmp), stdin);
    strcat(location, tmp);

    location[strlen(location) - 1] = '\0';

#ifdef __COVERAGESCANNER__
    __coveragescanner_set_custom_io(csfgets, csfputs, csfopenappend,
                                    csfopenread, csfopenwrite, csfclose,
                                    csremove);
    __coveragescanner_install(location);
#endif
}

Coco v7.2.1 ©2024 The Qt Company Ltd.
Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.