Integrated unit testing framework with scons on Linux.
I needed this to make sure that BKE_copy_images works properly, probably will be useful in future. Using Check framework (http://check.sourceforge.net/doc/check.html/index.html). WITH_BF_UNIT_TEST option builds 'alltest' program under [BUILDDIR]/bin, which, when executed, runs unit tests, currently only 1. Example output: ---------------------------------------------------------------------- Running suite(s): Image 0%: Checks: 1, Failures: 1, Errors: 0 tests/alltest.c:74:F:Core:test_copy_images:0: Expected //bar/image.png to be translated to /tmp/bar/image.png, got /tmp/bar/image.pn. ---------------------------------------------------------------------- Spent lots of time (a couple of days actually :) to figure out how to link the test program with Blender libraries. As it turned out there are circular dependencies among Blender libraries. GCC by default doesn't expect circular dependencies - dependant libs should precede libs they depend on. The magical --start-group linker option helped to solve this (http://stephane.carrez.free.fr/doc/ld_2.html#IDX122). Also: - added bpy.util module. bpy.sys.* functions will move here later - added bpy.util.copy_images that uses BKE_copy_images - export_obj.py uses bpy.util.copy_images
This commit is contained in:
parent
2a23fda9d5
commit
ad0b2c87d5
11
SConstruct
11
SConstruct
@ -407,7 +407,16 @@ if env['WITH_BF_PLAYER']:
|
||||
|
||||
if 'blender' in B.targets or not env['WITH_BF_NOBLENDER']:
|
||||
#env.BlenderProg(B.root_build_dir, "blender", dobj , [], mainlist + thestatlibs + thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blender')
|
||||
env.BlenderProg(B.root_build_dir, "blender", dobj + mainlist, [], thestatlibs + thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blender')
|
||||
blen = env.BlenderProg(B.root_build_dir, "blender", dobj + mainlist, [], thestatlibs + thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blender')
|
||||
|
||||
build_data = {"lib": thestatlibs + thesyslibs, "libpath": thelibincs, "blen": blen}
|
||||
|
||||
Export('env')
|
||||
Export('build_data')
|
||||
|
||||
BuildDir(B.root_build_dir+'/tests', 'tests', duplicate=0)
|
||||
SConscript(B.root_build_dir+'/tests/SConscript')
|
||||
|
||||
if env['WITH_BF_PLAYER']:
|
||||
playerlist = B.create_blender_liblist(env, 'player')
|
||||
env.BlenderProg(B.root_build_dir, "blenderplayer", dobj + playerlist, [], thestatlibs + thesyslibs, [B.root_build_dir+'/lib'] + thelibincs, 'blenderplayer')
|
||||
|
@ -178,19 +178,22 @@ def copy_images(dest_dir):
|
||||
pass
|
||||
|
||||
# Now copy images
|
||||
copyCount = 0
|
||||
# copyCount = 0
|
||||
|
||||
for bImage in uniqueImages.values():
|
||||
image_path = bpy.sys.expandpath(bImage.filename)
|
||||
if bpy.sys.exists(image_path):
|
||||
# Make a name for the target path.
|
||||
dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1]
|
||||
if not bpy.sys.exists(dest_image_path): # Image isnt alredy there
|
||||
print('\tCopying "%s" > "%s"' % (image_path, dest_image_path))
|
||||
copy_file(image_path, dest_image_path)
|
||||
copyCount+=1
|
||||
# for bImage in uniqueImages.values():
|
||||
# image_path = bpy.sys.expandpath(bImage.filename)
|
||||
# if bpy.sys.exists(image_path):
|
||||
# # Make a name for the target path.
|
||||
# dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1]
|
||||
# if not bpy.sys.exists(dest_image_path): # Image isnt alredy there
|
||||
# print('\tCopying "%s" > "%s"' % (image_path, dest_image_path))
|
||||
# copy_file(image_path, dest_image_path)
|
||||
# copyCount+=1
|
||||
|
||||
paths= bpy.util.copy_images(uniqueImages.values(), dest_dir)
|
||||
|
||||
print('\tCopied %d images' % copyCount)
|
||||
# print('\tCopied %d images' % copyCount)
|
||||
|
||||
# XXX not converted
|
||||
def test_nurbs_compat(ob):
|
||||
|
@ -40,6 +40,7 @@ struct ImBuf;
|
||||
struct Tex;
|
||||
struct anim;
|
||||
struct Scene;
|
||||
struct ListBase;
|
||||
|
||||
/* call from library */
|
||||
void free_image(struct Image *me);
|
||||
@ -154,6 +155,11 @@ struct Image *BKE_image_copy(struct Image *ima);
|
||||
/* merge source into dest, and free source */
|
||||
void BKE_image_merge(struct Image *dest, struct Image *source);
|
||||
|
||||
/* ********************************** FOR EXPORTERS *********************** */
|
||||
|
||||
/* copy images into dest_dir */
|
||||
void BKE_copy_images(struct ListBase *images, char *dest_dir, struct ListBase *filenames);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -57,6 +57,9 @@ if env['BF_NO_ELBEEM']:
|
||||
|
||||
if env['WITH_BF_LCMS']:
|
||||
defs.append('WITH_LCMS')
|
||||
|
||||
if env['WITH_BF_UNIT_TEST']:
|
||||
defs.append('WITH_UNIT_TEST')
|
||||
|
||||
if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'):
|
||||
incs += ' ' + env['BF_PTHREADS_INC']
|
||||
|
@ -2109,3 +2109,120 @@ void BKE_image_user_calc_imanr(ImageUser *iuser, int cfra, int fieldnr)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Copy list of images to dest_dir.
|
||||
|
||||
paths is optional, if given, image paths for each image will be written in it.
|
||||
If an image file doesn't exist, NULL is added in paths.
|
||||
|
||||
Logic:
|
||||
|
||||
For each image if it's "below" current .blend file directory,
|
||||
rebuild the same dir structure in dest_dir.
|
||||
|
||||
For example //textures/foo/bar.png becomes
|
||||
[dest_dir]/textures/foo/bar.png.
|
||||
|
||||
If an image is not "below" current .blend file directory, disregard
|
||||
it's path and copy it in the same directory where 3D file goes.
|
||||
|
||||
For example //../foo/bar.png becomes [dest_dir]/bar.png.
|
||||
|
||||
This logic will help ensure that all image paths are relative and
|
||||
that a user gets his images in one place. It'll also provide
|
||||
consistent behaviour across exporters.
|
||||
*/
|
||||
void BKE_copy_images(ListBase *images, char *dest_dir, ListBase *paths)
|
||||
{
|
||||
char path[FILE_MAX];
|
||||
char dir[FILE_MAX];
|
||||
char base[FILE_MAX];
|
||||
char blend_dir[FILE_MAX]; /* directory, where current .blend file resides */
|
||||
char dest_path[FILE_MAX];
|
||||
int len;
|
||||
Image *im;
|
||||
LinkData *link;
|
||||
|
||||
if (paths) {
|
||||
memset(paths, 0, sizeof(*paths));
|
||||
}
|
||||
|
||||
BLI_split_dirfile_basic(G.sce, blend_dir, NULL);
|
||||
|
||||
link= images->first;
|
||||
|
||||
while (link) {
|
||||
im= link->data;
|
||||
|
||||
BLI_strncpy(path, im->name, sizeof(path));
|
||||
|
||||
/* expand "//" in filename and get absolute path */
|
||||
BLI_convertstringcode(path, G.sce);
|
||||
|
||||
/* in unit tests, we don't want to modify the filesystem */
|
||||
#ifndef WITH_UNIT_TEST
|
||||
/* proceed only if image file exists */
|
||||
if (!BLI_exists(path)) {
|
||||
|
||||
if (paths) {
|
||||
LinkData *ld = MEM_callocN(sizeof(LinkData), "PathLinkData");
|
||||
ld->data= NULL;
|
||||
BLI_addtail(paths, ld);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* get the directory part */
|
||||
BLI_split_dirfile_basic(path, dir, base);
|
||||
|
||||
len= strlen(blend_dir);
|
||||
|
||||
/* if image is "below" current .blend file directory */
|
||||
if (!strncmp(path, blend_dir, len)) {
|
||||
|
||||
/* if image is _in_ current .blend file directory */
|
||||
if (!strcmp(dir, blend_dir)) {
|
||||
/* copy to dest_dir */
|
||||
BLI_join_dirfile(dest_path, dest_dir, base);
|
||||
}
|
||||
/* "below" */
|
||||
else {
|
||||
char rel[FILE_MAX];
|
||||
|
||||
/* rel = image_path_dir - blend_dir */
|
||||
BLI_strncpy(rel, dir + len, sizeof(rel));
|
||||
|
||||
BLI_join_dirfile(dest_path, dest_dir, rel);
|
||||
|
||||
#ifndef WITH_UNIT_TEST
|
||||
/* build identical directory structure under dest_dir */
|
||||
BLI_make_existing_file(dest_path);
|
||||
#endif
|
||||
|
||||
BLI_join_dirfile(dest_path, dest_path, base);
|
||||
}
|
||||
|
||||
}
|
||||
/* image is out of current directory */
|
||||
else {
|
||||
/* copy to dest_dir */
|
||||
BLI_join_dirfile(dest_path, dest_dir, base);
|
||||
}
|
||||
|
||||
#ifndef WITH_UNIT_TEST
|
||||
BLI_copy_fileops(path, dest_path);
|
||||
#endif
|
||||
|
||||
if (paths) {
|
||||
LinkData *ld = MEM_callocN(sizeof(LinkData), "PathLinkData");
|
||||
len= strlen(dest_path) + 1;
|
||||
ld->data= MEM_callocN(len, "PathLinkData");
|
||||
BLI_strncpy(ld->data, dest_path, len);
|
||||
BLI_addtail(paths, ld);
|
||||
}
|
||||
|
||||
link= link->next;
|
||||
}
|
||||
}
|
||||
|
@ -92,6 +92,9 @@ void BPY_update_modules( void )
|
||||
PyObject *mod= PyImport_ImportModuleLevel("bpy", NULL, NULL, NULL, 0);
|
||||
PyModule_AddObject( mod, "data", BPY_rna_module() );
|
||||
PyModule_AddObject( mod, "types", BPY_rna_types() );
|
||||
PyModule_AddObject( mod, "util", BPY_util_module() );
|
||||
|
||||
/* XXX this will move to bpy.util */
|
||||
PyModule_AddObject( mod, "sys", BPY_sys_module() );
|
||||
}
|
||||
|
||||
|
@ -25,12 +25,17 @@
|
||||
#include "DNA_listBase.h"
|
||||
#include "RNA_access.h"
|
||||
#include "bpy_util.h"
|
||||
#include "BLI_dynstr.h"
|
||||
#include "bpy_rna.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_dynstr.h"
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "BKE_report.h"
|
||||
|
||||
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_context.h"
|
||||
|
||||
bContext* __py_context = NULL;
|
||||
bContext* BPy_GetContext(void) { return __py_context; };
|
||||
void BPy_SetContext(bContext *C) { __py_context= C; };
|
||||
@ -464,3 +469,127 @@ int BPy_errors_to_report(ReportList *reports)
|
||||
Py_DECREF(pystring);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* bpy.util module */
|
||||
static PyObject *bpy_util_copy_images(PyObject *self, PyObject *args);
|
||||
|
||||
struct PyMethodDef bpy_util_methods[] = {
|
||||
{"copy_images", bpy_util_copy_images, METH_VARARGS, NULL},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03000000
|
||||
static struct PyModuleDef bpy_util_module = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"bpyutil",
|
||||
NULL,
|
||||
-1,
|
||||
bpy_util_methods,
|
||||
NULL, NULL, NULL, NULL
|
||||
};
|
||||
#endif
|
||||
|
||||
PyObject *BPY_util_module( void )
|
||||
{
|
||||
PyObject *submodule, *dict;
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03000000
|
||||
submodule= PyModule_Create(&bpy_util_module);
|
||||
#else /* Py2.x */
|
||||
submodule= Py_InitModule3("bpyutil", bpy_util_methods, NULL);
|
||||
#endif
|
||||
|
||||
dict = PyModule_GetDict(submodule);
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
/*
|
||||
copy_images(images, dest_dir)
|
||||
return filenames
|
||||
*/
|
||||
static PyObject *bpy_util_copy_images(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *dest_dir;
|
||||
ListBase *images;
|
||||
ListBase *paths;
|
||||
LinkData *link;
|
||||
PyObject *seq;
|
||||
PyObject *ret;
|
||||
PyObject *item;
|
||||
int i;
|
||||
int len;
|
||||
|
||||
/* check args/types */
|
||||
if (!PyArg_ParseTuple(args, "Os", &seq, &dest_dir)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Invalid arguments.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* expecting a sequence of Image objects */
|
||||
if (!PySequence_Check(seq)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Expected a sequence of images.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* create image list */
|
||||
len= PySequence_Size(seq);
|
||||
|
||||
if (!len) {
|
||||
PyErr_SetString(PyExc_TypeError, "At least one image should be specified.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* make sure all sequence items are Image */
|
||||
for(i= 0; i < len; i++) {
|
||||
item= PySequence_GetItem(seq, i);
|
||||
|
||||
if (!BPy_StructRNA_Check(item) || ((BPy_StructRNA*)item)->ptr.type != &RNA_Image) {
|
||||
PyErr_SetString(PyExc_TypeError, "Expected a sequence of Image objects.");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
images= MEM_callocN(sizeof(*images), "ListBase of images");
|
||||
|
||||
for(i= 0; i < len; i++) {
|
||||
BPy_StructRNA* srna;
|
||||
|
||||
item= PySequence_GetItem(seq, i);
|
||||
srna= (BPy_StructRNA*)item;
|
||||
|
||||
link= MEM_callocN(sizeof(LinkData), "LinkData image");
|
||||
link->data= srna->ptr.data;
|
||||
BLI_addtail(images, link);
|
||||
|
||||
Py_DECREF(item);
|
||||
}
|
||||
|
||||
paths= MEM_callocN(sizeof(*paths), "ListBase of image paths");
|
||||
|
||||
/* call BKE_copy_images */
|
||||
BKE_copy_images(images, dest_dir, paths);
|
||||
|
||||
/* convert filenames */
|
||||
ret= PyList_New(0);
|
||||
len= BLI_countlist(paths);
|
||||
|
||||
for(link= paths->first, i= 0; link; link++, i++) {
|
||||
if (link->data) {
|
||||
item= PyUnicode_FromString(link->data);
|
||||
PyList_Append(ret, item);
|
||||
Py_DECREF(item);
|
||||
}
|
||||
else {
|
||||
PyList_Append(ret, Py_None);
|
||||
}
|
||||
}
|
||||
|
||||
/* free memory */
|
||||
BLI_freelistN(images);
|
||||
BLI_freelistN(paths);
|
||||
|
||||
/* return filenames */
|
||||
return ret;
|
||||
}
|
||||
|
@ -81,4 +81,6 @@ int BPy_errors_to_report(struct ReportList *reports);
|
||||
struct bContext *BPy_GetContext(void);
|
||||
void BPy_SetContext(struct bContext *C);
|
||||
|
||||
PyObject *BPY_util_module(void);
|
||||
|
||||
#endif
|
||||
|
@ -65,6 +65,8 @@ def validate_arguments(args, bc):
|
||||
'WITH_BF_DOCS',
|
||||
'BF_NUMJOBS',
|
||||
'BF_MSVS',
|
||||
|
||||
'WITH_BF_UNIT_TEST'
|
||||
]
|
||||
|
||||
# Have options here that scons expects to be lists
|
||||
@ -356,7 +358,9 @@ def read_opts(cfg, args):
|
||||
|
||||
('BF_CONFIG', 'SCons python config file used to set default options', 'user_config.py'),
|
||||
('BF_NUMJOBS', 'Number of build processes to spawn', '1'),
|
||||
('BF_MSVS', 'Generate MSVS project files and solution', False)
|
||||
('BF_MSVS', 'Generate MSVS project files and solution', False),
|
||||
|
||||
(BoolVariable('WITH_BF_UNIT_TEST', 'Build unit tests', False))
|
||||
|
||||
) # end of opts.AddOptions()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user