Skip to content

Commit

Permalink
Fixes #261, creating a new dialog-box free export button and its (sic…
Browse files Browse the repository at this point in the history
…keningly annoying to make) unit test.

In tests.py, the way we parse for errors with the test is now even less fragile. New lines are appended to make that first line of the unit test useful.
  • Loading branch information
tngreene committed Jul 17, 2017
1 parent 17b111d commit 7a96f1d
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 20 deletions.
32 changes: 26 additions & 6 deletions io_xplane2blender/xplane_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import bpy
import mathutils
import os
import sys
from .xplane_helpers import XPlaneLogger, logger
from .xplane_types import xplane_file
from .xplane_config import getDebug, getLog, initConfig
Expand All @@ -21,7 +22,8 @@ def draw(self, context):
row.label('Please take a look into the internall text file XPlane2Blender.log')

def showLogDialog():
bpy.ops.wm.call_menu(name = "SCENE_MT_xplane_export_log")
if not ('-b' in sys.argv or '--background' in sys.argv):
bpy.ops.wm.call_menu(name = "SCENE_MT_xplane_export_log")

# Class: ExportXPlane
# Main Export class. Brings all parts together and creates the OBJ files.
Expand Down Expand Up @@ -160,17 +162,35 @@ def _writeXPlaneFile(self, xplaneFile, dir):
# only write layers that contain objects
if len(xplaneFile.objects) == 0:
return

fullpath = os.path.join(dir, xplaneFile.filename) + '.obj'


if xplaneFile.filename.find('//') == 0:
xplaneFile.filename = xplaneFile.filename.replace('//','',1)

if os.path.isabs(xplaneFile.filename):
logger.error("Bad export path %s: File paths must be relative to the .blend file" % (xplaneFile.filename))
return False

#Get the relative path
#Append .obj if needed
#Make paths based on the absolute path
#Write
relpath = os.path.normpath(os.path.join(dir, xplaneFile.filename))
if not '.obj' in relpath:
relpath += '.obj'


fullpath = os.path.abspath(os.path.join(os.path.dirname(bpy.context.blend_data.filepath),relpath))

os.makedirs(os.path.dirname(fullpath),exist_ok=True)

out = xplaneFile.write()

if logger.hasErrors():
return False

# write the file
logger.info("Writing %s" % fullpath)

objFile = open(fullpath, "w")
objFile.write(out)
objFile.close()
Expand Down
25 changes: 23 additions & 2 deletions io_xplane2blender/xplane_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import bpy
import os
import io_xplane2blender

FLOAT_PRECISION = 8

Expand Down Expand Up @@ -48,11 +49,28 @@ def resolveBlenderPath(path):
else:
return path

#This a hack to help tests.py catch when an error is an error,
#because everybody and their pet poodle like using the words 'Fail',
#'Error', "FAIL", and "ERROR" making regex impossible.
#
#unittest prints a handy string of .'s, F's, and E's on the first line,
#but due to reasons beyond my grasp, sometimes they don't print a newline
#at the end of it when a failure occurs, making it useless, since we use the word
#"INFO" with an F, meaning you can't search the first line for an F!
#
#Hence this stupid stupid hack, which, is hopefully useful in someway
#Rather than a "did_print_once"
#
#This is yet another reminder about how relying on strings printed to a console
#To tell how your unit test went is a bad idea, epsecially when you can't seem to control
#What gets output when.
message_to_str_count = 0

class XPlaneLogger():
def __init__(self):
self.transports = []
self.messages = []

