libvips/test/test_descriptors.c
Kleis Auke Wolthuizen 4611651d90
nsgifload: avoid minimise after mapping (#3189)
* nsgifload: avoid minimise after mapping

Not reliable on Windows.

* nsgifload: prefer use of `VIPS_FREEF` macro

* Improve `test_descriptors.c`

* Only build `test_descriptors` when targeting Linux
2022-11-27 15:43:35 +00:00

164 lines
3.7 KiB
C

/* Read an image and check that file handles are being closed on minimise.
*
* This will only work on linux: we signal success and do nothing if
* /proc/self/fd does not exist.
*/
#include <stdio.h>
#include <unistd.h>
#define _GNU_SOURCE
#include <stdlib.h>
#include <vips/vips.h>
/**
* get_open_files:
*
* Get a list of open files for this process.
*
* Returns (transfer full) (nullable): a new #GSList, or %NULL
*/
static GSList *
get_open_files()
{
GSList *list = NULL;
GDir *dir;
const char *name;
if( !(dir = g_dir_open( "/proc/self/fd", 0, NULL )) )
return( NULL );
while( (name = g_dir_read_name( dir )) ) {
char *fullname = g_build_filename( "/proc/self/fd", name, NULL );
list = g_slist_prepend( list, realpath( fullname, NULL ) );
g_free( fullname );
}
g_dir_close( dir );
return( list );
}
/**
* fd_check:
* @stage: the originating stage for the error message
* @fds: a #GSList of file descriptors to check against
*
* Check for a leak by comparing the currently open files for this
* process with the file descriptors in @fds. If there's a leak,
* print an error message and return %FALSE.
*
* See also: get_open_files().
*
* Returns: %TRUE if there are no leaks; %FALSE otherwise
*/
static gboolean
fd_check( const char *stage, GSList *fds )
{
GSList *unique_list = NULL, *list, *iter;
list = get_open_files();
for( iter = list; iter; iter = iter->next )
if( !g_slist_find_custom( fds, iter->data,
(GCompareFunc) g_strcmp0 ) )
unique_list = g_slist_prepend( unique_list, iter->data );
if( unique_list == NULL ) {
g_slist_free_full( list, g_free );
return( TRUE );
}
fprintf( stderr, "%s: file descriptors not closed after %s:\n",
vips_get_prgname(), stage );
for( iter = unique_list; iter; iter = iter->next )
fprintf( stderr, "%s\n", (char *) iter->data );
g_slist_free( unique_list );
g_slist_free_full( list, g_free );
return( FALSE );
}
int
main( int argc, char **argv )
{
VipsSource *source;
VipsImage *image, *x;
GSList *list;
double average;
if( VIPS_INIT( argv[0] ) )
vips_error_exit( "unable to start" );
if( argc != 2 )
vips_error_exit( "usage: %s test-image", argv[0] );
list = get_open_files();
if( list == NULL )
/* Probably not linux, silent success.
*/
return( 0 );
/* This is usually a list of 4 files. stdout / stdin / stderr plus one
* more made for us by glib, I think, doing what I don't know.
*/
/* Opening an image should read the header, then close the fd.
*/
printf( "** rand open ..\n" );
if( !(source = vips_source_new_from_file( argv[1] )) )
goto error;
if( !(image = vips_image_new_from_source( source, "",
"access", VIPS_ACCESS_RANDOM,
NULL )) )
goto error;
if( !fd_check( "header read", list ) )
goto error;
/* We should be able to read a chunk near the top, then have the fd
* closed again.
*/
printf( "** crop1, avg ..\n" );
if( vips_crop( image, &x, 0, 0, image->Xsize, 10, NULL ) ||
vips_avg( x, &average, NULL ) )
goto error;
g_object_unref( x );
if( !fd_check( "first read", list ) )
goto error;
/* We should be able to read again, a little further down, and have
* the input restarted and closed again.
*/
printf( "** crop2, avg ..\n" );
if( vips_crop( image, &x, 0, 20, image->Xsize, 10, NULL ) ||
vips_avg( x, &average, NULL ) )
goto error;
g_object_unref( x );
if( !fd_check( "second read", list ) )
goto error;
/* Clean up, and we should still just have three open.
*/
printf( "** unref ..\n" );
g_object_unref( image );
g_object_unref( source );
printf( "** shutdown ..\n" );
vips_shutdown();
if( !fd_check( "shutdown", list ) )
goto error;
g_slist_free_full( list, g_free );
return( 0 );
error:
g_slist_free_full( list, g_free );
return( 1 );
}