summaryrefslogtreecommitdiff
path: root/addons/gut/optparse.gd
diff options
context:
space:
mode:
Diffstat (limited to 'addons/gut/optparse.gd')
-rw-r--r--addons/gut/optparse.gd248
1 files changed, 248 insertions, 0 deletions
diff --git a/addons/gut/optparse.gd b/addons/gut/optparse.gd
new file mode 100644
index 0000000..290ed9d
--- /dev/null
+++ b/addons/gut/optparse.gd
@@ -0,0 +1,248 @@
+# ##############################################################################
+#(G)odot (U)nit (T)est class
+#
+# ##############################################################################
+# The MIT License (MIT)
+# =====================
+#
+# Copyright (c) 2020 Tom "Butch" Wesley
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# ##############################################################################
+# Description
+# -----------
+# Command line interface for the GUT unit testing tool. Allows you to run tests
+# from the command line instead of running a scene. Place this script along with
+# gut.gd into your scripts directory at the root of your project. Once there you
+# can run this script (from the root of your project) using the following command:
+# godot -s -d test/gut/gut_cmdln.gd
+#
+# See the readme for a list of options and examples. You can also use the -gh
+# option to get more information about how to use the command line interface.
+# ##############################################################################
+
+#-------------------------------------------------------------------------------
+# Parses the command line arguments supplied into an array that can then be
+# examined and parsed based on how the gut options work.
+#-------------------------------------------------------------------------------
+class CmdLineParser:
+ var _used_options = []
+ # an array of arrays. Each element in this array will contain an option
+ # name and if that option contains a value then it will have a sedond
+ # element. For example:
+ # [[-gselect, test.gd], [-gexit]]
+ var _opts = []
+
+ func _init():
+ for i in range(OS.get_cmdline_args().size()):
+ var opt_val = OS.get_cmdline_args()[i].split('=')
+ _opts.append(opt_val)
+
+ # Parse out multiple comma delimited values from a command line
+ # option. Values are separated from option name with "=" and
+ # additional values are comma separated.
+ func _parse_array_value(full_option):
+ var value = _parse_option_value(full_option)
+ var split = value.split(',')
+ return split
+
+ # Parse out the value of an option. Values are separated from
+ # the option name with "="
+ func _parse_option_value(full_option):
+ if(full_option.size() > 1):
+ return full_option[1]
+ else:
+ return null
+
+ # Search _opts for an element that starts with the option name
+ # specified.
+ func find_option(name):
+ var found = false
+ var idx = 0
+
+ while(idx < _opts.size() and !found):
+ if(_opts[idx][0] == name):
+ found = true
+ else:
+ idx += 1
+
+ if(found):
+ return idx
+ else:
+ return -1
+
+ func get_array_value(option):
+ _used_options.append(option)
+ var to_return = []
+ var opt_loc = find_option(option)
+ if(opt_loc != -1):
+ to_return = _parse_array_value(_opts[opt_loc])
+ _opts.remove(opt_loc)
+
+ return to_return
+
+ # returns the value of an option if it was specified, null otherwise. This
+ # used to return the default but that became problemnatic when trying to
+ # punch through the different places where values could be specified.
+ func get_value(option):
+ _used_options.append(option)
+ var to_return = null
+ var opt_loc = find_option(option)
+ if(opt_loc != -1):
+ to_return = _parse_option_value(_opts[opt_loc])
+ _opts.remove(opt_loc)
+
+ return to_return
+
+ # returns true if it finds the option, false if not.
+ func was_specified(option):
+ _used_options.append(option)
+ return find_option(option) != -1
+
+ # Returns any unused command line options. I found that only the -s and
+ # script name come through from godot, all other options that godot uses
+ # are not sent through OS.get_cmdline_args().
+ #
+ # This is a onetime thing b/c i kill all items in _used_options
+ func get_unused_options():
+ var to_return = []
+ for i in range(_opts.size()):
+ to_return.append(_opts[i][0])
+
+ var script_option = to_return.find('-s')
+ if script_option != -1:
+ to_return.remove(script_option + 1)
+ to_return.remove(script_option)
+
+ while(_used_options.size() > 0):
+ var index = to_return.find(_used_options[0].split("=")[0])
+ if(index != -1):
+ to_return.remove(index)
+ _used_options.remove(0)
+
+ return to_return
+
+#-------------------------------------------------------------------------------
+# Simple class to hold a command line option
+#-------------------------------------------------------------------------------
+class Option:
+ var value = null
+ var option_name = ''
+ var default = null
+ var description = ''
+
+ func _init(name, default_value, desc=''):
+ option_name = name
+ default = default_value
+ description = desc
+ value = null#default_value
+
+ func pad(to_pad, size, pad_with=' '):
+ var to_return = to_pad
+ for _i in range(to_pad.length(), size):
+ to_return += pad_with
+
+ return to_return
+
+ func to_s(min_space=0):
+ var subbed_desc = description
+ if(subbed_desc.find('[default]') != -1):
+ subbed_desc = subbed_desc.replace('[default]', str(default))
+ return pad(option_name, min_space) + subbed_desc
+
+#-------------------------------------------------------------------------------
+# The high level interface between this script and the command line options
+# supplied. Uses Option class and CmdLineParser to extract information from
+# the command line and make it easily accessible.
+#-------------------------------------------------------------------------------
+var options = []
+var _opts = []
+var _banner = ''
+
+func add(name, default, desc):
+ options.append(Option.new(name, default, desc))
+
+func get_value(name):
+ var found = false
+ var idx = 0
+
+ while(idx < options.size() and !found):
+ if(options[idx].option_name == name):
+ found = true
+ else:
+ idx += 1
+
+ if(found):
+ return options[idx].value
+ else:
+ print("COULD NOT FIND OPTION " + name)
+ return null
+
+func set_banner(banner):
+ _banner = banner
+
+func print_help():
+ var longest = 0
+ for i in range(options.size()):
+ if(options[i].option_name.length() > longest):
+ longest = options[i].option_name.length()
+
+ print('---------------------------------------------------------')
+ print(_banner)
+
+ print("\nOptions\n-------")
+ for i in range(options.size()):
+ print(' ' + options[i].to_s(longest + 2))
+ print('---------------------------------------------------------')
+
+func print_options():
+ for i in range(options.size()):
+ print(options[i].option_name + '=' + str(options[i].value))
+
+func parse():
+ var parser = CmdLineParser.new()
+
+ for i in range(options.size()):
+ var t = typeof(options[i].default)
+ # only set values that were specified at the command line so that
+ # we can punch through default and config values correctly later.
+ # Without this check, you can't tell the difference between the
+ # defaults and what was specified, so you can't punch through
+ # higher level options.
+ if(parser.was_specified(options[i].option_name)):
+ if(t == TYPE_INT):
+ options[i].value = int(parser.get_value(options[i].option_name))
+ elif(t == TYPE_STRING):
+ options[i].value = parser.get_value(options[i].option_name)
+ elif(t == TYPE_ARRAY):
+ options[i].value = parser.get_array_value(options[i].option_name)
+ elif(t == TYPE_BOOL):
+ options[i].value = parser.was_specified(options[i].option_name)
+ elif(t == TYPE_NIL):
+ print(options[i].option_name + ' cannot be processed, it has a nil datatype')
+ else:
+ print(options[i].option_name + ' cannot be processed, it has unknown datatype:' + str(t))
+
+ var unused = parser.get_unused_options()
+ if(unused.size() > 0):
+ print("Unrecognized options: ", unused)
+ return false
+
+ return true