Skip to content

Commit

Permalink
Problem: can't handle temp files in tests easily (#1777)
Browse files Browse the repository at this point in the history
* Problem: zproject's selftest-rw does not allways work

... we run our tests inside complicated Docker containers setup, where is no
guarantee user running test can access files in src/selftest-rw.

Solution: Add a constructor to create temporary file, which is automatically
removed on destroy. This will leads to nicer API for tests + it'd be hard to
find a platform without writable temporary directory. So this concept can work
better than src/selftest-rw + it leads to nicer code.

* Problem: new api not drafted

Solution: mark zfile_tmp as a draft

* Problem: zfile_tmp is linked in non-draft build

Solution: hide the test behing macro guard

* Problem: zfile_tmp can't be merged

Solution: use mkstemp funtion

* Problem: remove_on_destroy setup before file is opened

Solution: do it after file operations are succesfull

* Problem: zfile_tmp fails on Windows

Solution: use macros to hide the windows incompatible part

* Problem: can;t pass CI

Solution: turn zfile_tmp off on Windows
  • Loading branch information
vyskocilm authored and bluca committed Oct 30, 2017
1 parent 82993e1 commit 0714722
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 2 deletions.
5 changes: 5 additions & 0 deletions api/zfile.api
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
<argument name = "name" type = "string" />
</constructor>

<constructor name="tmp" state="draft">
Create new temporary file for writing via tmpfile. File is automaticaly
deleted on destroy
</constructor>

<destructor>
Destroy a file item
</destructor>
Expand Down
8 changes: 8 additions & 0 deletions include/zfile.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,14 @@ CZMQ_EXPORT const char *
CZMQ_EXPORT void
zfile_test (bool verbose);

#ifdef CZMQ_BUILD_DRAFT_API
// *** Draft method, for development use, may change without warning ***
// Create new temporary file for writing via tmpfile. File is automaticaly
// deleted on destroy
CZMQ_EXPORT zfile_t *
zfile_tmp (void);

#endif // CZMQ_BUILD_DRAFT_API
// @end


Expand Down
7 changes: 7 additions & 0 deletions src/czmq_classes.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ CZMQ_PRIVATE void
CZMQ_PRIVATE zlistx_t *
zcertstore_certs (zcertstore_t *self);

// *** Draft method, defined for internal use only ***
// Create new temporary file for writing via tmpfile. File is automaticaly
// deleted on destroy
// Caller owns return value and must destroy it when done.
CZMQ_PRIVATE zfile_t *
zfile_tmp (void);

// *** Draft method, defined for internal use only ***
// Return frame routing ID, if the frame came from a ZMQ_SERVER socket.
// Else returns zero.
Expand Down
69 changes: 67 additions & 2 deletions src/zfile.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ struct _zfile_t {
zdigest_t *digest; // File digest, if known
char *curline; // Last read line, if any
size_t linemax; // Size of allocated buffer
bool remove_on_destroy; // Whenever delete file on destroy
// Typically for tempfiles
int fd; // File descriptor - set up by zfile_tmp
bool close_fd; // XXX: for some reason self->fd == 0 in
// zdir and zdir_patch tests, this is a
// workaround for the problem

// Properties from files that exist on file system
time_t modified; // Modification time
Expand Down Expand Up @@ -101,9 +107,48 @@ zfile_new (const char *path, const char *name)
}
self->handle = 0;
zfile_restat (self);
self->fd = -1;
self->close_fd = false;
return self;
}

// --------------------------------------------------------------------------
// Constructor
// Create new temporary file for writing via tmpfile. File is automaticaly
// deleted on destroy

zfile_t *
zfile_tmp (void)
{
zfile_t *self = (zfile_t *) zmalloc (sizeof (zfile_t));
assert (self);

#if defined (__WINDOWS__)
zsys_info ("zfile_tmp is not yet implemented for Windows");
free (self);
return NULL;
#else
char buffer [PATH_MAX];
strcpy (buffer, "/tmp/czmq_zfile.XXXXXX");
self->fd = mkstemp (buffer);
if (self->fd == -1)
return NULL;

self->handle = fdopen (self->fd, "w+");

if (!self->handle) {
close (self->fd);
self->fd = -1;
return NULL;
}
self->close_fd = true;
self->fullname = strdup (buffer);
#endif

self->remove_on_destroy = true;
zfile_restat (self);
return self;
}

// --------------------------------------------------------------------------
// Destroy a file item
Expand All @@ -115,8 +160,9 @@ zfile_destroy (zfile_t **self_p)
if (*self_p) {
zfile_t *self = *self_p;
zdigest_destroy (&self->digest);
if (self->handle)
fclose (self->handle);
if (self->remove_on_destroy)
zfile_remove (self);
zfile_close (self);
freen (self->fullname);
freen (self->curline);
freen (self->link);
Expand Down Expand Up @@ -506,6 +552,8 @@ zfile_close (zfile_t *self)
zfile_restat (self);
self->eof = false;
}
if (self->close_fd)
close (self->fd);
}


Expand Down Expand Up @@ -830,6 +878,23 @@ zfile_test (bool verbose)
zfile_close (file);
zfile_destroy (&file);

#ifdef CZMQ_BUILD_DRAFT_API
# if ! defined(__WINDOWS__)
zfile_t *tempfile = zfile_tmp ();
assert (tempfile);
assert (zfile_filename (tempfile, NULL));
assert (zsys_file_exists (zfile_filename (tempfile, NULL)));
zchunk_t *tchunk = zchunk_new ("HELLO", 6);
assert (zfile_write (tempfile, tchunk, 0) == 0);
zchunk_destroy (&tchunk);

char *filename = strdup (zfile_filename (tempfile, NULL));
zfile_destroy (&tempfile);
assert (!zsys_file_exists (filename));
zstr_free (&filename);
# endif // ! defined(__WINDOWS__)
#endif // CZMQ_BUILD_DRAFT_API

#if defined (__WINDOWS__)
zsys_shutdown();
#endif
Expand Down

0 comments on commit 0714722

Please sign in to comment.