def addTransport(self, transport, messageTypes = ['error', 'warning', 'info', 'success']):
self.transports.append({
'fn': transport,
Expand Down Expand Up @@ -129,9 +147,10 @@ def hasWarnings(self):

def findInfos(self):
return self.findOfType('info')

@staticmethod
def messageToString(messageType, message, context = None):
io_xplane2blender.xplane_helpers.message_to_str_count += 1
return '%s: %s' % (messageType.upper(), message)

@staticmethod
Expand All @@ -151,6 +170,8 @@ def transport(messageType, message, context = None):
@staticmethod
def ConsoleTransport():
def transport(messageType, message, context = None):
if io_xplane2blender.xplane_helpers.message_to_str_count == 1:
print('\n')
print(XPlaneLogger.messageToString(messageType, message, context))

return transport
Expand Down
19 changes: 17 additions & 2 deletions io_xplane2blender/xplane_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,17 @@ def execute(self, context):
obj.xplane.conditions.remove(self.index)
return {'FINISHED'}

# Class: SCENE_OT_export_to_relative_dir
# Exports OBJS into the same folder as the .blend file, and/or folders beneath it
class SCENE_OT_export_to_relative_dir(bpy.types.Operator):
bl_label = 'Export OBJs'
bl_idname = 'scene.export_to_relative_dir'
bl_description = 'Exports OBJs into the same folder as the .blend file, and/or specified sub-folders'

def execute(self, context):
bpy.ops.export.xplane_obj(filepath="")
return {'FINISHED'}

# Function: addXPlaneOps
# Registers all Operators.
def addXPlaneOps():
Expand Down Expand Up @@ -578,9 +589,11 @@ def addXPlaneOps():
bpy.utils.register_class(OBJECT_OT_remove_xplane_object_condition)
bpy.utils.register_class(OBJECT_OT_add_xplane_material_condition)
bpy.utils.register_class(OBJECT_OT_remove_xplane_material_condition)

bpy.utils.register_class(SCENE_OT_export_to_relative_dir)

#See xplane_ops_dev.py
bpy.utils.register_class(SCENE_OT_dev_export_to_current_dir)
#bpy.utils.register_class(SCENE_OT_dev_export_to_current_dir)
bpy.utils.register_class(SCENE_OT_dev_layer_names_from_objects)

# Function: removeXPlaneOps
Expand Down Expand Up @@ -614,6 +627,8 @@ def removeXPlaneOps():
bpy.utils.unregister_class(OBJECT_OT_add_xplane_material_condition)
bpy.utils.unregister_class(OBJECT_OT_remove_xplane_material_condition)

bpy.utils.unregister_class(SCENE_OT_export_to_relative_dir)

#See xplane_ops_dev.py
bpy.utils.unregister_class(SCENE_OT_dev_export_to_current_dir)
#bpy.utils.unregister_class(SCENE_OT_dev_export_to_current_dir)
bpy.utils.unregister_class(SCENE_OT_dev_layer_names_from_objects)
2 changes: 1 addition & 1 deletion io_xplane2blender/xplane_ops_dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import re

class SCENE_OT_dev_export_to_current_dir(bpy.types.Operator):
bl_label = 'Export .blend File To Current Dir'
bl_label = 'Export .blend file to current dir'
bl_idname = 'scene.dev_export_to_current_dir'
bl_description = 'Exports blender file to current working directory. Useful for quick plugin testing'

Expand Down
13 changes: 8 additions & 5 deletions io_xplane2blender/xplane_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ def scene_layout(self, scene):
row = layout.row()
row.prop(scene.xplane, "debug", text = "Debug")

row = layout.row()
row.operator("scene.export_to_relative_dir")

if scene.xplane.debug:
box = layout.box()
box.prop(scene.xplane, "profile", text = "Profiling")
Expand All @@ -172,8 +175,8 @@ def scene_layout(self, scene):
row.prop(scene.xplane, "dev_enable_breakpoints")
row = box.row()
row.prop(scene.xplane, "dev_continue_export_on_error")
row = box.row()
row.operator("scene.dev_export_to_current_dir")
#row = box.row() #Enable if new export button is too inconvienent
#row.operator("scene.dev_export_to_current_dir")
row = box.row()
row.operator("scene.dev_layer_names_to_current_dir")
row = layout.row()
Expand Down Expand Up @@ -252,9 +255,9 @@ def layer_layout(self, layout, layerObj, version, context = 'scene'):
isInstanced = version >= 1000 and layerObj.export_type == 'instanced_scenery'

column = layout.column()
column.prop(layerObj, "export", text = "Export")
column.prop(layerObj, "debug", text = "Debug")
column.prop(layerObj, "name", text = "Name")
column.prop(layerObj, "export", text = "Export")
column.prop(layerObj, "debug", text = "Debug")
column.prop(layerObj, "name", text = "Name")
column.prop(layerObj, "export_type", text = "Type")

column.label('Textures')
Expand Down
13 changes: 9 additions & 4 deletions tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
#WARNING_LOGGER_REGEX = "LOGGER HAD ([+-]?\d+) UNEXPECTED WARNING"
if os.path.exists('./tests/tmp'):
# empty temp directory
shutil.rmtree('./tests/tmp')
shutil.rmtree('./tests/tmp',ignore_errors=True)

# create temp dir if not exists
os.mkdir('./tests/tmp')
os.makedirs('./tests/tmp',exist_ok=True)

def getFlag(name):
return name in sys.argv
Expand Down Expand Up @@ -130,8 +130,13 @@ def printTestEnd():

if not be_quiet:
print(out)

if 'FAIL' in out or 'ERROR:' in out or 'Error:' in out or num_errors != 0:

#Normalize line endings because Windows is dumb
out = out.replace('\r\n','\n')
out_lines = out.split('\n')

#First line of output is unittest's sequece of dots, E's and F's
if 'E' in out_lines[0] or 'F' in out_lines[0] or num_errors != 0:
if print_fails:
printTestBeginning("Running file %s - FAILED" % (pyFile))
print(out)
Expand Down
Binary file not shown.
71 changes: 71 additions & 0 deletions tests/features/instant_export_from_menu.test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import os
import unittest.mock
import bpy

from io_xplane2blender import xplane_constants
from io_xplane2blender.tests import *

#This folder is going to be messy with creating folders
#We use fakefilename to imitate what a user will go through
#in the file picking box
EXPORT_FOLDER = '../tmp'
nearly_blank_layers = [False] * 20

#Otherwise Blender won't accept a fully False array
nearly_blank_layers[19] = True

class TestInstantExportFromMenu(XPlaneTestCase):

def assert_file_exists(self,layer_num,relpath):
bpy.context.scene.layers = nearly_blank_layers
bpy.context.scene.layers[layer_num] = True
bpy.ops.export.xplane_obj(filepath=EXPORT_FOLDER + "/fakefilename")

path = os.path.abspath(
os.path.join(
os.path.dirname(bpy.context.blend_data.filepath),
relpath
)
)

self.assertTrue(os.path.isfile(path))


def test_ensure_append(self):
self.assert_file_exists(0,os.path.join(EXPORT_FOLDER, "ensure_append.obj"))

def test_ensure_no_append(self):
self.assert_file_exists(1,os.path.join(EXPORT_FOLDER, "ensure_no_double_append.obj"))

def test_ensure_no_abs_path_filename(self):
bpy.context.scene.layers = nearly_blank_layers
bpy.context.scene.layers[2] = True
bpy.ops.export.xplane_obj(filepath=EXPORT_FOLDER+'/fakefilename') #Must include a fakefilename to be removed later

self.assertEqual(len(logger.findErrors()), 1)
logger.clearMessages()

def test_ensure_paths_are_normalized_filename(self):
self.assert_file_exists(3, os.path.join(EXPORT_FOLDER,"ensure","paths","are","normalized","filename.obj"))

def test_ensure_blender_paths_resolve_filename(self):
#Check if file exists
self.assert_file_exists(4, os.path.join(EXPORT_FOLDER,"ensure","blender","paths","resolve","filename.obj"))


def test_ensure_lazy_paths_resolve_filename(self):
#Check if file exists
self.assert_file_exists(5, os.path.join(EXPORT_FOLDER,"ensure","lazy","paths","resolve","filename.obj"))

def test_ensure_dot_paths_are_created_filename(self):
#Check if file exists
self.assert_file_exists(6, os.path.join(EXPORT_FOLDER,"ensure","dot", "paths","resolve","filename.obj"))

def test_ensure_no_folder_named_filename(self):
bpy.context.scene.layers = nearly_blank_layers
bpy.context.scene.layers[7] = True
bpy.ops.export.xplane_obj(filepath=EXPORT_FOLDER +'/fakefilename') #Must include fake filename to be removed later

self.assertFalse(os.path.isdir(os.path.join(EXPORT_FOLDER,"ensure","no","folder","named","filename")))
#"""
runTestCases([TestInstantExportFromMenu])

0 comments on commit 7a96f1d

Please sign in to comment.