remove unit test framework
This commit is contained in:
		
							parent
							
								
									61efafe9a3
								
							
						
					
					
						commit
						5828df43e2
					
				
					 29 changed files with 0 additions and 5720 deletions
				
			
		| 
						 | 
					@ -1,348 +0,0 @@
 | 
				
			||||||
extends Panel
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
onready var _script_list = $ScriptsList
 | 
					 | 
				
			||||||
onready var _nav = {
 | 
					 | 
				
			||||||
	prev = $Navigation/Previous,
 | 
					 | 
				
			||||||
	next = $Navigation/Next,
 | 
					 | 
				
			||||||
	run = $Navigation/Run,
 | 
					 | 
				
			||||||
	current_script = $Navigation/CurrentScript,
 | 
					 | 
				
			||||||
	show_scripts = $Navigation/ShowScripts
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
onready var _progress = {
 | 
					 | 
				
			||||||
	script = $ScriptProgress,
 | 
					 | 
				
			||||||
	test = $TestProgress
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
onready var _summary = {
 | 
					 | 
				
			||||||
	failing = $Summary/Failing,
 | 
					 | 
				
			||||||
	passing = $Summary/Passing
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
onready var _extras = $ExtraOptions
 | 
					 | 
				
			||||||
onready var _ignore_pauses = $ExtraOptions/IgnorePause
 | 
					 | 
				
			||||||
onready var _continue_button = $Continue/Continue
 | 
					 | 
				
			||||||
onready var _text_box = $TextDisplay/RichTextLabel
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
onready var _titlebar = {
 | 
					 | 
				
			||||||
	bar = $TitleBar,
 | 
					 | 
				
			||||||
	time = $TitleBar/Time,
 | 
					 | 
				
			||||||
	label = $TitleBar/Title
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _mouse = {
 | 
					 | 
				
			||||||
	down = false,
 | 
					 | 
				
			||||||
	in_title = false,
 | 
					 | 
				
			||||||
	down_pos = null,
 | 
					 | 
				
			||||||
	in_handle = false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
var _is_running = false
 | 
					 | 
				
			||||||
var _start_time = 0.0
 | 
					 | 
				
			||||||
var _time = 0.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const DEFAULT_TITLE = 'Gut: The Godot Unit Testing tool.'
 | 
					 | 
				
			||||||
var _utils = load('res://addons/gut/utils.gd').new()
 | 
					 | 
				
			||||||
var _text_box_blocker_enabled = true
 | 
					 | 
				
			||||||
var _pre_maximize_size = null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
signal end_pause
 | 
					 | 
				
			||||||
signal ignore_pause
 | 
					 | 
				
			||||||
signal log_level_changed
 | 
					 | 
				
			||||||
signal run_script
 | 
					 | 
				
			||||||
signal run_single_script
 | 
					 | 
				
			||||||
signal script_selected
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _ready():
 | 
					 | 
				
			||||||
	_pre_maximize_size = rect_size
 | 
					 | 
				
			||||||
	_hide_scripts()
 | 
					 | 
				
			||||||
	_update_controls()
 | 
					 | 
				
			||||||
	_nav.current_script.set_text("No scripts available")
 | 
					 | 
				
			||||||
	set_title()
 | 
					 | 
				
			||||||
	clear_summary()
 | 
					 | 
				
			||||||
	$TitleBar/Time.set_text("")
 | 
					 | 
				
			||||||
	$ExtraOptions/DisableBlocker.pressed = !_text_box_blocker_enabled
 | 
					 | 
				
			||||||
	_extras.visible = false
 | 
					 | 
				
			||||||
	update()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _process(delta):
 | 
					 | 
				
			||||||
	if(_is_running):
 | 
					 | 
				
			||||||
		_time = OS.get_unix_time() - _start_time
 | 
					 | 
				
			||||||
		var disp_time = round(_time * 100)/100
 | 
					 | 
				
			||||||
		$TitleBar/Time.set_text(str(disp_time))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _draw(): # needs get_size()
 | 
					 | 
				
			||||||
	# Draw the lines in the corner to show where you can
 | 
					 | 
				
			||||||
	# drag to resize the dialog
 | 
					 | 
				
			||||||
	var grab_margin = 3
 | 
					 | 
				
			||||||
	var line_space = 3
 | 
					 | 
				
			||||||
	var grab_line_color = Color(.4, .4, .4)
 | 
					 | 
				
			||||||
	for i in range(1, 10):
 | 
					 | 
				
			||||||
		var x = rect_size - Vector2(i * line_space, grab_margin)
 | 
					 | 
				
			||||||
		var y = rect_size - Vector2(grab_margin, i * line_space)
 | 
					 | 
				
			||||||
		draw_line(x, y, grab_line_color, 1, true)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _on_Maximize_draw():
 | 
					 | 
				
			||||||
	# draw the maximize square thing.
 | 
					 | 
				
			||||||
	var btn = $TitleBar/Maximize
 | 
					 | 
				
			||||||
	btn.set_text('')
 | 
					 | 
				
			||||||
	var w = btn.get_size().x
 | 
					 | 
				
			||||||
	var h = btn.get_size().y
 | 
					 | 
				
			||||||
	btn.draw_rect(Rect2(0, 0, w, h), Color(0, 0, 0, 1))
 | 
					 | 
				
			||||||
	btn.draw_rect(Rect2(2, 4, w - 4, h - 6), Color(1,1,1,1))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _on_ShowExtras_draw():
 | 
					 | 
				
			||||||
	var btn = $Continue/ShowExtras
 | 
					 | 
				
			||||||
	btn.set_text('')
 | 
					 | 
				
			||||||
	var start_x = 20
 | 
					 | 
				
			||||||
	var start_y = 15
 | 
					 | 
				
			||||||
	var pad = 5
 | 
					 | 
				
			||||||
	var color = Color(.1, .1, .1, 1)
 | 
					 | 
				
			||||||
	var width = 2
 | 
					 | 
				
			||||||
	for i in range(3):
 | 
					 | 
				
			||||||
		var y = start_y + pad * i
 | 
					 | 
				
			||||||
		btn.draw_line(Vector2(start_x, y), Vector2(btn.get_size().x - start_x, y), color, width, true)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ####################
 | 
					 | 
				
			||||||
# GUI Events
 | 
					 | 
				
			||||||
# ####################
 | 
					 | 
				
			||||||
func _on_Run_pressed():
 | 
					 | 
				
			||||||
	_run_mode()
 | 
					 | 
				
			||||||
	emit_signal('run_script', get_selected_index())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _on_CurrentScript_pressed():
 | 
					 | 
				
			||||||
	_run_mode()
 | 
					 | 
				
			||||||
	emit_signal('run_single_script', get_selected_index())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _on_Previous_pressed():
 | 
					 | 
				
			||||||
	_select_script(get_selected_index() - 1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _on_Next_pressed():
 | 
					 | 
				
			||||||
	_select_script(get_selected_index() + 1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _on_LogLevelSlider_value_changed(value):
 | 
					 | 
				
			||||||
	emit_signal('log_level_changed', $LogLevelSlider.value)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _on_Continue_pressed():
 | 
					 | 
				
			||||||
	_continue_button.disabled = true
 | 
					 | 
				
			||||||
	emit_signal('end_pause')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _on_IgnorePause_pressed():
 | 
					 | 
				
			||||||
	var checked = _ignore_pauses.is_pressed()
 | 
					 | 
				
			||||||
	emit_signal('ignore_pause', checked)
 | 
					 | 
				
			||||||
	if(checked):
 | 
					 | 
				
			||||||
		emit_signal('end_pause')
 | 
					 | 
				
			||||||
		_continue_button.disabled = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _on_ShowScripts_pressed():
 | 
					 | 
				
			||||||
	_toggle_scripts()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _on_ScriptsList_item_selected(index):
 | 
					 | 
				
			||||||
	_select_script(index)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _on_TitleBar_mouse_entered():
 | 
					 | 
				
			||||||
	_mouse.in_title = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _on_TitleBar_mouse_exited():
 | 
					 | 
				
			||||||
	_mouse.in_title = false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _input(event):
 | 
					 | 
				
			||||||
	if(event is InputEventMouseButton):
 | 
					 | 
				
			||||||
		if(event.button_index == 1):
 | 
					 | 
				
			||||||
			_mouse.down = event.pressed
 | 
					 | 
				
			||||||
			if(_mouse.down):
 | 
					 | 
				
			||||||
				_mouse.down_pos = event.position
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(_mouse.in_title):
 | 
					 | 
				
			||||||
		if(event is InputEventMouseMotion and _mouse.down):
 | 
					 | 
				
			||||||
			set_position(get_position() + (event.position - _mouse.down_pos))
 | 
					 | 
				
			||||||
			_mouse.down_pos = event.position
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(_mouse.in_handle):
 | 
					 | 
				
			||||||
		if(event is InputEventMouseMotion and _mouse.down):
 | 
					 | 
				
			||||||
			var new_size = rect_size + event.position - _mouse.down_pos
 | 
					 | 
				
			||||||
			var new_mouse_down_pos = event.position
 | 
					 | 
				
			||||||
			rect_size = new_size
 | 
					 | 
				
			||||||
			_mouse.down_pos = new_mouse_down_pos
 | 
					 | 
				
			||||||
			_pre_maximize_size = rect_size
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _on_ResizeHandle_mouse_entered():
 | 
					 | 
				
			||||||
	_mouse.in_handle = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _on_ResizeHandle_mouse_exited():
 | 
					 | 
				
			||||||
	_mouse.in_handle = false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Send scroll type events through to the text box
 | 
					 | 
				
			||||||
func _on_FocusBlocker_gui_input(ev):
 | 
					 | 
				
			||||||
	if(_text_box_blocker_enabled):
 | 
					 | 
				
			||||||
		if(ev is InputEventPanGesture):
 | 
					 | 
				
			||||||
			get_text_box()._gui_input(ev)
 | 
					 | 
				
			||||||
		# convert a drag into a pan gesture so it scrolls.
 | 
					 | 
				
			||||||
		elif(ev is InputEventScreenDrag):
 | 
					 | 
				
			||||||
			var converted = InputEventPanGesture.new()
 | 
					 | 
				
			||||||
			converted.delta = Vector2(0, ev.relative.y)
 | 
					 | 
				
			||||||
			converted.position = Vector2(0, 0)
 | 
					 | 
				
			||||||
			get_text_box()._gui_input(converted)
 | 
					 | 
				
			||||||
		elif(ev is InputEventMouseButton and (ev.button_index == BUTTON_WHEEL_DOWN or ev.button_index == BUTTON_WHEEL_UP)):
 | 
					 | 
				
			||||||
			get_text_box()._gui_input(ev)
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		get_text_box()._gui_input(ev)
 | 
					 | 
				
			||||||
		print(ev)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _on_RichTextLabel_gui_input(ev):
 | 
					 | 
				
			||||||
	pass
 | 
					 | 
				
			||||||
	# leaving this b/c it is wired up and might have to send
 | 
					 | 
				
			||||||
	# more signals through
 | 
					 | 
				
			||||||
	print(ev)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _on_Copy_pressed():
 | 
					 | 
				
			||||||
	_text_box.select_all()
 | 
					 | 
				
			||||||
	_text_box.copy()
 | 
					 | 
				
			||||||
	_text_box.deselect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _on_DisableBlocker_toggled(button_pressed):
 | 
					 | 
				
			||||||
	_text_box_blocker_enabled = !button_pressed
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _on_ShowExtras_toggled(button_pressed):
 | 
					 | 
				
			||||||
	_extras.visible = button_pressed
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _on_Maximize_pressed():
 | 
					 | 
				
			||||||
	if(rect_size == _pre_maximize_size):
 | 
					 | 
				
			||||||
		maximize()
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		rect_size = _pre_maximize_size
 | 
					 | 
				
			||||||
# ####################
 | 
					 | 
				
			||||||
# Private
 | 
					 | 
				
			||||||
# ####################
 | 
					 | 
				
			||||||
func _run_mode(is_running=true):
 | 
					 | 
				
			||||||
	if(is_running):
 | 
					 | 
				
			||||||
		_start_time = OS.get_unix_time()
 | 
					 | 
				
			||||||
		_time = _start_time
 | 
					 | 
				
			||||||
		_summary.failing.set_text("0")
 | 
					 | 
				
			||||||
		_summary.passing.set_text("0")
 | 
					 | 
				
			||||||
	_is_running = is_running
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_hide_scripts()
 | 
					 | 
				
			||||||
	var ctrls = $Navigation.get_children()
 | 
					 | 
				
			||||||
	for i in range(ctrls.size()):
 | 
					 | 
				
			||||||
		ctrls[i].disabled = is_running
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _select_script(index):
 | 
					 | 
				
			||||||
	$Navigation/CurrentScript.set_text(_script_list.get_item_text(index))
 | 
					 | 
				
			||||||
	_script_list.select(index)
 | 
					 | 
				
			||||||
	_update_controls()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _toggle_scripts():
 | 
					 | 
				
			||||||
	if(_script_list.visible):
 | 
					 | 
				
			||||||
		_hide_scripts()
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		_show_scripts()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _show_scripts():
 | 
					 | 
				
			||||||
	_script_list.show()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _hide_scripts():
 | 
					 | 
				
			||||||
	_script_list.hide()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _update_controls():
 | 
					 | 
				
			||||||
	var is_empty = _script_list.get_selected_items().size() == 0
 | 
					 | 
				
			||||||
	if(is_empty):
 | 
					 | 
				
			||||||
		_nav.next.disabled = true
 | 
					 | 
				
			||||||
		_nav.prev.disabled = true
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		var index = get_selected_index()
 | 
					 | 
				
			||||||
		_nav.prev.disabled = index <= 0
 | 
					 | 
				
			||||||
		_nav.next.disabled = index >= _script_list.get_item_count() - 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_nav.run.disabled = is_empty
 | 
					 | 
				
			||||||
	_nav.current_script.disabled = is_empty
 | 
					 | 
				
			||||||
	_nav.show_scripts.disabled = is_empty
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ####################
 | 
					 | 
				
			||||||
# Public
 | 
					 | 
				
			||||||
# ####################
 | 
					 | 
				
			||||||
func run_mode(is_running=true):
 | 
					 | 
				
			||||||
	_run_mode(is_running)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_scripts(scripts):
 | 
					 | 
				
			||||||
	_script_list.clear()
 | 
					 | 
				
			||||||
	for i in range(scripts.size()):
 | 
					 | 
				
			||||||
		_script_list.add_item(scripts[i])
 | 
					 | 
				
			||||||
	_select_script(0)
 | 
					 | 
				
			||||||
	_update_controls()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func select_script(index):
 | 
					 | 
				
			||||||
	_select_script(index)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_selected_index():
 | 
					 | 
				
			||||||
	return _script_list.get_selected_items()[0]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_log_level():
 | 
					 | 
				
			||||||
	return $LogLevelSlider.value
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_log_level(value):
 | 
					 | 
				
			||||||
	$LogLevelSlider.value = _utils.nvl(value, 0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_ignore_pause(should):
 | 
					 | 
				
			||||||
	_ignore_pauses.pressed = should
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_ignore_pause():
 | 
					 | 
				
			||||||
	return _ignore_pauses.pressed
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_text_box():
 | 
					 | 
				
			||||||
	return $TextDisplay/RichTextLabel
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func end_run():
 | 
					 | 
				
			||||||
	_run_mode(false)
 | 
					 | 
				
			||||||
	_update_controls()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_progress_script_max(value):
 | 
					 | 
				
			||||||
	_progress.script.set_max(value)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_progress_script_value(value):
 | 
					 | 
				
			||||||
	_progress.script.set_value(value)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_progress_test_max(value):
 | 
					 | 
				
			||||||
	_progress.test.set_max(value)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_progress_test_value(value):
 | 
					 | 
				
			||||||
	_progress.test.set_value(value)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func clear_progress():
 | 
					 | 
				
			||||||
	_progress.test.set_value(0)
 | 
					 | 
				
			||||||
	_progress.script.set_value(0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func pause():
 | 
					 | 
				
			||||||
	print('we got here')
 | 
					 | 
				
			||||||
	_continue_button.disabled = false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_title(title=null):
 | 
					 | 
				
			||||||
	if(title == null):
 | 
					 | 
				
			||||||
		$TitleBar/Title.set_text(DEFAULT_TITLE)
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		$TitleBar/Title.set_text(title)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_run_duration():
 | 
					 | 
				
			||||||
	return $TitleBar/Time.text.to_float()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func add_passing(amount=1):
 | 
					 | 
				
			||||||
	if(!_summary):
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	_summary.passing.set_text(str(_summary.passing.get_text().to_int() + amount))
 | 
					 | 
				
			||||||
	$Summary.show()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func add_failing(amount=1):
 | 
					 | 
				
			||||||
	if(!_summary):
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	_summary.failing.set_text(str(_summary.failing.get_text().to_int() + amount))
 | 
					 | 
				
			||||||
	$Summary.show()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func clear_summary():
 | 
					 | 
				
			||||||
	_summary.passing.set_text("0")
 | 
					 | 
				
			||||||
	_summary.failing.set_text("0")
 | 
					 | 
				
			||||||
	$Summary.hide()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func maximize():
 | 
					 | 
				
			||||||
	if(is_inside_tree()):
 | 
					 | 
				
			||||||
		var vp_size_offset = get_viewport().size
 | 
					 | 
				
			||||||
		rect_size = vp_size_offset / get_scale()
 | 
					 | 
				
			||||||
		set_position(Vector2(0, 0))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,306 +0,0 @@
 | 
				
			||||||
[gd_scene load_steps=5 format=2]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[ext_resource path="res://addons/gut/GutScene.gd" type="Script" id=1]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[sub_resource type="StyleBoxFlat" id=1]
 | 
					 | 
				
			||||||
bg_color = Color( 0.193863, 0.205501, 0.214844, 1 )
 | 
					 | 
				
			||||||
corner_radius_top_left = 20
 | 
					 | 
				
			||||||
corner_radius_top_right = 20
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[sub_resource type="StyleBoxFlat" id=2]
 | 
					 | 
				
			||||||
bg_color = Color( 1, 1, 1, 1 )
 | 
					 | 
				
			||||||
border_color = Color( 0, 0, 0, 1 )
 | 
					 | 
				
			||||||
corner_radius_top_left = 5
 | 
					 | 
				
			||||||
corner_radius_top_right = 5
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[sub_resource type="Theme" id=3]
 | 
					 | 
				
			||||||
resource_local_to_scene = true
 | 
					 | 
				
			||||||
Panel/styles/panel = SubResource( 2 )
 | 
					 | 
				
			||||||
Panel/styles/panelf = null
 | 
					 | 
				
			||||||
Panel/styles/panelnc = null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="Gut" type="Panel"]
 | 
					 | 
				
			||||||
margin_right = 740.0
 | 
					 | 
				
			||||||
margin_bottom = 320.0
 | 
					 | 
				
			||||||
rect_min_size = Vector2( 740, 250 )
 | 
					 | 
				
			||||||
custom_styles/panel = SubResource( 1 )
 | 
					 | 
				
			||||||
script = ExtResource( 1 )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="TitleBar" type="Panel" parent="."]
 | 
					 | 
				
			||||||
anchor_right = 1.0
 | 
					 | 
				
			||||||
margin_bottom = 40.0
 | 
					 | 
				
			||||||
theme = SubResource( 3 )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="Title" type="Label" parent="TitleBar"]
 | 
					 | 
				
			||||||
anchor_right = 1.0
 | 
					 | 
				
			||||||
margin_bottom = 40.0
 | 
					 | 
				
			||||||
custom_colors/font_color = Color( 0, 0, 0, 1 )
 | 
					 | 
				
			||||||
text = "Gut"
 | 
					 | 
				
			||||||
align = 1
 | 
					 | 
				
			||||||
valign = 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="Time" type="Label" parent="TitleBar"]
 | 
					 | 
				
			||||||
anchor_left = 1.0
 | 
					 | 
				
			||||||
anchor_right = 1.0
 | 
					 | 
				
			||||||
margin_left = -114.0
 | 
					 | 
				
			||||||
margin_right = -53.0
 | 
					 | 
				
			||||||
margin_bottom = 40.0
 | 
					 | 
				
			||||||
custom_colors/font_color = Color( 0, 0, 0, 1 )
 | 
					 | 
				
			||||||
text = "9999.99"
 | 
					 | 
				
			||||||
valign = 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="Maximize" type="Button" parent="TitleBar"]
 | 
					 | 
				
			||||||
anchor_left = 1.0
 | 
					 | 
				
			||||||
anchor_right = 1.0
 | 
					 | 
				
			||||||
margin_left = -30.0
 | 
					 | 
				
			||||||
margin_top = 10.0
 | 
					 | 
				
			||||||
margin_right = -6.0
 | 
					 | 
				
			||||||
margin_bottom = 30.0
 | 
					 | 
				
			||||||
custom_colors/font_color = Color( 0, 0, 0, 1 )
 | 
					 | 
				
			||||||
text = "M"
 | 
					 | 
				
			||||||
flat = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="ScriptProgress" type="ProgressBar" parent="."]
 | 
					 | 
				
			||||||
editor/display_folded = true
 | 
					 | 
				
			||||||
anchor_top = 1.0
 | 
					 | 
				
			||||||
anchor_bottom = 1.0
 | 
					 | 
				
			||||||
margin_left = 70.0
 | 
					 | 
				
			||||||
margin_top = -100.0
 | 
					 | 
				
			||||||
margin_right = 180.0
 | 
					 | 
				
			||||||
margin_bottom = -70.0
 | 
					 | 
				
			||||||
step = 1.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="Label" type="Label" parent="ScriptProgress"]
 | 
					 | 
				
			||||||
margin_left = -70.0
 | 
					 | 
				
			||||||
margin_right = -10.0
 | 
					 | 
				
			||||||
margin_bottom = 24.0
 | 
					 | 
				
			||||||
text = "Script"
 | 
					 | 
				
			||||||
align = 1
 | 
					 | 
				
			||||||
valign = 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="TestProgress" type="ProgressBar" parent="."]
 | 
					 | 
				
			||||||
editor/display_folded = true
 | 
					 | 
				
			||||||
anchor_top = 1.0
 | 
					 | 
				
			||||||
anchor_bottom = 1.0
 | 
					 | 
				
			||||||
margin_left = 70.0
 | 
					 | 
				
			||||||
margin_top = -70.0
 | 
					 | 
				
			||||||
margin_right = 180.0
 | 
					 | 
				
			||||||
margin_bottom = -40.0
 | 
					 | 
				
			||||||
step = 1.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="Label" type="Label" parent="TestProgress"]
 | 
					 | 
				
			||||||
margin_left = -70.0
 | 
					 | 
				
			||||||
margin_right = -10.0
 | 
					 | 
				
			||||||
margin_bottom = 24.0
 | 
					 | 
				
			||||||
text = "Tests"
 | 
					 | 
				
			||||||
align = 1
 | 
					 | 
				
			||||||
valign = 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="TextDisplay" type="Panel" parent="."]
 | 
					 | 
				
			||||||
editor/display_folded = true
 | 
					 | 
				
			||||||
anchor_right = 1.0
 | 
					 | 
				
			||||||
anchor_bottom = 1.0
 | 
					 | 
				
			||||||
margin_top = 40.0
 | 
					 | 
				
			||||||
margin_bottom = -107.0
 | 
					 | 
				
			||||||
__meta__ = {
 | 
					 | 
				
			||||||
"_edit_group_": true
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="RichTextLabel" type="TextEdit" parent="TextDisplay"]
 | 
					 | 
				
			||||||
anchor_right = 1.0
 | 
					 | 
				
			||||||
anchor_bottom = 1.0
 | 
					 | 
				
			||||||
mouse_default_cursor_shape = 0
 | 
					 | 
				
			||||||
readonly = true
 | 
					 | 
				
			||||||
syntax_highlighting = true
 | 
					 | 
				
			||||||
smooth_scrolling = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="FocusBlocker" type="Panel" parent="TextDisplay"]
 | 
					 | 
				
			||||||
self_modulate = Color( 1, 1, 1, 0 )
 | 
					 | 
				
			||||||
anchor_right = 1.0
 | 
					 | 
				
			||||||
anchor_bottom = 1.0
 | 
					 | 
				
			||||||
margin_right = -10.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="Navigation" type="Panel" parent="."]
 | 
					 | 
				
			||||||
editor/display_folded = true
 | 
					 | 
				
			||||||
self_modulate = Color( 1, 1, 1, 0 )
 | 
					 | 
				
			||||||
anchor_top = 1.0
 | 
					 | 
				
			||||||
anchor_bottom = 1.0
 | 
					 | 
				
			||||||
margin_left = 220.0
 | 
					 | 
				
			||||||
margin_top = -100.0
 | 
					 | 
				
			||||||
margin_right = 580.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="Previous" type="Button" parent="Navigation"]
 | 
					 | 
				
			||||||
margin_left = -30.0
 | 
					 | 
				
			||||||
margin_right = 50.0
 | 
					 | 
				
			||||||
margin_bottom = 40.0
 | 
					 | 
				
			||||||
text = "<"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="Next" type="Button" parent="Navigation"]
 | 
					 | 
				
			||||||
margin_left = 230.0
 | 
					 | 
				
			||||||
margin_right = 310.0
 | 
					 | 
				
			||||||
margin_bottom = 40.0
 | 
					 | 
				
			||||||
text = ">"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="Run" type="Button" parent="Navigation"]
 | 
					 | 
				
			||||||
margin_left = 60.0
 | 
					 | 
				
			||||||
margin_right = 220.0
 | 
					 | 
				
			||||||
margin_bottom = 40.0
 | 
					 | 
				
			||||||
text = "Run"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="CurrentScript" type="Button" parent="Navigation"]
 | 
					 | 
				
			||||||
margin_left = -30.0
 | 
					 | 
				
			||||||
margin_top = 50.0
 | 
					 | 
				
			||||||
margin_right = 310.0
 | 
					 | 
				
			||||||
margin_bottom = 90.0
 | 
					 | 
				
			||||||
text = "res://test/unit/test_gut.gd"
 | 
					 | 
				
			||||||
clip_text = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="ShowScripts" type="Button" parent="Navigation"]
 | 
					 | 
				
			||||||
margin_left = 320.0
 | 
					 | 
				
			||||||
margin_top = 50.0
 | 
					 | 
				
			||||||
margin_right = 360.0
 | 
					 | 
				
			||||||
margin_bottom = 90.0
 | 
					 | 
				
			||||||
text = "..."
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="LogLevelSlider" type="HSlider" parent="."]
 | 
					 | 
				
			||||||
editor/display_folded = true
 | 
					 | 
				
			||||||
anchor_top = 1.0
 | 
					 | 
				
			||||||
anchor_bottom = 1.0
 | 
					 | 
				
			||||||
margin_left = 80.0
 | 
					 | 
				
			||||||
margin_top = -40.0
 | 
					 | 
				
			||||||
margin_right = 130.0
 | 
					 | 
				
			||||||
margin_bottom = -20.0
 | 
					 | 
				
			||||||
rect_scale = Vector2( 2, 2 )
 | 
					 | 
				
			||||||
max_value = 2.0
 | 
					 | 
				
			||||||
tick_count = 3
 | 
					 | 
				
			||||||
ticks_on_borders = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="Label" type="Label" parent="LogLevelSlider"]
 | 
					 | 
				
			||||||
margin_left = -35.0
 | 
					 | 
				
			||||||
margin_top = 5.0
 | 
					 | 
				
			||||||
margin_right = 25.0
 | 
					 | 
				
			||||||
margin_bottom = 25.0
 | 
					 | 
				
			||||||
rect_scale = Vector2( 0.5, 0.5 )
 | 
					 | 
				
			||||||
text = "Log Level"
 | 
					 | 
				
			||||||
align = 1
 | 
					 | 
				
			||||||
valign = 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="ScriptsList" type="ItemList" parent="."]
 | 
					 | 
				
			||||||
anchor_bottom = 1.0
 | 
					 | 
				
			||||||
margin_left = 180.0
 | 
					 | 
				
			||||||
margin_top = 40.0
 | 
					 | 
				
			||||||
margin_right = 620.0
 | 
					 | 
				
			||||||
margin_bottom = -108.0
 | 
					 | 
				
			||||||
allow_reselect = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="ExtraOptions" type="Panel" parent="."]
 | 
					 | 
				
			||||||
anchor_left = 1.0
 | 
					 | 
				
			||||||
anchor_top = 1.0
 | 
					 | 
				
			||||||
anchor_right = 1.0
 | 
					 | 
				
			||||||
anchor_bottom = 1.0
 | 
					 | 
				
			||||||
margin_left = -210.0
 | 
					 | 
				
			||||||
margin_top = -246.0
 | 
					 | 
				
			||||||
margin_bottom = -106.0
 | 
					 | 
				
			||||||
custom_styles/panel = SubResource( 1 )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="IgnorePause" type="CheckBox" parent="ExtraOptions"]
 | 
					 | 
				
			||||||
margin_left = 10.0
 | 
					 | 
				
			||||||
margin_top = 10.0
 | 
					 | 
				
			||||||
margin_right = 128.0
 | 
					 | 
				
			||||||
margin_bottom = 34.0
 | 
					 | 
				
			||||||
rect_scale = Vector2( 1.5, 1.5 )
 | 
					 | 
				
			||||||
text = "Ignore Pauses"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="DisableBlocker" type="CheckBox" parent="ExtraOptions"]
 | 
					 | 
				
			||||||
margin_left = 10.0
 | 
					 | 
				
			||||||
margin_top = 50.0
 | 
					 | 
				
			||||||
margin_right = 130.0
 | 
					 | 
				
			||||||
margin_bottom = 74.0
 | 
					 | 
				
			||||||
rect_scale = Vector2( 1.5, 1.5 )
 | 
					 | 
				
			||||||
text = "Selectable"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="Copy" type="Button" parent="ExtraOptions"]
 | 
					 | 
				
			||||||
margin_left = 20.0
 | 
					 | 
				
			||||||
margin_top = 90.0
 | 
					 | 
				
			||||||
margin_right = 200.0
 | 
					 | 
				
			||||||
margin_bottom = 130.0
 | 
					 | 
				
			||||||
text = "Copy"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="ResizeHandle" type="Control" parent="."]
 | 
					 | 
				
			||||||
anchor_left = 1.0
 | 
					 | 
				
			||||||
anchor_top = 1.0
 | 
					 | 
				
			||||||
anchor_right = 1.0
 | 
					 | 
				
			||||||
anchor_bottom = 1.0
 | 
					 | 
				
			||||||
margin_left = -40.0
 | 
					 | 
				
			||||||
margin_top = -40.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="Continue" type="Panel" parent="."]
 | 
					 | 
				
			||||||
self_modulate = Color( 1, 1, 1, 0 )
 | 
					 | 
				
			||||||
anchor_left = 1.0
 | 
					 | 
				
			||||||
anchor_top = 1.0
 | 
					 | 
				
			||||||
anchor_right = 1.0
 | 
					 | 
				
			||||||
anchor_bottom = 1.0
 | 
					 | 
				
			||||||
margin_left = -150.0
 | 
					 | 
				
			||||||
margin_top = -100.0
 | 
					 | 
				
			||||||
margin_right = -30.0
 | 
					 | 
				
			||||||
margin_bottom = -10.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="Continue" type="Button" parent="Continue"]
 | 
					 | 
				
			||||||
margin_top = 50.0
 | 
					 | 
				
			||||||
margin_right = 119.0
 | 
					 | 
				
			||||||
margin_bottom = 90.0
 | 
					 | 
				
			||||||
disabled = true
 | 
					 | 
				
			||||||
text = "Continue"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="ShowExtras" type="Button" parent="Continue"]
 | 
					 | 
				
			||||||
margin_left = 50.0
 | 
					 | 
				
			||||||
margin_right = 120.0
 | 
					 | 
				
			||||||
margin_bottom = 40.0
 | 
					 | 
				
			||||||
rect_pivot_offset = Vector2( 35, 20 )
 | 
					 | 
				
			||||||
toggle_mode = true
 | 
					 | 
				
			||||||
text = "_"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="Summary" type="Node2D" parent="."]
 | 
					 | 
				
			||||||
editor/display_folded = true
 | 
					 | 
				
			||||||
position = Vector2( 0, 3 )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="Passing" type="Label" parent="Summary"]
 | 
					 | 
				
			||||||
margin_top = 10.0
 | 
					 | 
				
			||||||
margin_right = 40.0
 | 
					 | 
				
			||||||
margin_bottom = 24.0
 | 
					 | 
				
			||||||
custom_colors/font_color = Color( 0, 0, 0, 1 )
 | 
					 | 
				
			||||||
text = "0"
 | 
					 | 
				
			||||||
align = 1
 | 
					 | 
				
			||||||
valign = 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="Failing" type="Label" parent="Summary"]
 | 
					 | 
				
			||||||
margin_left = 40.0
 | 
					 | 
				
			||||||
margin_top = 10.0
 | 
					 | 
				
			||||||
margin_right = 80.0
 | 
					 | 
				
			||||||
margin_bottom = 24.0
 | 
					 | 
				
			||||||
custom_colors/font_color = Color( 0, 0, 0, 1 )
 | 
					 | 
				
			||||||
text = "0"
 | 
					 | 
				
			||||||
align = 1
 | 
					 | 
				
			||||||
valign = 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[connection signal="mouse_entered" from="TitleBar" to="." method="_on_TitleBar_mouse_entered"]
 | 
					 | 
				
			||||||
[connection signal="mouse_exited" from="TitleBar" to="." method="_on_TitleBar_mouse_exited"]
 | 
					 | 
				
			||||||
[connection signal="draw" from="TitleBar/Maximize" to="." method="_on_Maximize_draw"]
 | 
					 | 
				
			||||||
[connection signal="pressed" from="TitleBar/Maximize" to="." method="_on_Maximize_pressed"]
 | 
					 | 
				
			||||||
[connection signal="gui_input" from="TextDisplay/RichTextLabel" to="." method="_on_RichTextLabel_gui_input"]
 | 
					 | 
				
			||||||
[connection signal="gui_input" from="TextDisplay/FocusBlocker" to="." method="_on_FocusBlocker_gui_input"]
 | 
					 | 
				
			||||||
[connection signal="pressed" from="Navigation/Previous" to="." method="_on_Previous_pressed"]
 | 
					 | 
				
			||||||
[connection signal="pressed" from="Navigation/Next" to="." method="_on_Next_pressed"]
 | 
					 | 
				
			||||||
[connection signal="pressed" from="Navigation/Run" to="." method="_on_Run_pressed"]
 | 
					 | 
				
			||||||
[connection signal="pressed" from="Navigation/CurrentScript" to="." method="_on_CurrentScript_pressed"]
 | 
					 | 
				
			||||||
[connection signal="pressed" from="Navigation/ShowScripts" to="." method="_on_ShowScripts_pressed"]
 | 
					 | 
				
			||||||
[connection signal="value_changed" from="LogLevelSlider" to="." method="_on_LogLevelSlider_value_changed"]
 | 
					 | 
				
			||||||
[connection signal="item_selected" from="ScriptsList" to="." method="_on_ScriptsList_item_selected"]
 | 
					 | 
				
			||||||
[connection signal="pressed" from="ExtraOptions/IgnorePause" to="." method="_on_IgnorePause_pressed"]
 | 
					 | 
				
			||||||
[connection signal="toggled" from="ExtraOptions/DisableBlocker" to="." method="_on_DisableBlocker_toggled"]
 | 
					 | 
				
			||||||
[connection signal="pressed" from="ExtraOptions/Copy" to="." method="_on_Copy_pressed"]
 | 
					 | 
				
			||||||
[connection signal="mouse_entered" from="ResizeHandle" to="." method="_on_ResizeHandle_mouse_entered"]
 | 
					 | 
				
			||||||
[connection signal="mouse_exited" from="ResizeHandle" to="." method="_on_ResizeHandle_mouse_exited"]
 | 
					 | 
				
			||||||
[connection signal="pressed" from="Continue/Continue" to="." method="_on_Continue_pressed"]
 | 
					 | 
				
			||||||
[connection signal="draw" from="Continue/ShowExtras" to="." method="_on_ShowExtras_draw"]
 | 
					 | 
				
			||||||
[connection signal="toggled" from="Continue/ShowExtras" to="." method="_on_ShowExtras_toggled"]
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,22 +0,0 @@
 | 
				
			||||||
The MIT License (MIT)
 | 
					 | 
				
			||||||
=====================
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Copyright (c) 2018 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.
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,487 +0,0 @@
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
# Utility class to hold the local and built in methods separately.  Add all local
 | 
					 | 
				
			||||||
# methods FIRST, then add built ins.
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
class ScriptMethods:
 | 
					 | 
				
			||||||
	# List of methods that should not be overloaded when they are not defined
 | 
					 | 
				
			||||||
	# in the class being doubled.  These either break things if they are
 | 
					 | 
				
			||||||
	# overloaded or do not have a "super" equivalent so we can't just pass
 | 
					 | 
				
			||||||
	# through.
 | 
					 | 
				
			||||||
	var _blacklist = [
 | 
					 | 
				
			||||||
		'has_method',
 | 
					 | 
				
			||||||
		'get_script',
 | 
					 | 
				
			||||||
		'get',
 | 
					 | 
				
			||||||
		'_notification',
 | 
					 | 
				
			||||||
		'get_path',
 | 
					 | 
				
			||||||
		'_enter_tree',
 | 
					 | 
				
			||||||
		'_exit_tree',
 | 
					 | 
				
			||||||
		'_process',
 | 
					 | 
				
			||||||
		'_draw',
 | 
					 | 
				
			||||||
		'_physics_process',
 | 
					 | 
				
			||||||
		'_input',
 | 
					 | 
				
			||||||
		'_unhandled_input',
 | 
					 | 
				
			||||||
		'_unhandled_key_input',
 | 
					 | 
				
			||||||
		'_set',
 | 
					 | 
				
			||||||
		'_get', # probably
 | 
					 | 
				
			||||||
		'emit_signal', # can't handle extra parameters to be sent with signal.
 | 
					 | 
				
			||||||
		'draw_mesh', # issue with one parameter, value is `Null((..), (..), (..))``
 | 
					 | 
				
			||||||
		'_to_string', # nonexistant function ._to_string
 | 
					 | 
				
			||||||
	]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var built_ins = []
 | 
					 | 
				
			||||||
	var local_methods = []
 | 
					 | 
				
			||||||
	var _method_names = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func is_blacklisted(method_meta):
 | 
					 | 
				
			||||||
		return _blacklist.find(method_meta.name) != -1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func _add_name_if_does_not_have(method_name):
 | 
					 | 
				
			||||||
		var should_add = _method_names.find(method_name) == -1
 | 
					 | 
				
			||||||
		if(should_add):
 | 
					 | 
				
			||||||
			_method_names.append(method_name)
 | 
					 | 
				
			||||||
		return should_add
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func add_built_in_method(method_meta):
 | 
					 | 
				
			||||||
		var did_add = _add_name_if_does_not_have(method_meta.name)
 | 
					 | 
				
			||||||
		if(did_add and !is_blacklisted(method_meta)):
 | 
					 | 
				
			||||||
			built_ins.append(method_meta)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func add_local_method(method_meta):
 | 
					 | 
				
			||||||
		var did_add = _add_name_if_does_not_have(method_meta.name)
 | 
					 | 
				
			||||||
		if(did_add):
 | 
					 | 
				
			||||||
			local_methods.append(method_meta)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func to_s():
 | 
					 | 
				
			||||||
		var text = "Locals\n"
 | 
					 | 
				
			||||||
		for i in range(local_methods.size()):
 | 
					 | 
				
			||||||
			text += str("  ", local_methods[i].name, "\n")
 | 
					 | 
				
			||||||
		text += "Built-Ins\n"
 | 
					 | 
				
			||||||
		for i in range(built_ins.size()):
 | 
					 | 
				
			||||||
			text += str("  ", built_ins[i].name, "\n")
 | 
					 | 
				
			||||||
		return text
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
# Helper class to deal with objects and inner classes.
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
class ObjectInfo:
 | 
					 | 
				
			||||||
	var _path = null
 | 
					 | 
				
			||||||
	var _subpaths = []
 | 
					 | 
				
			||||||
	var _utils = load('res://addons/gut/utils.gd').new()
 | 
					 | 
				
			||||||
	var _method_strategy = null
 | 
					 | 
				
			||||||
	var make_partial_double = false
 | 
					 | 
				
			||||||
	var scene_path = null
 | 
					 | 
				
			||||||
	var _native_class = null
 | 
					 | 
				
			||||||
	var _native_class_instance = null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func _init(path, subpath=null):
 | 
					 | 
				
			||||||
		_path = path
 | 
					 | 
				
			||||||
		if(subpath != null):
 | 
					 | 
				
			||||||
			_subpaths = _utils.split_string(subpath, '/')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	# Returns an instance of the class/inner class
 | 
					 | 
				
			||||||
	func instantiate():
 | 
					 | 
				
			||||||
		var to_return = null
 | 
					 | 
				
			||||||
		if(is_native()):
 | 
					 | 
				
			||||||
			to_return = _native_class.new()
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			to_return = get_loaded_class().new()
 | 
					 | 
				
			||||||
		return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	# Can't call it get_class because that is reserved so it gets this ugly name.
 | 
					 | 
				
			||||||
	# Loads up the class and then any inner classes to give back a reference to
 | 
					 | 
				
			||||||
	# the desired Inner class (if there is any)
 | 
					 | 
				
			||||||
	func get_loaded_class():
 | 
					 | 
				
			||||||
		var LoadedClass = load(_path)
 | 
					 | 
				
			||||||
		for i in range(_subpaths.size()):
 | 
					 | 
				
			||||||
			LoadedClass = LoadedClass.get(_subpaths[i])
 | 
					 | 
				
			||||||
		return LoadedClass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func to_s():
 | 
					 | 
				
			||||||
		return str(_path, '[', get_subpath(), ']')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func get_path():
 | 
					 | 
				
			||||||
		return _path
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func get_subpath():
 | 
					 | 
				
			||||||
		return _utils.join_array(_subpaths, '/')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func has_subpath():
 | 
					 | 
				
			||||||
		return _subpaths.size() != 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func get_extends_text():
 | 
					 | 
				
			||||||
		var extend = null
 | 
					 | 
				
			||||||
		if(is_native()):
 | 
					 | 
				
			||||||
			extend = str("extends ", get_native_class_name())
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			extend = str("extends '", get_path(), '\'')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if(has_subpath()):
 | 
					 | 
				
			||||||
			extend += str('.', get_subpath().replace('/', '.'))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return extend
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func get_method_strategy():
 | 
					 | 
				
			||||||
		return _method_strategy
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func set_method_strategy(method_strategy):
 | 
					 | 
				
			||||||
		_method_strategy = method_strategy
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func is_native():
 | 
					 | 
				
			||||||
		return _native_class != null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func set_native_class(native_class):
 | 
					 | 
				
			||||||
		_native_class = native_class
 | 
					 | 
				
			||||||
		_native_class_instance = native_class.new()
 | 
					 | 
				
			||||||
		_path = _native_class_instance.get_class()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func get_native_class_name():
 | 
					 | 
				
			||||||
		return _native_class_instance.get_class()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
# START Doubler
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
var _utils = load('res://addons/gut/utils.gd').new()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _output_dir = null
 | 
					 | 
				
			||||||
var _double_count = 0 # used in making files names unique
 | 
					 | 
				
			||||||
var _use_unique_names = true
 | 
					 | 
				
			||||||
var _spy = null
 | 
					 | 
				
			||||||
var  _ignored_methods = _utils.OneToMany.new()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _stubber = _utils.Stubber.new()
 | 
					 | 
				
			||||||
var _lgr = _utils.get_logger()
 | 
					 | 
				
			||||||
var _method_maker = _utils.MethodMaker.new()
 | 
					 | 
				
			||||||
var _strategy = null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _init(strategy=_utils.DOUBLE_STRATEGY.PARTIAL):
 | 
					 | 
				
			||||||
	# make sure _method_maker gets logger too
 | 
					 | 
				
			||||||
	set_logger(_utils.get_logger())
 | 
					 | 
				
			||||||
	_strategy = strategy
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ###############
 | 
					 | 
				
			||||||
# Private
 | 
					 | 
				
			||||||
# ###############
 | 
					 | 
				
			||||||
func _get_indented_line(indents, text):
 | 
					 | 
				
			||||||
	var to_return = ''
 | 
					 | 
				
			||||||
	for i in range(indents):
 | 
					 | 
				
			||||||
		to_return += "\t"
 | 
					 | 
				
			||||||
	return str(to_return, text, "\n")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _stub_to_call_super(obj_info, method_name):
 | 
					 | 
				
			||||||
	var path = obj_info.get_path()
 | 
					 | 
				
			||||||
	if(obj_info.scene_path != null):
 | 
					 | 
				
			||||||
		path = obj_info.scene_path
 | 
					 | 
				
			||||||
	var params = _utils.StubParams.new(path, method_name, obj_info.get_subpath())
 | 
					 | 
				
			||||||
	params.to_call_super()
 | 
					 | 
				
			||||||
	_stubber.add_stub(params)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _write_file(obj_info, dest_path, override_path=null):
 | 
					 | 
				
			||||||
	var script_methods = _get_methods(obj_info)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var metadata = _get_stubber_metadata_text(obj_info)
 | 
					 | 
				
			||||||
	if(override_path):
 | 
					 | 
				
			||||||
		metadata = _get_stubber_metadata_text(obj_info, override_path)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var f = File.new()
 | 
					 | 
				
			||||||
	var f_result = f.open(dest_path, f.WRITE)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(f_result != OK):
 | 
					 | 
				
			||||||
		print('Error creating file ', dest_path)
 | 
					 | 
				
			||||||
		print('Could not create double for :', obj_info.to_s())
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	f.store_string(str(obj_info.get_extends_text(), "\n"))
 | 
					 | 
				
			||||||
	f.store_string(metadata)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i in range(script_methods.local_methods.size()):
 | 
					 | 
				
			||||||
		if(obj_info.make_partial_double):
 | 
					 | 
				
			||||||
			_stub_to_call_super(obj_info, script_methods.local_methods[i].name)
 | 
					 | 
				
			||||||
		f.store_string(_get_func_text(script_methods.local_methods[i]))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i in range(script_methods.built_ins.size()):
 | 
					 | 
				
			||||||
		_stub_to_call_super(obj_info, script_methods.built_ins[i].name)
 | 
					 | 
				
			||||||
		f.store_string(_get_func_text(script_methods.built_ins[i]))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	f.close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _double_scene_and_script(scene_info, dest_path):
 | 
					 | 
				
			||||||
	var dir = Directory.new()
 | 
					 | 
				
			||||||
	dir.copy(scene_info.get_path(), dest_path)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var inst = load(scene_info.get_path()).instance()
 | 
					 | 
				
			||||||
	var script_path = null
 | 
					 | 
				
			||||||
	if(inst.get_script()):
 | 
					 | 
				
			||||||
		script_path = inst.get_script().get_path()
 | 
					 | 
				
			||||||
	inst.free()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(script_path):
 | 
					 | 
				
			||||||
		var oi = ObjectInfo.new(script_path)
 | 
					 | 
				
			||||||
		oi.set_method_strategy(scene_info.get_method_strategy())
 | 
					 | 
				
			||||||
		oi.make_partial_double = scene_info.make_partial_double
 | 
					 | 
				
			||||||
		oi.scene_path = scene_info.get_path()
 | 
					 | 
				
			||||||
		var double_path = _double(oi, scene_info.get_path())
 | 
					 | 
				
			||||||
		var dq = '"'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		var f = File.new()
 | 
					 | 
				
			||||||
		f.open(dest_path, f.READ)
 | 
					 | 
				
			||||||
		var source = f.get_as_text()
 | 
					 | 
				
			||||||
		f.close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		source = source.replace(dq + script_path + dq, dq + double_path + dq)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		f.open(dest_path, f.WRITE)
 | 
					 | 
				
			||||||
		f.store_string(source)
 | 
					 | 
				
			||||||
		f.close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return script_path
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _get_methods(object_info):
 | 
					 | 
				
			||||||
	var obj = object_info.instantiate()
 | 
					 | 
				
			||||||
	# any method in the script or super script
 | 
					 | 
				
			||||||
	var script_methods = ScriptMethods.new()
 | 
					 | 
				
			||||||
	var methods = obj.get_method_list()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	# first pass is for local methods only
 | 
					 | 
				
			||||||
	for i in range(methods.size()):
 | 
					 | 
				
			||||||
		# 65 is a magic number for methods in script, though documentation
 | 
					 | 
				
			||||||
		# says 64.  This picks up local overloads of base class methods too.
 | 
					 | 
				
			||||||
		if(methods[i].flags == 65 and !_ignored_methods.has(object_info.get_path(), methods[i]['name'])):
 | 
					 | 
				
			||||||
			script_methods.add_local_method(methods[i])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(object_info.get_method_strategy() == _utils.DOUBLE_STRATEGY.FULL):
 | 
					 | 
				
			||||||
		# second pass is for anything not local
 | 
					 | 
				
			||||||
		for i in range(methods.size()):
 | 
					 | 
				
			||||||
			# 65 is a magic number for methods in script, though documentation
 | 
					 | 
				
			||||||
			# says 64.  This picks up local overloads of base class methods too.
 | 
					 | 
				
			||||||
			if(methods[i].flags != 65 and !_ignored_methods.has(object_info.get_path(), methods[i]['name'])):
 | 
					 | 
				
			||||||
				script_methods.add_built_in_method(methods[i])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return script_methods
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _get_inst_id_ref_str(inst):
 | 
					 | 
				
			||||||
	var ref_str = 'null'
 | 
					 | 
				
			||||||
	if(inst):
 | 
					 | 
				
			||||||
		ref_str = str('instance_from_id(', inst.get_instance_id(),')')
 | 
					 | 
				
			||||||
	return ref_str
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _get_stubber_metadata_text(obj_info, override_path = null):
 | 
					 | 
				
			||||||
	var path = obj_info.get_path()
 | 
					 | 
				
			||||||
	if(override_path != null):
 | 
					 | 
				
			||||||
		path = override_path
 | 
					 | 
				
			||||||
	return "var __gut_metadata_ = {\n" + \
 | 
					 | 
				
			||||||
           "\tpath='" + path + "',\n" + \
 | 
					 | 
				
			||||||
		   "\tsubpath='" + obj_info.get_subpath() + "',\n" + \
 | 
					 | 
				
			||||||
		   "\tstubber=" + _get_inst_id_ref_str(_stubber) + ",\n" + \
 | 
					 | 
				
			||||||
		   "\tspy=" + _get_inst_id_ref_str(_spy) + "\n" + \
 | 
					 | 
				
			||||||
           "}\n"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _get_spy_text(method_hash):
 | 
					 | 
				
			||||||
	var txt = ''
 | 
					 | 
				
			||||||
	if(_spy):
 | 
					 | 
				
			||||||
		var called_with = _method_maker.get_spy_call_parameters_text(method_hash)
 | 
					 | 
				
			||||||
		txt += "\t__gut_metadata_.spy.add_call(self, '" + method_hash.name + "', " + called_with + ")\n"
 | 
					 | 
				
			||||||
	return txt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _get_func_text(method_hash):
 | 
					 | 
				
			||||||
	var ftxt = _method_maker.get_decleration_text(method_hash) + "\n"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var called_with = _method_maker.get_spy_call_parameters_text(method_hash)
 | 
					 | 
				
			||||||
	ftxt += _get_spy_text(method_hash)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(_stubber and method_hash.name != '_init'):
 | 
					 | 
				
			||||||
		var call_method = _method_maker.get_super_call_text(method_hash)
 | 
					 | 
				
			||||||
		ftxt += "\tif(__gut_metadata_.stubber.should_call_super(self, '" + method_hash.name + "', " + called_with + ")):\n"
 | 
					 | 
				
			||||||
		ftxt += "\t\treturn " + call_method + "\n"
 | 
					 | 
				
			||||||
		ftxt += "\telse:\n"
 | 
					 | 
				
			||||||
		ftxt += "\t\treturn __gut_metadata_.stubber.get_return(self, '" + method_hash.name + "', " + called_with + ")\n"
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		ftxt += "\tpass\n"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return ftxt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _get_super_func_text(method_hash):
 | 
					 | 
				
			||||||
	var call_method = _method_maker.get_super_call_text(method_hash)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var call_super_text = str("return ", call_method, "\n")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var ftxt = _method_maker.get_decleration_text(method_hash) + "\n"
 | 
					 | 
				
			||||||
	ftxt += _get_spy_text(method_hash)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ftxt += _get_indented_line(1, call_super_text)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return ftxt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# returns the path to write the double file to
 | 
					 | 
				
			||||||
func _get_temp_path(object_info):
 | 
					 | 
				
			||||||
	var file_name = null
 | 
					 | 
				
			||||||
	var extension = null
 | 
					 | 
				
			||||||
	if(object_info.is_native()):
 | 
					 | 
				
			||||||
		file_name = object_info.get_native_class_name()
 | 
					 | 
				
			||||||
		extension = 'gd'
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		file_name = object_info.get_path().get_file().get_basename()
 | 
					 | 
				
			||||||
		extension = object_info.get_path().get_extension()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(object_info.has_subpath()):
 | 
					 | 
				
			||||||
		file_name += '__' + object_info.get_subpath().replace('/', '__')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(_use_unique_names):
 | 
					 | 
				
			||||||
		file_name += str('__dbl', _double_count, '__.', extension)
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		file_name += '.' + extension
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var to_return = _output_dir.plus_file(file_name)
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _double(obj_info, override_path=null):
 | 
					 | 
				
			||||||
	var temp_path = _get_temp_path(obj_info)
 | 
					 | 
				
			||||||
	_write_file(obj_info, temp_path, override_path)
 | 
					 | 
				
			||||||
	_double_count += 1
 | 
					 | 
				
			||||||
	return temp_path
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _double_script(path, make_partial, strategy):
 | 
					 | 
				
			||||||
	var oi = ObjectInfo.new(path)
 | 
					 | 
				
			||||||
	oi.make_partial_double = make_partial
 | 
					 | 
				
			||||||
	oi.set_method_strategy(strategy)
 | 
					 | 
				
			||||||
	var to_return = load(_double(oi))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _double_inner(path, subpath, make_partial, strategy):
 | 
					 | 
				
			||||||
	var oi = ObjectInfo.new(path, subpath)
 | 
					 | 
				
			||||||
	oi.set_method_strategy(strategy)
 | 
					 | 
				
			||||||
	oi.make_partial_double = make_partial
 | 
					 | 
				
			||||||
	var to_return = load(_double(oi))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _double_scene(path, make_partial, strategy):
 | 
					 | 
				
			||||||
	var oi = ObjectInfo.new(path)
 | 
					 | 
				
			||||||
	oi.set_method_strategy(strategy)
 | 
					 | 
				
			||||||
	oi.make_partial_double = make_partial
 | 
					 | 
				
			||||||
	var temp_path = _get_temp_path(oi)
 | 
					 | 
				
			||||||
	_double_scene_and_script(oi, temp_path)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return load(temp_path)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _double_gdnative(native_class, make_partial, strategy):
 | 
					 | 
				
			||||||
	var oi = ObjectInfo.new(null)
 | 
					 | 
				
			||||||
	oi.set_native_class(native_class)
 | 
					 | 
				
			||||||
	oi.set_method_strategy(strategy)
 | 
					 | 
				
			||||||
	oi.make_partial_double = make_partial
 | 
					 | 
				
			||||||
	var to_return = load(_double(oi))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ###############
 | 
					 | 
				
			||||||
# Public
 | 
					 | 
				
			||||||
# ###############
 | 
					 | 
				
			||||||
func get_output_dir():
 | 
					 | 
				
			||||||
	return _output_dir
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_output_dir(output_dir):
 | 
					 | 
				
			||||||
	_output_dir = output_dir
 | 
					 | 
				
			||||||
	var d = Directory.new()
 | 
					 | 
				
			||||||
	d.make_dir_recursive(output_dir)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_spy():
 | 
					 | 
				
			||||||
	return _spy
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_spy(spy):
 | 
					 | 
				
			||||||
	_spy = spy
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_stubber():
 | 
					 | 
				
			||||||
	return _stubber
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_stubber(stubber):
 | 
					 | 
				
			||||||
	_stubber = stubber
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_logger():
 | 
					 | 
				
			||||||
	return _lgr
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_logger(logger):
 | 
					 | 
				
			||||||
	_lgr = logger
 | 
					 | 
				
			||||||
	_method_maker.set_logger(logger)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_strategy():
 | 
					 | 
				
			||||||
	return _strategy
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_strategy(strategy):
 | 
					 | 
				
			||||||
	_strategy = strategy
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func partial_double_scene(path, strategy=_strategy):
 | 
					 | 
				
			||||||
	return _double_scene(path, true, strategy)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# double a scene
 | 
					 | 
				
			||||||
func double_scene(path, strategy=_strategy):
 | 
					 | 
				
			||||||
	return _double_scene(path, false, strategy)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# double a script/object
 | 
					 | 
				
			||||||
func double(path, strategy=_strategy):
 | 
					 | 
				
			||||||
	return _double_script(path, false, strategy)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func partial_double(path, strategy=_strategy):
 | 
					 | 
				
			||||||
	return _double_script(path, true, strategy)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func partial_double_inner(path, subpath, strategy=_strategy):
 | 
					 | 
				
			||||||
	return _double_inner(path, subpath, true, strategy)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# double an inner class in a script
 | 
					 | 
				
			||||||
func double_inner(path, subpath, strategy=_strategy):
 | 
					 | 
				
			||||||
	return _double_inner(path, subpath, false, strategy)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# must always use FULL strategy since this is a native class and you won't get
 | 
					 | 
				
			||||||
# any methods if you don't use FULL
 | 
					 | 
				
			||||||
func double_gdnative(native_class):
 | 
					 | 
				
			||||||
	return _double_gdnative(native_class, false, _utils.DOUBLE_STRATEGY.FULL)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# must always use FULL strategy since this is a native class and you won't get
 | 
					 | 
				
			||||||
# any methods if you don't use FULL
 | 
					 | 
				
			||||||
func partial_double_gdnative(native_class):
 | 
					 | 
				
			||||||
	return _double_gdnative(native_class, true, _utils.DOUBLE_STRATEGY.FULL)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func clear_output_directory():
 | 
					 | 
				
			||||||
	var did = false
 | 
					 | 
				
			||||||
	if(_output_dir.find('user://') == 0):
 | 
					 | 
				
			||||||
		var d = Directory.new()
 | 
					 | 
				
			||||||
		var result = d.open(_output_dir)
 | 
					 | 
				
			||||||
		# BIG GOTCHA HERE.  If it cannot open the dir w/ erro 31, then the
 | 
					 | 
				
			||||||
		# directory becomes res:// and things go on normally and gut clears out
 | 
					 | 
				
			||||||
		# out res:// which is SUPER BAD.
 | 
					 | 
				
			||||||
		if(result == OK):
 | 
					 | 
				
			||||||
			d.list_dir_begin(true)
 | 
					 | 
				
			||||||
			var f = d.get_next()
 | 
					 | 
				
			||||||
			while(f != ''):
 | 
					 | 
				
			||||||
				d.remove(f)
 | 
					 | 
				
			||||||
				f = d.get_next()
 | 
					 | 
				
			||||||
				did = true
 | 
					 | 
				
			||||||
	return did
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func delete_output_directory():
 | 
					 | 
				
			||||||
	var did = clear_output_directory()
 | 
					 | 
				
			||||||
	if(did):
 | 
					 | 
				
			||||||
		var d = Directory.new()
 | 
					 | 
				
			||||||
		d.remove(_output_dir)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# When creating doubles a unique name is used that each double can be its own
 | 
					 | 
				
			||||||
# thing.  Sometimes, for testing, we do not want to do this so this allows
 | 
					 | 
				
			||||||
# you to turn off creating unique names for each double class.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# THIS SHOULD NEVER BE USED OUTSIDE OF INTERNAL GUT TESTING.  It can cause
 | 
					 | 
				
			||||||
# weird, hard to track down problems.
 | 
					 | 
				
			||||||
func set_use_unique_names(should):
 | 
					 | 
				
			||||||
	_use_unique_names = should
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func add_ignored_method(path, method_name):
 | 
					 | 
				
			||||||
	_ignored_methods.add(path, method_name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_ignored_methods():
 | 
					 | 
				
			||||||
	return _ignored_methods
 | 
					 | 
				
			||||||
							
								
								
									
										1334
									
								
								addons/gut/gut.gd
									
										
									
									
									
								
							
							
						
						
									
										1334
									
								
								addons/gut/gut.gd
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
					@ -1,356 +0,0 @@
 | 
				
			||||||
################################################################################
 | 
					 | 
				
			||||||
#(G)odot (U)nit (T)est class
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
################################################################################
 | 
					 | 
				
			||||||
#The MIT License (MIT)
 | 
					 | 
				
			||||||
#=====================
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
#Copyright (c) 2019 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.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# Version 6.8.1
 | 
					 | 
				
			||||||
################################################################################
 | 
					 | 
				
			||||||
extends SceneTree
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var Optparse = load('res://addons/gut/optparse.gd')
 | 
					 | 
				
			||||||
var Gut = load('res://addons/gut/gut.gd')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#-------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
# Helper class to resolve the various different places where an option can
 | 
					 | 
				
			||||||
# be set.  Using the get_value method will enforce the order of precedence of:
 | 
					 | 
				
			||||||
# 	1.  command line value
 | 
					 | 
				
			||||||
#	2.  config file value
 | 
					 | 
				
			||||||
#	3.  default value
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# The idea is that you set the base_opts.  That will get you a copies of the
 | 
					 | 
				
			||||||
# hash with null values for the other types of values.  Lower precedented hashes
 | 
					 | 
				
			||||||
# will punch through null values of higher precedented hashes.
 | 
					 | 
				
			||||||
#-------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
class OptionResolver:
 | 
					 | 
				
			||||||
	var base_opts = null
 | 
					 | 
				
			||||||
	var cmd_opts = null
 | 
					 | 
				
			||||||
	var config_opts = null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func get_value(key):
 | 
					 | 
				
			||||||
		return _nvl(cmd_opts[key], _nvl(config_opts[key], base_opts[key]))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func set_base_opts(opts):
 | 
					 | 
				
			||||||
		base_opts = opts
 | 
					 | 
				
			||||||
		cmd_opts = _null_copy(opts)
 | 
					 | 
				
			||||||
		config_opts = _null_copy(opts)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	# creates a copy of a hash with all values null.
 | 
					 | 
				
			||||||
	func _null_copy(h):
 | 
					 | 
				
			||||||
		var new_hash = {}
 | 
					 | 
				
			||||||
		for key in h:
 | 
					 | 
				
			||||||
			new_hash[key] = null
 | 
					 | 
				
			||||||
		return new_hash
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func _nvl(a, b):
 | 
					 | 
				
			||||||
		if(a == null):
 | 
					 | 
				
			||||||
			return b
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			return a
 | 
					 | 
				
			||||||
	func _string_it(h):
 | 
					 | 
				
			||||||
		var to_return = ''
 | 
					 | 
				
			||||||
		for key in h:
 | 
					 | 
				
			||||||
			to_return += str('(',key, ':', _nvl(h[key], 'NULL'), ')')
 | 
					 | 
				
			||||||
		return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func to_s():
 | 
					 | 
				
			||||||
		return str("base:\n", _string_it(base_opts), "\n", \
 | 
					 | 
				
			||||||
				   "config:\n", _string_it(config_opts), "\n", \
 | 
					 | 
				
			||||||
				   "cmd:\n", _string_it(cmd_opts), "\n", \
 | 
					 | 
				
			||||||
				   "resolved:\n", _string_it(get_resolved_values()))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func get_resolved_values():
 | 
					 | 
				
			||||||
		var to_return = {}
 | 
					 | 
				
			||||||
		for key in base_opts:
 | 
					 | 
				
			||||||
			to_return[key] = get_value(key)
 | 
					 | 
				
			||||||
		return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func to_s_verbose():
 | 
					 | 
				
			||||||
		var to_return = ''
 | 
					 | 
				
			||||||
		var resolved = get_resolved_values()
 | 
					 | 
				
			||||||
		for key in base_opts:
 | 
					 | 
				
			||||||
			to_return += str(key, "\n")
 | 
					 | 
				
			||||||
			to_return += str('  default: ', _nvl(base_opts[key], 'NULL'), "\n")
 | 
					 | 
				
			||||||
			to_return += str('  config:  ', _nvl(config_opts[key], ' --'), "\n")
 | 
					 | 
				
			||||||
			to_return += str('  cmd:     ', _nvl(cmd_opts[key], ' --'), "\n")
 | 
					 | 
				
			||||||
			to_return += str('  final:   ', _nvl(resolved[key], 'NULL'), "\n")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#-------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
# Here starts the actual script that uses the Options class to kick off Gut
 | 
					 | 
				
			||||||
# and run your tests.
 | 
					 | 
				
			||||||
#-------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
var _utils = load('res://addons/gut/utils.gd').new()
 | 
					 | 
				
			||||||
# instance of gut
 | 
					 | 
				
			||||||
var _tester = null
 | 
					 | 
				
			||||||
# array of command line options specified
 | 
					 | 
				
			||||||
var _opts = []
 | 
					 | 
				
			||||||
# Hash for easier access to the options in the code.  Options will be
 | 
					 | 
				
			||||||
# extracted into this hash and then the hash will be used afterwards so
 | 
					 | 
				
			||||||
# that I don't make any dumb typos and get the neat code-sense when I
 | 
					 | 
				
			||||||
# type a dot.
 | 
					 | 
				
			||||||
var options = {
 | 
					 | 
				
			||||||
	config_file = 'res://.gutconfig.json',
 | 
					 | 
				
			||||||
	dirs = [],
 | 
					 | 
				
			||||||
	double_strategy = 'partial',
 | 
					 | 
				
			||||||
	ignore_pause = false,
 | 
					 | 
				
			||||||
	include_subdirs = false,
 | 
					 | 
				
			||||||
	inner_class = '',
 | 
					 | 
				
			||||||
	log_level = 1,
 | 
					 | 
				
			||||||
	opacity = 100,
 | 
					 | 
				
			||||||
	prefix = 'test_',
 | 
					 | 
				
			||||||
	selected = '',
 | 
					 | 
				
			||||||
	should_exit = false,
 | 
					 | 
				
			||||||
	should_exit_on_success = false,
 | 
					 | 
				
			||||||
	should_maximize = false,
 | 
					 | 
				
			||||||
	show_help = false,
 | 
					 | 
				
			||||||
	suffix = '.gd',
 | 
					 | 
				
			||||||
	tests = [],
 | 
					 | 
				
			||||||
	unit_test_name = '',
 | 
					 | 
				
			||||||
	pre_run_script = '',
 | 
					 | 
				
			||||||
	post_run_script = ''
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# flag to indicate if only a single script should be run.
 | 
					 | 
				
			||||||
var _run_single = false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func setup_options():
 | 
					 | 
				
			||||||
	var opts = Optparse.new()
 | 
					 | 
				
			||||||
	opts.set_banner(('This is the command line interface for the unit testing tool Gut.  With this ' +
 | 
					 | 
				
			||||||
					'interface you can run one or more test scripts from the command line.  In order ' +
 | 
					 | 
				
			||||||
					'for the Gut options to not clash with any other godot options, each option starts ' +
 | 
					 | 
				
			||||||
					'with a "g".  Also, any option that requires a value will take the form of ' +
 | 
					 | 
				
			||||||
					'"-g<name>=<value>".  There cannot be any spaces between the option, the "=", or ' +
 | 
					 | 
				
			||||||
					'inside a specified value or godot will think you are trying to run a scene.'))
 | 
					 | 
				
			||||||
	opts.add('-gtest', [], 'Comma delimited list of full paths to test scripts to run.')
 | 
					 | 
				
			||||||
	opts.add('-gdir', [], 'Comma delimited list of directories to add tests from.')
 | 
					 | 
				
			||||||
	opts.add('-gprefix', 'test_', 'Prefix used to find tests when specifying -gdir.  Default "[default]"')
 | 
					 | 
				
			||||||
	opts.add('-gsuffix', '.gd', 'Suffix used to find tests when specifying -gdir.  Default "[default]"')
 | 
					 | 
				
			||||||
	opts.add('-gmaximize', false, 'Maximizes test runner window to fit the viewport.')
 | 
					 | 
				
			||||||
	opts.add('-gexit', false, 'Exit after running tests.  If not specified you have to manually close the window.')
 | 
					 | 
				
			||||||
	opts.add('-gexit_on_success', false, 'Only exit if all tests pass.')
 | 
					 | 
				
			||||||
	opts.add('-glog', 1, 'Log level.  Default [default]')
 | 
					 | 
				
			||||||
	opts.add('-gignore_pause', false, 'Ignores any calls to gut.pause_before_teardown.')
 | 
					 | 
				
			||||||
	opts.add('-gselect', '', ('Select a script to run initially.  The first script that ' +
 | 
					 | 
				
			||||||
							'was loaded using -gtest or -gdir that contains the specified ' +
 | 
					 | 
				
			||||||
							'string will be executed.  You may run others by interacting ' +
 | 
					 | 
				
			||||||
							'with the GUI.'))
 | 
					 | 
				
			||||||
	opts.add('-gunit_test_name', '', ('Name of a test to run.  Any test that contains the specified ' +
 | 
					 | 
				
			||||||
								'text will be run, all others will be skipped.'))
 | 
					 | 
				
			||||||
	opts.add('-gh', false, 'Print this help, then quit')
 | 
					 | 
				
			||||||
	opts.add('-gconfig', 'res://.gutconfig.json', 'A config file that contains configuration information.  Default is res://.gutconfig.json')
 | 
					 | 
				
			||||||
	opts.add('-ginner_class', '', 'Only run inner classes that contain this string')
 | 
					 | 
				
			||||||
	opts.add('-gopacity', 100, 'Set opacity of test runner window. Use range 0 - 100. 0 = transparent, 100 = opaque.')
 | 
					 | 
				
			||||||
	opts.add('-gpo', false, 'Print option values from all sources and the value used, then quit.')
 | 
					 | 
				
			||||||
	opts.add('-ginclude_subdirs', false, 'Include subdirectories of -gdir.')
 | 
					 | 
				
			||||||
	opts.add('-gdouble_strategy', 'partial', 'Default strategy to use when doubling.  Valid values are [partial, full].  Default "[default]"')
 | 
					 | 
				
			||||||
	opts.add('-gpre_run_script', '', 'pre-run hook script path')
 | 
					 | 
				
			||||||
	opts.add('-gpost_run_script', '', 'post-run hook script path')
 | 
					 | 
				
			||||||
	opts.add('-gprint_gutconfig_sample', false, 'Print out json that can be used to make a gutconfig file then quit.')
 | 
					 | 
				
			||||||
	return opts
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Parses options, applying them to the _tester or setting values
 | 
					 | 
				
			||||||
# in the options struct.
 | 
					 | 
				
			||||||
func extract_command_line_options(from, to):
 | 
					 | 
				
			||||||
	to.tests = from.get_value('-gtest')
 | 
					 | 
				
			||||||
	to.dirs = from.get_value('-gdir')
 | 
					 | 
				
			||||||
	to.should_exit = from.get_value('-gexit')
 | 
					 | 
				
			||||||
	to.should_exit_on_success = from.get_value('-gexit_on_success')
 | 
					 | 
				
			||||||
	to.should_maximize = from.get_value('-gmaximize')
 | 
					 | 
				
			||||||
	to.log_level = from.get_value('-glog')
 | 
					 | 
				
			||||||
	to.ignore_pause = from.get_value('-gignore_pause')
 | 
					 | 
				
			||||||
	to.selected = from.get_value('-gselect')
 | 
					 | 
				
			||||||
	to.prefix = from.get_value('-gprefix')
 | 
					 | 
				
			||||||
	to.suffix = from.get_value('-gsuffix')
 | 
					 | 
				
			||||||
	to.unit_test_name = from.get_value('-gunit_test_name')
 | 
					 | 
				
			||||||
	to.config_file = from.get_value('-gconfig')
 | 
					 | 
				
			||||||
	to.inner_class = from.get_value('-ginner_class')
 | 
					 | 
				
			||||||
	to.opacity = from.get_value('-gopacity')
 | 
					 | 
				
			||||||
	to.include_subdirs = from.get_value('-ginclude_subdirs')
 | 
					 | 
				
			||||||
	to.double_strategy = from.get_value('-gdouble_strategy')
 | 
					 | 
				
			||||||
	to.pre_run_script = from.get_value('-gpre_run_script')
 | 
					 | 
				
			||||||
	to.post_run_script = from.get_value('-gpost_run_script')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func load_options_from_config_file(file_path, into):
 | 
					 | 
				
			||||||
	# SHORTCIRCUIT
 | 
					 | 
				
			||||||
	var f = File.new()
 | 
					 | 
				
			||||||
	if(!f.file_exists(file_path)):
 | 
					 | 
				
			||||||
		if(file_path != 'res://.gutconfig.json'):
 | 
					 | 
				
			||||||
			print('ERROR:  Config File "', file_path, '" does not exist.')
 | 
					 | 
				
			||||||
			return -1
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			return 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	f.open(file_path, f.READ)
 | 
					 | 
				
			||||||
	var json = f.get_as_text()
 | 
					 | 
				
			||||||
	f.close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var results = JSON.parse(json)
 | 
					 | 
				
			||||||
	# SHORTCIRCUIT
 | 
					 | 
				
			||||||
	if(results.error != OK):
 | 
					 | 
				
			||||||
		print("\n\n",'!! ERROR parsing file:  ', file_path)
 | 
					 | 
				
			||||||
		print('    at line ', results.error_line, ':')
 | 
					 | 
				
			||||||
		print('    ', results.error_string)
 | 
					 | 
				
			||||||
		return -1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	# Get all the options out of the config file using the option name.  The
 | 
					 | 
				
			||||||
	# options hash is now the default source of truth for the name of an option.
 | 
					 | 
				
			||||||
	for key in into:
 | 
					 | 
				
			||||||
		if(results.result.has(key)):
 | 
					 | 
				
			||||||
			into[key] = results.result[key]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Apply all the options specified to _tester.  This is where the rubber meets
 | 
					 | 
				
			||||||
# the road.
 | 
					 | 
				
			||||||
func apply_options(opts):
 | 
					 | 
				
			||||||
	_tester = Gut.new()
 | 
					 | 
				
			||||||
	get_root().add_child(_tester)
 | 
					 | 
				
			||||||
	_tester.connect('tests_finished', self, '_on_tests_finished', [opts.should_exit, opts.should_exit_on_success])
 | 
					 | 
				
			||||||
	_tester.set_yield_between_tests(true)
 | 
					 | 
				
			||||||
	_tester.set_modulate(Color(1.0, 1.0, 1.0, min(1.0, float(opts.opacity) / 100)))
 | 
					 | 
				
			||||||
	_tester.show()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_tester.set_include_subdirectories(opts.include_subdirs)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(opts.should_maximize):
 | 
					 | 
				
			||||||
		_tester.maximize()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(opts.inner_class != ''):
 | 
					 | 
				
			||||||
		_tester.set_inner_class_name(opts.inner_class)
 | 
					 | 
				
			||||||
	_tester.set_log_level(opts.log_level)
 | 
					 | 
				
			||||||
	_tester.set_ignore_pause_before_teardown(opts.ignore_pause)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i in range(opts.dirs.size()):
 | 
					 | 
				
			||||||
		_tester.add_directory(opts.dirs[i], opts.prefix, opts.suffix)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i in range(opts.tests.size()):
 | 
					 | 
				
			||||||
		_tester.add_script(opts.tests[i])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(opts.selected != ''):
 | 
					 | 
				
			||||||
		_tester.select_script(opts.selected)
 | 
					 | 
				
			||||||
		_run_single = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(opts.double_strategy == 'full'):
 | 
					 | 
				
			||||||
		_tester.set_double_strategy(_utils.DOUBLE_STRATEGY.FULL)
 | 
					 | 
				
			||||||
	elif(opts.double_strategy == 'partial'):
 | 
					 | 
				
			||||||
		_tester.set_double_strategy(_utils.DOUBLE_STRATEGY.PARTIAL)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_tester.set_unit_test_name(opts.unit_test_name)
 | 
					 | 
				
			||||||
	_tester.set_pre_run_script(opts.pre_run_script)
 | 
					 | 
				
			||||||
	_tester.set_post_run_script(opts.post_run_script)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _print_gutconfigs(values):
 | 
					 | 
				
			||||||
	var header = """Here is a sample of a full .gutconfig.json file.
 | 
					 | 
				
			||||||
You do not need to specify all values in your own file.  The values supplied in
 | 
					 | 
				
			||||||
this sample are what would be used if you ran gut w/o the -gprint_gutconfig_sample
 | 
					 | 
				
			||||||
option (the resolved values where default < .gutconfig < command line)."""
 | 
					 | 
				
			||||||
	print("\n", header.replace("\n", ' '), "\n\n")
 | 
					 | 
				
			||||||
	var resolved = values
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	# remove some options that don't make sense to be in config
 | 
					 | 
				
			||||||
	resolved.erase("config_file")
 | 
					 | 
				
			||||||
	resolved.erase("show_help")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	print("Here's a config with all the properties set based off of your current command and config.")
 | 
					 | 
				
			||||||
	var text = JSON.print(resolved)
 | 
					 | 
				
			||||||
	print(text.replace(',', ",\n"))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for key in resolved:
 | 
					 | 
				
			||||||
		resolved[key] = null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	print("\n\nAnd here's an empty config for you fill in what you want.")
 | 
					 | 
				
			||||||
	text = JSON.print(resolved)
 | 
					 | 
				
			||||||
	print(text.replace(',', ",\n"))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# parse options and run Gut
 | 
					 | 
				
			||||||
func _init():
 | 
					 | 
				
			||||||
	var opt_resolver = OptionResolver.new()
 | 
					 | 
				
			||||||
	opt_resolver.set_base_opts(options)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	print("\n\n", ' ---  Gut  ---')
 | 
					 | 
				
			||||||
	var o = setup_options()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var all_options_valid = o.parse()
 | 
					 | 
				
			||||||
	extract_command_line_options(o, opt_resolver.cmd_opts)
 | 
					 | 
				
			||||||
	var load_result = \
 | 
					 | 
				
			||||||
			load_options_from_config_file(opt_resolver.get_value('config_file'), opt_resolver.config_opts)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(load_result == -1): # -1 indicates json parse error
 | 
					 | 
				
			||||||
		quit()
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		if(!all_options_valid):
 | 
					 | 
				
			||||||
			quit()
 | 
					 | 
				
			||||||
		elif(o.get_value('-gh')):
 | 
					 | 
				
			||||||
			var v_info = Engine.get_version_info()
 | 
					 | 
				
			||||||
			print(str('Godot version:  ', v_info.major,  '.',  v_info.minor,  '.',  v_info.patch))
 | 
					 | 
				
			||||||
			print(str('GUT version:  ', Gut.new().get_version()))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			o.print_help()
 | 
					 | 
				
			||||||
			quit()
 | 
					 | 
				
			||||||
		elif(o.get_value('-gpo')):
 | 
					 | 
				
			||||||
			print('All command line options and where they are specified.  ' +
 | 
					 | 
				
			||||||
				'The "final" value shows which value will actually be used ' +
 | 
					 | 
				
			||||||
				'based on order of precedence (default < .gutconfig < cmd line).' + "\n")
 | 
					 | 
				
			||||||
			print(opt_resolver.to_s_verbose())
 | 
					 | 
				
			||||||
			quit()
 | 
					 | 
				
			||||||
		elif(o.get_value('-gprint_gutconfig_sample')):
 | 
					 | 
				
			||||||
			_print_gutconfigs(opt_resolver.get_resolved_values())
 | 
					 | 
				
			||||||
			quit()
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			apply_options(opt_resolver.get_resolved_values())
 | 
					 | 
				
			||||||
			_tester.test_scripts(!_run_single)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# exit if option is set.
 | 
					 | 
				
			||||||
func _on_tests_finished(should_exit, should_exit_on_success):
 | 
					 | 
				
			||||||
	if(_tester.get_fail_count()):
 | 
					 | 
				
			||||||
		OS.exit_code = 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	# Overwrite the exit code with the post_script
 | 
					 | 
				
			||||||
	var post_inst = _tester.get_post_run_script_instance()
 | 
					 | 
				
			||||||
	if(post_inst != null and post_inst.get_exit_code() != null):
 | 
					 | 
				
			||||||
		OS.exit_code = post_inst.get_exit_code()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(should_exit or (should_exit_on_success and _tester.get_fail_count() == 0)):
 | 
					 | 
				
			||||||
		quit()
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		print("Tests finished, exit manually")
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,12 +0,0 @@
 | 
				
			||||||
tool
 | 
					 | 
				
			||||||
extends EditorPlugin
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _enter_tree():
 | 
					 | 
				
			||||||
    # Initialization of the plugin goes here
 | 
					 | 
				
			||||||
    # Add the new type with a name, a parent type, a script and an icon
 | 
					 | 
				
			||||||
    add_custom_type("Gut", "Control", preload("gut.gd"), preload("icon.png"))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _exit_tree():
 | 
					 | 
				
			||||||
    # Clean-up of the plugin goes here
 | 
					 | 
				
			||||||
    # Always remember to remove it from the engine when deactivated
 | 
					 | 
				
			||||||
    remove_custom_type("Gut")
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,35 +0,0 @@
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
# This script is the base for custom scripts to be used in pre and post
 | 
					 | 
				
			||||||
# run hooks.
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# This is the instance of GUT that is running the tests.  You can get
 | 
					 | 
				
			||||||
# information about the run from this object.  This is set by GUT when the
 | 
					 | 
				
			||||||
# script is instantiated.
 | 
					 | 
				
			||||||
var gut  = null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# the exit code to be used by gut_cmdln.  See set method.
 | 
					 | 
				
			||||||
var _exit_code = null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _should_abort =  false
 | 
					 | 
				
			||||||
# Virtual method that will be called by GUT after instantiating
 | 
					 | 
				
			||||||
# this script.
 | 
					 | 
				
			||||||
func run():
 | 
					 | 
				
			||||||
	pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Set the exit code when running from the command line.  If not set then the
 | 
					 | 
				
			||||||
# default exit code will be returned (0 when no tests fail, 1 when any tests
 | 
					 | 
				
			||||||
# fail).
 | 
					 | 
				
			||||||
func set_exit_code(code):
 | 
					 | 
				
			||||||
	_exit_code  = code
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_exit_code():
 | 
					 | 
				
			||||||
	return _exit_code
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Usable by pre-run script to cause the run to end AFTER the run() method
 | 
					 | 
				
			||||||
# finishes.  post-run script will not be ran.
 | 
					 | 
				
			||||||
func abort():
 | 
					 | 
				
			||||||
	_should_abort = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func should_abort():
 | 
					 | 
				
			||||||
	return _should_abort
 | 
					 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 320 B  | 
| 
						 | 
					@ -1,34 +0,0 @@
 | 
				
			||||||
[remap]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
importer="texture"
 | 
					 | 
				
			||||||
type="StreamTexture"
 | 
					 | 
				
			||||||
path="res://.import/icon.png-91b084043b8aaf2f1c906e7b9fa92969.stex"
 | 
					 | 
				
			||||||
metadata={
 | 
					 | 
				
			||||||
"vram_texture": false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[deps]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
source_file="res://addons/gut/icon.png"
 | 
					 | 
				
			||||||
dest_files=[ "res://.import/icon.png-91b084043b8aaf2f1c906e7b9fa92969.stex" ]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[params]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
compress/mode=0
 | 
					 | 
				
			||||||
compress/lossy_quality=0.7
 | 
					 | 
				
			||||||
compress/hdr_mode=0
 | 
					 | 
				
			||||||
compress/bptc_ldr=0
 | 
					 | 
				
			||||||
compress/normal_map=0
 | 
					 | 
				
			||||||
flags/repeat=0
 | 
					 | 
				
			||||||
flags/filter=true
 | 
					 | 
				
			||||||
flags/mipmaps=false
 | 
					 | 
				
			||||||
flags/anisotropic=false
 | 
					 | 
				
			||||||
flags/srgb=2
 | 
					 | 
				
			||||||
process/fix_alpha_border=true
 | 
					 | 
				
			||||||
process/premult_alpha=false
 | 
					 | 
				
			||||||
process/HDR_as_SRGB=false
 | 
					 | 
				
			||||||
process/invert_color=false
 | 
					 | 
				
			||||||
stream=false
 | 
					 | 
				
			||||||
size_limit=0
 | 
					 | 
				
			||||||
detect_3d=true
 | 
					 | 
				
			||||||
svg/scale=1.0
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,105 +0,0 @@
 | 
				
			||||||
extends Node2D
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _gut = null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var types = {
 | 
					 | 
				
			||||||
	warn = 'WARNING',
 | 
					 | 
				
			||||||
	error = 'ERROR',
 | 
					 | 
				
			||||||
	info = 'INFO',
 | 
					 | 
				
			||||||
	debug = 'DEBUG',
 | 
					 | 
				
			||||||
	deprecated = 'DEPRECATED'
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _logs = {
 | 
					 | 
				
			||||||
	types.warn: [],
 | 
					 | 
				
			||||||
	types.error: [],
 | 
					 | 
				
			||||||
	types.info: [],
 | 
					 | 
				
			||||||
	types.debug: [],
 | 
					 | 
				
			||||||
	types.deprecated: []
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _suppress_output = false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _gut_log_level_for_type(log_type):
 | 
					 | 
				
			||||||
	if(log_type == types.warn or log_type == types.error or log_type == types.deprecated):
 | 
					 | 
				
			||||||
		return 0
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		return 2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _log(type, text):
 | 
					 | 
				
			||||||
	_logs[type].append(text)
 | 
					 | 
				
			||||||
	var formatted = str('[', type, ']  ', text)
 | 
					 | 
				
			||||||
	if(!_suppress_output):
 | 
					 | 
				
			||||||
		if(_gut):
 | 
					 | 
				
			||||||
			# this will keep the text indented under test for readability
 | 
					 | 
				
			||||||
			_gut.p(formatted, _gut_log_level_for_type(type))
 | 
					 | 
				
			||||||
			# IDEA!  We could store the current script and test that generated
 | 
					 | 
				
			||||||
			# this output, which could be useful later if we printed out a summary.
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			print(formatted)
 | 
					 | 
				
			||||||
	return formatted
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ---------------
 | 
					 | 
				
			||||||
# Get Methods
 | 
					 | 
				
			||||||
# ---------------
 | 
					 | 
				
			||||||
func get_warnings():
 | 
					 | 
				
			||||||
	return get_log_entries(types.warn)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_errors():
 | 
					 | 
				
			||||||
	return get_log_entries(types.error)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_infos():
 | 
					 | 
				
			||||||
	return get_log_entries(types.info)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_debugs():
 | 
					 | 
				
			||||||
	return get_log_entries(types.debug)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_deprecated():
 | 
					 | 
				
			||||||
	return get_log_entries(types.deprecated)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_count(log_type=null):
 | 
					 | 
				
			||||||
	var count = 0
 | 
					 | 
				
			||||||
	if(log_type == null):
 | 
					 | 
				
			||||||
		for key in _logs:
 | 
					 | 
				
			||||||
			count += _logs[key].size()
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		count = _logs[log_type].size()
 | 
					 | 
				
			||||||
	return count
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_log_entries(log_type):
 | 
					 | 
				
			||||||
	return _logs[log_type]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ---------------
 | 
					 | 
				
			||||||
# Log methods
 | 
					 | 
				
			||||||
# ---------------
 | 
					 | 
				
			||||||
func warn(text):
 | 
					 | 
				
			||||||
	return _log(types.warn, text)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func error(text):
 | 
					 | 
				
			||||||
	return _log(types.error, text)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func info(text):
 | 
					 | 
				
			||||||
	return _log(types.info, text)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func debug(text):
 | 
					 | 
				
			||||||
	return _log(types.debug, text)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# supply some text or the name of the deprecated method and the replacement.
 | 
					 | 
				
			||||||
func deprecated(text, alt_method=null):
 | 
					 | 
				
			||||||
	var msg = text
 | 
					 | 
				
			||||||
	if(alt_method):
 | 
					 | 
				
			||||||
		msg = str('The method ', text, ' is deprecated, use ', alt_method , ' instead.')
 | 
					 | 
				
			||||||
	return _log(types.deprecated, msg)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ---------------
 | 
					 | 
				
			||||||
# Misc
 | 
					 | 
				
			||||||
# ---------------
 | 
					 | 
				
			||||||
func get_gut():
 | 
					 | 
				
			||||||
	return _gut
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_gut(gut):
 | 
					 | 
				
			||||||
	_gut = gut
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func clear():
 | 
					 | 
				
			||||||
	for key in _logs:
 | 
					 | 
				
			||||||
		_logs[key].clear()
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,200 +0,0 @@
 | 
				
			||||||
# This class will generate method declaration lines based on method meta
 | 
					 | 
				
			||||||
# data.  It will create defaults that match the method data.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# --------------------
 | 
					 | 
				
			||||||
# function meta data
 | 
					 | 
				
			||||||
# --------------------
 | 
					 | 
				
			||||||
# name:
 | 
					 | 
				
			||||||
# flags:
 | 
					 | 
				
			||||||
# args: [{
 | 
					 | 
				
			||||||
# 	(class_name:),
 | 
					 | 
				
			||||||
# 	(hint:0),
 | 
					 | 
				
			||||||
# 	(hint_string:),
 | 
					 | 
				
			||||||
# 	(name:),
 | 
					 | 
				
			||||||
# 	(type:4),
 | 
					 | 
				
			||||||
# 	(usage:7)
 | 
					 | 
				
			||||||
# }]
 | 
					 | 
				
			||||||
# default_args []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _utils = load('res://addons/gut/utils.gd').new()
 | 
					 | 
				
			||||||
var _lgr = _utils.get_logger()
 | 
					 | 
				
			||||||
const PARAM_PREFIX = 'p_'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ------------------------------------------------------
 | 
					 | 
				
			||||||
# _supported_defaults
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# This array contains all the data types that are supported for default values.
 | 
					 | 
				
			||||||
# If a value is supported it will contain either an empty string or a prefix
 | 
					 | 
				
			||||||
# that should be used when setting the parameter default value.
 | 
					 | 
				
			||||||
# For example int, real, bool do not need anything func(p1=1, p2=2.2, p3=false)
 | 
					 | 
				
			||||||
# but things like Vectors and Colors do since only the parameters to create a
 | 
					 | 
				
			||||||
# new Vector or Color are included in the metadata.
 | 
					 | 
				
			||||||
# ------------------------------------------------------
 | 
					 | 
				
			||||||
	# TYPE_NIL = 0 — Variable is of type nil (only applied for null).
 | 
					 | 
				
			||||||
	# TYPE_BOOL = 1 — Variable is of type bool.
 | 
					 | 
				
			||||||
	# TYPE_INT = 2 — Variable is of type int.
 | 
					 | 
				
			||||||
	# TYPE_REAL = 3 — Variable is of type float/real.
 | 
					 | 
				
			||||||
	# TYPE_STRING = 4 — Variable is of type String.
 | 
					 | 
				
			||||||
	# TYPE_VECTOR2 = 5 — Variable is of type Vector2.
 | 
					 | 
				
			||||||
	# TYPE_RECT2 = 6 — Variable is of type Rect2.
 | 
					 | 
				
			||||||
	# TYPE_VECTOR3 = 7 — Variable is of type Vector3.
 | 
					 | 
				
			||||||
	# TYPE_COLOR = 14 — Variable is of type Color.
 | 
					 | 
				
			||||||
	# TYPE_OBJECT = 17 — Variable is of type Object.
 | 
					 | 
				
			||||||
	# TYPE_DICTIONARY = 18 — Variable is of type Dictionary.
 | 
					 | 
				
			||||||
	# TYPE_ARRAY = 19 — Variable is of type Array.
 | 
					 | 
				
			||||||
	# TYPE_VECTOR2_ARRAY = 24 — Variable is of type PoolVector2Array.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# TYPE_TRANSFORM2D = 8 — Variable is of type Transform2D.
 | 
					 | 
				
			||||||
# TYPE_PLANE = 9 — Variable is of type Plane.
 | 
					 | 
				
			||||||
# TYPE_QUAT = 10 — Variable is of type Quat.
 | 
					 | 
				
			||||||
# TYPE_AABB = 11 — Variable is of type AABB.
 | 
					 | 
				
			||||||
# TYPE_BASIS = 12 — Variable is of type Basis.
 | 
					 | 
				
			||||||
# TYPE_TRANSFORM = 13 — Variable is of type Transform.
 | 
					 | 
				
			||||||
# TYPE_NODE_PATH = 15 — Variable is of type NodePath.
 | 
					 | 
				
			||||||
# TYPE_RID = 16 — Variable is of type RID.
 | 
					 | 
				
			||||||
# TYPE_RAW_ARRAY = 20 — Variable is of type PoolByteArray.
 | 
					 | 
				
			||||||
# TYPE_INT_ARRAY = 21 — Variable is of type PoolIntArray.
 | 
					 | 
				
			||||||
# TYPE_REAL_ARRAY = 22 — Variable is of type PoolRealArray.
 | 
					 | 
				
			||||||
# TYPE_STRING_ARRAY = 23 — Variable is of type PoolStringArray.
 | 
					 | 
				
			||||||
# TYPE_VECTOR3_ARRAY = 25 — Variable is of type PoolVector3Array.
 | 
					 | 
				
			||||||
# TYPE_COLOR_ARRAY = 26 — Variable is of type PoolColorArray.
 | 
					 | 
				
			||||||
# TYPE_MAX = 27 — Marker for end of type constants.
 | 
					 | 
				
			||||||
# ------------------------------------------------------
 | 
					 | 
				
			||||||
var _supported_defaults = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _init():
 | 
					 | 
				
			||||||
	for i in range(TYPE_MAX):
 | 
					 | 
				
			||||||
		_supported_defaults.append(null)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	# These types do not require a prefix for defaults
 | 
					 | 
				
			||||||
	_supported_defaults[TYPE_NIL] = ''
 | 
					 | 
				
			||||||
	_supported_defaults[TYPE_BOOL] = ''
 | 
					 | 
				
			||||||
	_supported_defaults[TYPE_INT] = ''
 | 
					 | 
				
			||||||
	_supported_defaults[TYPE_REAL] = ''
 | 
					 | 
				
			||||||
	_supported_defaults[TYPE_OBJECT] = ''
 | 
					 | 
				
			||||||
	_supported_defaults[TYPE_ARRAY] = ''
 | 
					 | 
				
			||||||
	_supported_defaults[TYPE_STRING] = ''
 | 
					 | 
				
			||||||
	_supported_defaults[TYPE_DICTIONARY] = ''
 | 
					 | 
				
			||||||
	_supported_defaults[TYPE_VECTOR2_ARRAY] = ''
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	# These require a prefix for whatever default is provided
 | 
					 | 
				
			||||||
	_supported_defaults[TYPE_VECTOR2] = 'Vector2'
 | 
					 | 
				
			||||||
	_supported_defaults[TYPE_RECT2] = 'Rect2'
 | 
					 | 
				
			||||||
	_supported_defaults[TYPE_VECTOR3] = 'Vector3'
 | 
					 | 
				
			||||||
	_supported_defaults[TYPE_COLOR] = 'Color'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ###############
 | 
					 | 
				
			||||||
# Private
 | 
					 | 
				
			||||||
# ###############
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _is_supported_default(type_flag):
 | 
					 | 
				
			||||||
	return type_flag >= 0 and type_flag < _supported_defaults.size() and [type_flag] != null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Creates a list of parameters with defaults of null unless a default value is
 | 
					 | 
				
			||||||
# found in the metadata.  If a default is found in the meta then it is used if
 | 
					 | 
				
			||||||
# it is one we know how support.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# If a default is found that we don't know how to handle then this method will
 | 
					 | 
				
			||||||
# return null.
 | 
					 | 
				
			||||||
func _get_arg_text(method_meta):
 | 
					 | 
				
			||||||
	var text = ''
 | 
					 | 
				
			||||||
	var args = method_meta.args
 | 
					 | 
				
			||||||
	var defaults = []
 | 
					 | 
				
			||||||
	var has_unsupported_defaults = false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	# fill up the defaults with null defaults for everything that doesn't have
 | 
					 | 
				
			||||||
	# a default in the meta data.  default_args is an array of default values
 | 
					 | 
				
			||||||
	# for the last n parameters where n is the size of default_args so we only
 | 
					 | 
				
			||||||
	# add nulls for everything up to the first parameter with a default.
 | 
					 | 
				
			||||||
	for i in range(args.size() - method_meta.default_args.size()):
 | 
					 | 
				
			||||||
		defaults.append('null')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	# Add meta-data defaults.
 | 
					 | 
				
			||||||
	for i in range(method_meta.default_args.size()):
 | 
					 | 
				
			||||||
		var t = args[defaults.size()]['type']
 | 
					 | 
				
			||||||
		var value = ''
 | 
					 | 
				
			||||||
		if(_is_supported_default(t)):
 | 
					 | 
				
			||||||
			# strings are special, they need quotes around the value
 | 
					 | 
				
			||||||
			if(t == TYPE_STRING):
 | 
					 | 
				
			||||||
				value = str("'", str(method_meta.default_args[i]), "'")
 | 
					 | 
				
			||||||
			# Colors need the parens but things like Vector2 and Rect2 don't
 | 
					 | 
				
			||||||
			elif(t == TYPE_COLOR):
 | 
					 | 
				
			||||||
				value = str(_supported_defaults[t], '(', str(method_meta.default_args[i]), ')')
 | 
					 | 
				
			||||||
			elif(t == TYPE_OBJECT):
 | 
					 | 
				
			||||||
				if(str(method_meta.default_args[i]) == "[Object:null]"):
 | 
					 | 
				
			||||||
					value = str(_supported_defaults[t], 'null')
 | 
					 | 
				
			||||||
				else:
 | 
					 | 
				
			||||||
					value = str(_supported_defaults[t], str(method_meta.default_args[i]).to_lower())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			# Everything else puts the prefix (if one is there) form _supported_defaults
 | 
					 | 
				
			||||||
			# in front.  The to_lower is used b/c for some reason the defaults for
 | 
					 | 
				
			||||||
			# null, true, false are all "Null", "True", "False".
 | 
					 | 
				
			||||||
			else:
 | 
					 | 
				
			||||||
				value = str(_supported_defaults[t], str(method_meta.default_args[i]).to_lower())
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			_lgr.warn(str(
 | 
					 | 
				
			||||||
				'Unsupported default param type:  ',method_meta.name, '-', args[defaults.size()].name, ' ', t, ' = ', method_meta.default_args[i]))
 | 
					 | 
				
			||||||
			value = str('unsupported=',t)
 | 
					 | 
				
			||||||
			has_unsupported_defaults = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		defaults.append(value)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	# construct the string of parameters
 | 
					 | 
				
			||||||
	for i in range(args.size()):
 | 
					 | 
				
			||||||
		text += str(PARAM_PREFIX, args[i].name, '=', defaults[i])
 | 
					 | 
				
			||||||
		if(i != args.size() -1):
 | 
					 | 
				
			||||||
			text += ', '
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	# if we don't know how to make a default then we have to return null b/c
 | 
					 | 
				
			||||||
	# it will cause a runtime error and it's one thing we could return to let
 | 
					 | 
				
			||||||
	# callers know it didn't work.
 | 
					 | 
				
			||||||
	if(has_unsupported_defaults):
 | 
					 | 
				
			||||||
		text = null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return text
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ###############
 | 
					 | 
				
			||||||
# Public
 | 
					 | 
				
			||||||
# ###############
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Creates a delceration for a function based off of function metadata.  All
 | 
					 | 
				
			||||||
# types whose defaults are supported will have their values.  If a datatype
 | 
					 | 
				
			||||||
# is not supported and the parameter has a default, a warning message will be
 | 
					 | 
				
			||||||
# printed and the declaration will return null.
 | 
					 | 
				
			||||||
func get_decleration_text(meta):
 | 
					 | 
				
			||||||
	var param_text = _get_arg_text(meta)
 | 
					 | 
				
			||||||
	var text = null
 | 
					 | 
				
			||||||
	if(param_text != null):
 | 
					 | 
				
			||||||
		text = str('func ', meta.name, '(', param_text, '):')
 | 
					 | 
				
			||||||
	return text
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# creates a call to the function in meta in the super's class.
 | 
					 | 
				
			||||||
func get_super_call_text(meta):
 | 
					 | 
				
			||||||
	var params = ''
 | 
					 | 
				
			||||||
	var all_supported = true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i in range(meta.args.size()):
 | 
					 | 
				
			||||||
		params += PARAM_PREFIX + meta.args[i].name
 | 
					 | 
				
			||||||
		if(meta.args.size() > 1 and i != meta.args.size() -1):
 | 
					 | 
				
			||||||
			params += ', '
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return str('.', meta.name, '(', params, ')')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_spy_call_parameters_text(meta):
 | 
					 | 
				
			||||||
	var called_with = 'null'
 | 
					 | 
				
			||||||
	if(meta.args.size() > 0):
 | 
					 | 
				
			||||||
		called_with = '['
 | 
					 | 
				
			||||||
		for i in range(meta.args.size()):
 | 
					 | 
				
			||||||
			called_with += str(PARAM_PREFIX, meta.args[i].name)
 | 
					 | 
				
			||||||
			if(i < meta.args.size() - 1):
 | 
					 | 
				
			||||||
				called_with += ', '
 | 
					 | 
				
			||||||
		called_with += ']'
 | 
					 | 
				
			||||||
	return called_with
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_logger():
 | 
					 | 
				
			||||||
	return _lgr
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_logger(logger):
 | 
					 | 
				
			||||||
	_lgr = logger
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,38 +0,0 @@
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
# This datastructure represents a simple one-to-many relationship.  It manages
 | 
					 | 
				
			||||||
# a dictionary of value/array pairs.  It ignores duplicates of both the "one"
 | 
					 | 
				
			||||||
# and the "many".
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
var _items = {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# return the size of _items or the size of an element in _items if "one" was
 | 
					 | 
				
			||||||
# specified.
 | 
					 | 
				
			||||||
func size(one=null):
 | 
					 | 
				
			||||||
	var to_return = 0
 | 
					 | 
				
			||||||
	if(one == null):
 | 
					 | 
				
			||||||
		to_return = _items.size()
 | 
					 | 
				
			||||||
	elif(_items.has(one)):
 | 
					 | 
				
			||||||
		to_return = _items[one].size()
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Add an element to "one" if it does not already exist
 | 
					 | 
				
			||||||
func add(one, many_item):
 | 
					 | 
				
			||||||
	if(_items.has(one) and !_items[one].has(many_item)):
 | 
					 | 
				
			||||||
		_items[one].append(many_item)
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		_items[one] = [many_item]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func clear():
 | 
					 | 
				
			||||||
	_items.clear()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func has(one, many_item):
 | 
					 | 
				
			||||||
	var to_return = false
 | 
					 | 
				
			||||||
	if(_items.has(one)):
 | 
					 | 
				
			||||||
		to_return = _items[one].has(many_item)
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func to_s():
 | 
					 | 
				
			||||||
	var to_return = ''
 | 
					 | 
				
			||||||
	for key in _items:
 | 
					 | 
				
			||||||
		to_return += str(key, ":  ", _items[key], "\n")
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,250 +0,0 @@
 | 
				
			||||||
################################################################################
 | 
					 | 
				
			||||||
#(G)odot (U)nit (T)est class
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
################################################################################
 | 
					 | 
				
			||||||
#The MIT License (MIT)
 | 
					 | 
				
			||||||
#=====================
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
#Copyright (c) 2019 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.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# Version 6.8.1
 | 
					 | 
				
			||||||
################################################################################
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#-------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
# 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(value, size, pad_with=' '):
 | 
					 | 
				
			||||||
		var to_return = value
 | 
					 | 
				
			||||||
		for i in range(value.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
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,7 +0,0 @@
 | 
				
			||||||
[plugin]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
name="Gut"
 | 
					 | 
				
			||||||
description="Unit Testing tool for Godot."
 | 
					 | 
				
			||||||
author="Butch Wesley"
 | 
					 | 
				
			||||||
version="6.8.1"
 | 
					 | 
				
			||||||
script="gut_plugin.gd"
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,166 +0,0 @@
 | 
				
			||||||
################################################################################
 | 
					 | 
				
			||||||
#The MIT License (MIT)
 | 
					 | 
				
			||||||
#=====================
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
#Copyright (c) 2019 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.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
################################################################################
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Some arbitrary string that should never show up by accident.  If it does, then
 | 
					 | 
				
			||||||
# shame on  you.
 | 
					 | 
				
			||||||
const ARG_NOT_SET = '_*_argument_*_is_*_not_set_*_'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# This hash holds the objects that are being watched, the signals that are being
 | 
					 | 
				
			||||||
# watched, and an array of arrays that contains arguments that were passed
 | 
					 | 
				
			||||||
# each time the signal was emitted.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# For example:
 | 
					 | 
				
			||||||
#	_watched_signals => {
 | 
					 | 
				
			||||||
#		ref1 => {
 | 
					 | 
				
			||||||
#			'signal1' => [[], [], []],
 | 
					 | 
				
			||||||
#			'signal2' => [[p1, p2]],
 | 
					 | 
				
			||||||
#			'signal3' => [[p1]]
 | 
					 | 
				
			||||||
#		},
 | 
					 | 
				
			||||||
#		ref2 => {
 | 
					 | 
				
			||||||
#			'some_signal' => [],
 | 
					 | 
				
			||||||
#			'other_signal' => [[p1, p2, p3], [p1, p2, p3], [p1, p2, p3]]
 | 
					 | 
				
			||||||
#		}
 | 
					 | 
				
			||||||
#	}
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# In this sample:
 | 
					 | 
				
			||||||
#	- signal1 on the ref1 object was emitted 3 times and each time, zero
 | 
					 | 
				
			||||||
#	  parameters were passed.
 | 
					 | 
				
			||||||
#	- signal3 on ref1 was emitted once and passed a single parameter
 | 
					 | 
				
			||||||
#	- some_signal on ref2 was never emitted.
 | 
					 | 
				
			||||||
#	- other_signal on ref2 was emitted 3 times, each time with 3 parameters.
 | 
					 | 
				
			||||||
var _watched_signals = {}
 | 
					 | 
				
			||||||
var _utils = load('res://addons/gut/utils.gd').new()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _add_watched_signal(obj, name):
 | 
					 | 
				
			||||||
	# SHORTCIRCUIT - ignore dupes
 | 
					 | 
				
			||||||
	if(_watched_signals.has(obj) and _watched_signals[obj].has(name)):
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(!_watched_signals.has(obj)):
 | 
					 | 
				
			||||||
		_watched_signals[obj] = {name:[]}
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		_watched_signals[obj][name] = []
 | 
					 | 
				
			||||||
	obj.connect(name, self, '_on_watched_signal', [obj, name])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# This handles all the signals that are watched.  It supports up to 9 parameters
 | 
					 | 
				
			||||||
# which could be emitted by the signal and the two parameters used when it is
 | 
					 | 
				
			||||||
# connected via watch_signal.  I chose 9 since you can only specify up to 9
 | 
					 | 
				
			||||||
# parameters when dynamically calling a method via call (per the Godot
 | 
					 | 
				
			||||||
# documentation, i.e. some_object.call('some_method', 1, 2, 3...)).
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# Based on the documentation of emit_signal, it appears you can only pass up
 | 
					 | 
				
			||||||
# to 4 parameters when firing a signal.  I haven't verified this, but this should
 | 
					 | 
				
			||||||
# future proof this some if the value ever grows.
 | 
					 | 
				
			||||||
func _on_watched_signal(arg1=ARG_NOT_SET, arg2=ARG_NOT_SET, arg3=ARG_NOT_SET, \
 | 
					 | 
				
			||||||
                        arg4=ARG_NOT_SET, arg5=ARG_NOT_SET, arg6=ARG_NOT_SET, \
 | 
					 | 
				
			||||||
						arg7=ARG_NOT_SET, arg8=ARG_NOT_SET, arg9=ARG_NOT_SET, \
 | 
					 | 
				
			||||||
						arg10=ARG_NOT_SET, arg11=ARG_NOT_SET):
 | 
					 | 
				
			||||||
	var args = [arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	# strip off any unused vars.
 | 
					 | 
				
			||||||
	var idx = args.size() -1
 | 
					 | 
				
			||||||
	while(str(args[idx]) == ARG_NOT_SET):
 | 
					 | 
				
			||||||
		args.remove(idx)
 | 
					 | 
				
			||||||
		idx -= 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	# retrieve object and signal name from the array and remove them.  These
 | 
					 | 
				
			||||||
	# will always be at the end since they are added when the connect happens.
 | 
					 | 
				
			||||||
	var signal_name = args[args.size() -1]
 | 
					 | 
				
			||||||
	args.pop_back()
 | 
					 | 
				
			||||||
	var object = args[args.size() -1]
 | 
					 | 
				
			||||||
	args.pop_back()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_watched_signals[object][signal_name].append(args)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func does_object_have_signal(object, signal_name):
 | 
					 | 
				
			||||||
	var signals = object.get_signal_list()
 | 
					 | 
				
			||||||
	for i in range(signals.size()):
 | 
					 | 
				
			||||||
		if(signals[i]['name'] == signal_name):
 | 
					 | 
				
			||||||
			return true
 | 
					 | 
				
			||||||
	return false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func watch_signals(object):
 | 
					 | 
				
			||||||
	var signals = object.get_signal_list()
 | 
					 | 
				
			||||||
	for i in range(signals.size()):
 | 
					 | 
				
			||||||
		_add_watched_signal(object, signals[i]['name'])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func watch_signal(object, signal_name):
 | 
					 | 
				
			||||||
	var did = false
 | 
					 | 
				
			||||||
	if(does_object_have_signal(object, signal_name)):
 | 
					 | 
				
			||||||
		_add_watched_signal(object, signal_name)
 | 
					 | 
				
			||||||
		did = true
 | 
					 | 
				
			||||||
	return did
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_emit_count(object, signal_name):
 | 
					 | 
				
			||||||
	var to_return = -1
 | 
					 | 
				
			||||||
	if(is_watching(object, signal_name)):
 | 
					 | 
				
			||||||
		to_return = _watched_signals[object][signal_name].size()
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func did_emit(object, signal_name):
 | 
					 | 
				
			||||||
	var did = false
 | 
					 | 
				
			||||||
	if(is_watching(object, signal_name)):
 | 
					 | 
				
			||||||
		did = get_emit_count(object, signal_name) != 0
 | 
					 | 
				
			||||||
	return did
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func print_object_signals(object):
 | 
					 | 
				
			||||||
	var list = object.get_signal_list()
 | 
					 | 
				
			||||||
	for i in range(list.size()):
 | 
					 | 
				
			||||||
		print(list[i].name, "\n  ", list[i])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_signal_parameters(object, signal_name, index=-1):
 | 
					 | 
				
			||||||
	var params = null
 | 
					 | 
				
			||||||
	if(is_watching(object, signal_name)):
 | 
					 | 
				
			||||||
		var all_params = _watched_signals[object][signal_name]
 | 
					 | 
				
			||||||
		if(all_params.size() > 0):
 | 
					 | 
				
			||||||
			if(index == -1):
 | 
					 | 
				
			||||||
				index = all_params.size() -1
 | 
					 | 
				
			||||||
			params = all_params[index]
 | 
					 | 
				
			||||||
	return params
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func is_watching_object(object):
 | 
					 | 
				
			||||||
	return _watched_signals.has(object)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func is_watching(object, signal_name):
 | 
					 | 
				
			||||||
	return _watched_signals.has(object) and _watched_signals[object].has(signal_name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func clear():
 | 
					 | 
				
			||||||
	for obj in _watched_signals:
 | 
					 | 
				
			||||||
		for signal_name in _watched_signals[obj]:
 | 
					 | 
				
			||||||
			if(_utils.is_not_freed(obj)):
 | 
					 | 
				
			||||||
				obj.disconnect(signal_name, self, '_on_watched_signal')
 | 
					 | 
				
			||||||
	_watched_signals.clear()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Returns a list of all the signal names that were emitted by the object.
 | 
					 | 
				
			||||||
# If the object is not being watched then an empty list is returned.
 | 
					 | 
				
			||||||
func get_signals_emitted(obj):
 | 
					 | 
				
			||||||
	var emitted = []
 | 
					 | 
				
			||||||
	if(is_watching_object(obj)):
 | 
					 | 
				
			||||||
		for signal_name in _watched_signals[obj]:
 | 
					 | 
				
			||||||
			if(_watched_signals[obj][signal_name].size() > 0):
 | 
					 | 
				
			||||||
				emitted.append(signal_name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return emitted
 | 
					 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -1,96 +0,0 @@
 | 
				
			||||||
# {
 | 
					 | 
				
			||||||
#   instance_id_or_path1:{
 | 
					 | 
				
			||||||
#       method1:[ [p1, p2], [p1, p2] ],
 | 
					 | 
				
			||||||
#       method2:[ [p1, p2], [p1, p2] ]
 | 
					 | 
				
			||||||
#   },
 | 
					 | 
				
			||||||
#   instance_id_or_path1:{
 | 
					 | 
				
			||||||
#       method1:[ [p1, p2], [p1, p2] ],
 | 
					 | 
				
			||||||
#       method2:[ [p1, p2], [p1, p2] ]
 | 
					 | 
				
			||||||
#   },
 | 
					 | 
				
			||||||
# }
 | 
					 | 
				
			||||||
var _calls = {}
 | 
					 | 
				
			||||||
var _utils = load('res://addons/gut/utils.gd').new()
 | 
					 | 
				
			||||||
var _lgr = _utils.get_logger()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _get_params_as_string(params):
 | 
					 | 
				
			||||||
	var to_return = ''
 | 
					 | 
				
			||||||
	if(params == null):
 | 
					 | 
				
			||||||
		return ''
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i in range(params.size()):
 | 
					 | 
				
			||||||
		if(params[i] == null):
 | 
					 | 
				
			||||||
			to_return += 'null'
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			if(typeof(params[i]) == TYPE_STRING):
 | 
					 | 
				
			||||||
				to_return += str('"', params[i], '"')
 | 
					 | 
				
			||||||
			else:
 | 
					 | 
				
			||||||
				to_return += str(params[i])
 | 
					 | 
				
			||||||
		if(i != params.size() -1):
 | 
					 | 
				
			||||||
			to_return += ', '
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func add_call(variant, method_name, parameters=null):
 | 
					 | 
				
			||||||
	if(!_calls.has(variant)):
 | 
					 | 
				
			||||||
		_calls[variant] = {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(!_calls[variant].has(method_name)):
 | 
					 | 
				
			||||||
		_calls[variant][method_name] = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_calls[variant][method_name].append(parameters)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func was_called(variant, method_name, parameters=null):
 | 
					 | 
				
			||||||
	var to_return = false
 | 
					 | 
				
			||||||
	if(_calls.has(variant) and _calls[variant].has(method_name)):
 | 
					 | 
				
			||||||
		if(parameters):
 | 
					 | 
				
			||||||
			to_return =  _calls[variant][method_name].has(parameters)
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			to_return = true
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_call_parameters(variant, method_name, index=-1):
 | 
					 | 
				
			||||||
	var to_return = null
 | 
					 | 
				
			||||||
	var get_index = -1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(_calls.has(variant) and _calls[variant].has(method_name)):
 | 
					 | 
				
			||||||
		var call_size = _calls[variant][method_name].size()
 | 
					 | 
				
			||||||
		if(index == -1):
 | 
					 | 
				
			||||||
			# get the most recent call by default
 | 
					 | 
				
			||||||
			get_index =  call_size -1
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			get_index = index
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if(get_index < call_size):
 | 
					 | 
				
			||||||
			to_return = _calls[variant][method_name][get_index]
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			_lgr.error(str('Specified index ', index, ' is outside range of the number of registered calls:  ', call_size))
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func call_count(instance, method_name, parameters=null):
 | 
					 | 
				
			||||||
	var to_return = 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(was_called(instance, method_name)):
 | 
					 | 
				
			||||||
		if(parameters):
 | 
					 | 
				
			||||||
			for i in range(_calls[instance][method_name].size()):
 | 
					 | 
				
			||||||
				if(_calls[instance][method_name][i] == parameters):
 | 
					 | 
				
			||||||
					to_return += 1
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			to_return = _calls[instance][method_name].size()
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func clear():
 | 
					 | 
				
			||||||
	_calls = {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_call_list_as_string(instance):
 | 
					 | 
				
			||||||
	var to_return = ''
 | 
					 | 
				
			||||||
	if(_calls.has(instance)):
 | 
					 | 
				
			||||||
		for method in _calls[instance]:
 | 
					 | 
				
			||||||
			for i in range(_calls[instance][method].size()):
 | 
					 | 
				
			||||||
				to_return += str(method, '(', _get_params_as_string(_calls[instance][method][i]), ")\n")
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_logger():
 | 
					 | 
				
			||||||
	return _lgr
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_logger(logger):
 | 
					 | 
				
			||||||
	_lgr = logger
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,43 +0,0 @@
 | 
				
			||||||
var return_val = null
 | 
					 | 
				
			||||||
var stub_target = null
 | 
					 | 
				
			||||||
var target_subpath = null
 | 
					 | 
				
			||||||
var parameters = null
 | 
					 | 
				
			||||||
var stub_method = null
 | 
					 | 
				
			||||||
var call_super = false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const NOT_SET = '|_1_this_is_not_set_1_|'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _init(target=null, method=null, subpath=null):
 | 
					 | 
				
			||||||
	stub_target = target
 | 
					 | 
				
			||||||
	stub_method = method
 | 
					 | 
				
			||||||
	target_subpath = subpath
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func to_return(val):
 | 
					 | 
				
			||||||
	return_val = val
 | 
					 | 
				
			||||||
	call_super = false
 | 
					 | 
				
			||||||
	return self
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func to_do_nothing():
 | 
					 | 
				
			||||||
	return to_return(null)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func to_call_super():
 | 
					 | 
				
			||||||
	call_super = true
 | 
					 | 
				
			||||||
	return self
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func when_passed(p1=NOT_SET,p2=NOT_SET,p3=NOT_SET,p4=NOT_SET,p5=NOT_SET,p6=NOT_SET,p7=NOT_SET,p8=NOT_SET,p9=NOT_SET,p10=NOT_SET):
 | 
					 | 
				
			||||||
	parameters = [p1,p2,p3,p4,p5,p6,p7,p8,p9,p10]
 | 
					 | 
				
			||||||
	var idx = 0
 | 
					 | 
				
			||||||
	while(idx < parameters.size()):
 | 
					 | 
				
			||||||
		if(str(parameters[idx]) == NOT_SET):
 | 
					 | 
				
			||||||
			parameters.remove(idx)
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			idx += 1
 | 
					 | 
				
			||||||
	return self
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func to_s():
 | 
					 | 
				
			||||||
	var base_string = str(stub_target, '[', target_subpath, '].', stub_method)
 | 
					 | 
				
			||||||
	if(call_super):
 | 
					 | 
				
			||||||
		base_string += " to call SUPER"
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		base_string += str(' with (', parameters, ') = ', return_val)
 | 
					 | 
				
			||||||
	return base_string
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,162 +0,0 @@
 | 
				
			||||||
# {
 | 
					 | 
				
			||||||
# 	inst_id_or_path1:{
 | 
					 | 
				
			||||||
# 		method_name1: [StubParams, StubParams],
 | 
					 | 
				
			||||||
# 		method_name2: [StubParams, StubParams]
 | 
					 | 
				
			||||||
# 	},
 | 
					 | 
				
			||||||
# 	inst_id_or_path2:{
 | 
					 | 
				
			||||||
# 		method_name1: [StubParams, StubParams],
 | 
					 | 
				
			||||||
# 		method_name2: [StubParams, StubParams]
 | 
					 | 
				
			||||||
# 	}
 | 
					 | 
				
			||||||
# }
 | 
					 | 
				
			||||||
var returns = {}
 | 
					 | 
				
			||||||
var _utils = load('res://addons/gut/utils.gd').new()
 | 
					 | 
				
			||||||
var _lgr = _utils.get_logger()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _is_instance(obj):
 | 
					 | 
				
			||||||
	return typeof(obj) == TYPE_OBJECT and !obj.has_method('new')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _make_key_from_metadata(doubled):
 | 
					 | 
				
			||||||
	var to_return = doubled.__gut_metadata_.path
 | 
					 | 
				
			||||||
	if(doubled.__gut_metadata_.subpath != ''):
 | 
					 | 
				
			||||||
		to_return += str('-', doubled.__gut_metadata_.subpath)
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Creates they key for the returns hash based on the type of object passed in
 | 
					 | 
				
			||||||
# obj could be a string of a path to a script with an optional subpath or
 | 
					 | 
				
			||||||
# it could be an instance of a doubled object.
 | 
					 | 
				
			||||||
func _make_key_from_variant(obj, subpath=null):
 | 
					 | 
				
			||||||
	var to_return = null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	match typeof(obj):
 | 
					 | 
				
			||||||
		TYPE_STRING:
 | 
					 | 
				
			||||||
			# this has to match what is done in _make_key_from_metadata
 | 
					 | 
				
			||||||
			to_return = obj
 | 
					 | 
				
			||||||
			if(subpath != null and subpath != ''):
 | 
					 | 
				
			||||||
				to_return += str('-', subpath)
 | 
					 | 
				
			||||||
		TYPE_OBJECT:
 | 
					 | 
				
			||||||
			if(_is_instance(obj)):
 | 
					 | 
				
			||||||
				to_return = _make_key_from_metadata(obj)
 | 
					 | 
				
			||||||
			elif(_utils.is_native_class(obj)):
 | 
					 | 
				
			||||||
				to_return = _utils.get_native_class_name(obj)
 | 
					 | 
				
			||||||
			else:
 | 
					 | 
				
			||||||
				to_return = obj.resource_path
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _add_obj_method(obj, method, subpath=null):
 | 
					 | 
				
			||||||
	var key = _make_key_from_variant(obj, subpath)
 | 
					 | 
				
			||||||
	if(_is_instance(obj)):
 | 
					 | 
				
			||||||
		key = obj
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(!returns.has(key)):
 | 
					 | 
				
			||||||
		returns[key] = {}
 | 
					 | 
				
			||||||
	if(!returns[key].has(method)):
 | 
					 | 
				
			||||||
		returns[key][method] = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return key
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ##############
 | 
					 | 
				
			||||||
# Public
 | 
					 | 
				
			||||||
# ##############
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# TODO: This method is only used in tests and should be refactored out.  It
 | 
					 | 
				
			||||||
# does not support inner classes and isn't helpful.
 | 
					 | 
				
			||||||
func set_return(obj, method, value, parameters=null):
 | 
					 | 
				
			||||||
	var key = _add_obj_method(obj, method)
 | 
					 | 
				
			||||||
	var sp = _utils.StubParams.new(key, method)
 | 
					 | 
				
			||||||
	sp.parameters = parameters
 | 
					 | 
				
			||||||
	sp.return_val = value
 | 
					 | 
				
			||||||
	returns[key][method].append(sp)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func add_stub(stub_params):
 | 
					 | 
				
			||||||
	var key = _add_obj_method(stub_params.stub_target, stub_params.stub_method, stub_params.target_subpath)
 | 
					 | 
				
			||||||
	returns[key][stub_params.stub_method].append(stub_params)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Searches returns for an entry that matches the instance or the class that
 | 
					 | 
				
			||||||
# passed in obj is.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# obj can be an instance, class, or a path.
 | 
					 | 
				
			||||||
func _find_stub(obj, method, parameters=null):
 | 
					 | 
				
			||||||
	var key = _make_key_from_variant(obj)
 | 
					 | 
				
			||||||
	var to_return = null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(_is_instance(obj)):
 | 
					 | 
				
			||||||
		if(returns.has(obj) and returns[obj].has(method)):
 | 
					 | 
				
			||||||
			key = obj
 | 
					 | 
				
			||||||
		elif(obj.get('__gut_metadata_')):
 | 
					 | 
				
			||||||
			key = _make_key_from_metadata(obj)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(returns.has(key) and returns[key].has(method)):
 | 
					 | 
				
			||||||
		var param_idx = -1
 | 
					 | 
				
			||||||
		var null_idx = -1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for i in range(returns[key][method].size()):
 | 
					 | 
				
			||||||
			if(returns[key][method][i].parameters == parameters):
 | 
					 | 
				
			||||||
				param_idx = i
 | 
					 | 
				
			||||||
			if(returns[key][method][i].parameters == null):
 | 
					 | 
				
			||||||
				null_idx = i
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		# We have matching parameter values so return the stub value for that
 | 
					 | 
				
			||||||
		if(param_idx != -1):
 | 
					 | 
				
			||||||
			to_return = returns[key][method][param_idx]
 | 
					 | 
				
			||||||
		# We found a case where the parameters were not specified so return
 | 
					 | 
				
			||||||
		# parameters for that
 | 
					 | 
				
			||||||
		elif(null_idx != -1):
 | 
					 | 
				
			||||||
			to_return = returns[key][method][null_idx]
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			_lgr.warn(str('Call to [', method, '] was not stubbed for the supplied parameters ', parameters, '.  Null was returned.'))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Gets a stubbed return value for the object and method passed in.  If the
 | 
					 | 
				
			||||||
# instance was stubbed it will use that, otherwise it will use the path and
 | 
					 | 
				
			||||||
# subpath of the object to try to find a value.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# It will also use the optional list of parameter values to find a value.  If
 | 
					 | 
				
			||||||
# the object was stubbed with no parameters than any parameters will match.
 | 
					 | 
				
			||||||
# If it was stubbed with specific parameter values then it will try to match.
 | 
					 | 
				
			||||||
# If the parameters do not match BUT there was also an empty parameter list stub
 | 
					 | 
				
			||||||
# then it will return those.
 | 
					 | 
				
			||||||
# If it cannot find anything that matches then null is returned.for
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# Parameters
 | 
					 | 
				
			||||||
# obj:  this should be an instance of a doubled object.
 | 
					 | 
				
			||||||
# method:  the method called
 | 
					 | 
				
			||||||
# parameters:  optional array of parameter vales to find a return value for.
 | 
					 | 
				
			||||||
func get_return(obj, method, parameters=null):
 | 
					 | 
				
			||||||
	var stub_info = _find_stub(obj, method, parameters)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(stub_info != null):
 | 
					 | 
				
			||||||
		return stub_info.return_val
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		return null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func should_call_super(obj, method, parameters=null):
 | 
					 | 
				
			||||||
	var stub_info = _find_stub(obj, method, parameters)
 | 
					 | 
				
			||||||
	if(stub_info != null):
 | 
					 | 
				
			||||||
		return stub_info.call_super
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		# this log message is here because of how the generated doubled scripts
 | 
					 | 
				
			||||||
		# are structured.  With this log msg here, you will only see one
 | 
					 | 
				
			||||||
		# "unstubbed" info instead of multiple.
 | 
					 | 
				
			||||||
		_lgr.info('Unstubbed call to ' + method + '::' + str(obj))
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func clear():
 | 
					 | 
				
			||||||
	returns.clear()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_logger():
 | 
					 | 
				
			||||||
	return _lgr
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_logger(logger):
 | 
					 | 
				
			||||||
	_lgr = logger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func to_s():
 | 
					 | 
				
			||||||
	var text = ''
 | 
					 | 
				
			||||||
	for thing in returns:
 | 
					 | 
				
			||||||
		text += str(thing) + "\n"
 | 
					 | 
				
			||||||
		for method in returns[thing]:
 | 
					 | 
				
			||||||
			text += str("\t", method, "\n")
 | 
					 | 
				
			||||||
			for i in range(returns[thing][method].size()):
 | 
					 | 
				
			||||||
				text += "\t\t" + returns[thing][method][i].to_s() + "\n"
 | 
					 | 
				
			||||||
	return text
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,153 +0,0 @@
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
# Contains all the results of a single test.  Allows for multiple asserts results
 | 
					 | 
				
			||||||
# and pending calls.
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
class Test:
 | 
					 | 
				
			||||||
	var pass_texts = []
 | 
					 | 
				
			||||||
	var fail_texts = []
 | 
					 | 
				
			||||||
	var pending_texts = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func to_s():
 | 
					 | 
				
			||||||
		var pad = '     '
 | 
					 | 
				
			||||||
		var to_return = ''
 | 
					 | 
				
			||||||
		for i in range(fail_texts.size()):
 | 
					 | 
				
			||||||
			to_return += str(pad, 'FAILED:  ', fail_texts[i], "\n")
 | 
					 | 
				
			||||||
		for i in range(pending_texts.size()):
 | 
					 | 
				
			||||||
			to_return += str(pad, 'Pending:  ', pending_texts[i], "\n")
 | 
					 | 
				
			||||||
		return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
# Contains all the results for a single test-script/inner class.  Persists the
 | 
					 | 
				
			||||||
# names of the tests and results and the order in which  the tests were run.
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
class TestScript:
 | 
					 | 
				
			||||||
	var name = 'NOT_SET'
 | 
					 | 
				
			||||||
	#
 | 
					 | 
				
			||||||
	var _tests = {}
 | 
					 | 
				
			||||||
	var _test_order = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func _init(script_name):
 | 
					 | 
				
			||||||
		name = script_name
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func get_pass_count():
 | 
					 | 
				
			||||||
		var count = 0
 | 
					 | 
				
			||||||
		for key in _tests:
 | 
					 | 
				
			||||||
			count += _tests[key].pass_texts.size()
 | 
					 | 
				
			||||||
		return count
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func get_fail_count():
 | 
					 | 
				
			||||||
		var count = 0
 | 
					 | 
				
			||||||
		for key in _tests:
 | 
					 | 
				
			||||||
			count += _tests[key].fail_texts.size()
 | 
					 | 
				
			||||||
		return count
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func get_pending_count():
 | 
					 | 
				
			||||||
		var count = 0
 | 
					 | 
				
			||||||
		for key in _tests:
 | 
					 | 
				
			||||||
			count += _tests[key].pending_texts.size()
 | 
					 | 
				
			||||||
		return count
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func get_test_obj(name):
 | 
					 | 
				
			||||||
		if(!_tests.has(name)):
 | 
					 | 
				
			||||||
			_tests[name] = Test.new()
 | 
					 | 
				
			||||||
			_test_order.append(name)
 | 
					 | 
				
			||||||
		return _tests[name]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func add_pass(test_name, reason):
 | 
					 | 
				
			||||||
		var t = get_test_obj(test_name)
 | 
					 | 
				
			||||||
		t.pass_texts.append(reason)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func add_fail(test_name, reason):
 | 
					 | 
				
			||||||
		var t = get_test_obj(test_name)
 | 
					 | 
				
			||||||
		t.fail_texts.append(reason)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func add_pending(test_name, reason):
 | 
					 | 
				
			||||||
		var t = get_test_obj(test_name)
 | 
					 | 
				
			||||||
		t.pending_texts.append(reason)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
# Summary Class
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# This class holds the results of all the test scripts and Inner Classes that
 | 
					 | 
				
			||||||
# were run.
 | 
					 | 
				
			||||||
# -------------------------------------------d-----------------------------------
 | 
					 | 
				
			||||||
var _scripts = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func add_script(name):
 | 
					 | 
				
			||||||
	_scripts.append(TestScript.new(name))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_scripts():
 | 
					 | 
				
			||||||
	return _scripts
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_current_script():
 | 
					 | 
				
			||||||
	return _scripts[_scripts.size() - 1]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func add_test(test_name):
 | 
					 | 
				
			||||||
	get_current_script().get_test_obj(test_name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func add_pass(test_name, reason = ''):
 | 
					 | 
				
			||||||
	get_current_script().add_pass(test_name, reason)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func add_fail(test_name, reason = ''):
 | 
					 | 
				
			||||||
	get_current_script().add_fail(test_name, reason)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func add_pending(test_name, reason = ''):
 | 
					 | 
				
			||||||
	get_current_script().add_pending(test_name, reason)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_test_text(test_name):
 | 
					 | 
				
			||||||
	return test_name + "\n" + get_current_script().get_test_obj(test_name).to_s()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Gets the count of unique script names minus the .<Inner Class Name> at the
 | 
					 | 
				
			||||||
# end.  Used for displaying the number of scripts without including all the
 | 
					 | 
				
			||||||
# Inner Classes.
 | 
					 | 
				
			||||||
func get_non_inner_class_script_count():
 | 
					 | 
				
			||||||
	var count = 0
 | 
					 | 
				
			||||||
	var unique_scripts = {}
 | 
					 | 
				
			||||||
	for i in range(_scripts.size()):
 | 
					 | 
				
			||||||
		var ext_loc = _scripts[i].name.find_last('.gd.')
 | 
					 | 
				
			||||||
		if(ext_loc == -1):
 | 
					 | 
				
			||||||
			unique_scripts[_scripts[i].name] = 1
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			unique_scripts[_scripts[i].name.substr(0, ext_loc + 3)] = 1
 | 
					 | 
				
			||||||
	return unique_scripts.keys().size()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_totals():
 | 
					 | 
				
			||||||
	var totals = {
 | 
					 | 
				
			||||||
		passing = 0,
 | 
					 | 
				
			||||||
		pending = 0,
 | 
					 | 
				
			||||||
		failing = 0,
 | 
					 | 
				
			||||||
		tests = 0,
 | 
					 | 
				
			||||||
		scripts = 0
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for s in range(_scripts.size()):
 | 
					 | 
				
			||||||
		totals.passing += _scripts[s].get_pass_count()
 | 
					 | 
				
			||||||
		totals.pending += _scripts[s].get_pending_count()
 | 
					 | 
				
			||||||
		totals.failing += _scripts[s].get_fail_count()
 | 
					 | 
				
			||||||
		totals.tests += _scripts[s]._test_order.size()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	totals.scripts = get_non_inner_class_script_count()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return totals
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_summary_text():
 | 
					 | 
				
			||||||
	var _totals = get_totals()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var to_return = ''
 | 
					 | 
				
			||||||
	for s in range(_scripts.size()):
 | 
					 | 
				
			||||||
		if(_scripts[s].get_fail_count() > 0 or _scripts[s].get_pending_count() > 0):
 | 
					 | 
				
			||||||
			to_return += _scripts[s].name + "\n"
 | 
					 | 
				
			||||||
		for t in range(_scripts[s]._test_order.size()):
 | 
					 | 
				
			||||||
			var tname = _scripts[s]._test_order[t]
 | 
					 | 
				
			||||||
			var test = _scripts[s].get_test_obj(tname)
 | 
					 | 
				
			||||||
			if(test.fail_texts.size() > 0 or test.pending_texts.size() > 0):
 | 
					 | 
				
			||||||
				to_return += str('  - ', tname, "\n", test.to_s())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var header = "***  Totals  ***\n"
 | 
					 | 
				
			||||||
	header += str('  scripts:          ', get_non_inner_class_script_count(), "\n")
 | 
					 | 
				
			||||||
	header += str('  tests:            ', _totals.tests, "\n")
 | 
					 | 
				
			||||||
	header += str('  passing asserts:  ', _totals.passing, "\n")
 | 
					 | 
				
			||||||
	header += str('  failing asserts:  ',_totals.failing, "\n")
 | 
					 | 
				
			||||||
	header += str('  pending:          ', _totals.pending, "\n")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return to_return + "\n" + header
 | 
					 | 
				
			||||||
							
								
								
									
										1088
									
								
								addons/gut/test.gd
									
										
									
									
									
								
							
							
						
						
									
										1088
									
								
								addons/gut/test.gd
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
					@ -1,241 +0,0 @@
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
# Used to keep track of info about each test ran.
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
class Test:
 | 
					 | 
				
			||||||
	# indicator if it passed or not.  defaults to true since it takes only
 | 
					 | 
				
			||||||
	# one failure to make it not pass.  _fail in gut will set this.
 | 
					 | 
				
			||||||
	var passed = true
 | 
					 | 
				
			||||||
	# the name of the function
 | 
					 | 
				
			||||||
	var name = ""
 | 
					 | 
				
			||||||
	# flag to know if the name has been printed yet.
 | 
					 | 
				
			||||||
	var has_printed_name = false
 | 
					 | 
				
			||||||
	# the line number the test is on
 | 
					 | 
				
			||||||
	var line_number = -1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
class TestScript:
 | 
					 | 
				
			||||||
	var inner_class_name = null
 | 
					 | 
				
			||||||
	var tests = []
 | 
					 | 
				
			||||||
	var path = null
 | 
					 | 
				
			||||||
	var _utils = null
 | 
					 | 
				
			||||||
	var _lgr = null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func _init(utils=null, logger=null):
 | 
					 | 
				
			||||||
		_utils = utils
 | 
					 | 
				
			||||||
		_lgr = logger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func to_s():
 | 
					 | 
				
			||||||
		var to_return = path
 | 
					 | 
				
			||||||
		if(inner_class_name != null):
 | 
					 | 
				
			||||||
			to_return += str('.', inner_class_name)
 | 
					 | 
				
			||||||
		to_return += "\n"
 | 
					 | 
				
			||||||
		for i in range(tests.size()):
 | 
					 | 
				
			||||||
			to_return += str('  ', tests[i].name, "\n")
 | 
					 | 
				
			||||||
		return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func get_new():
 | 
					 | 
				
			||||||
		var TheScript = load(path)
 | 
					 | 
				
			||||||
		var inst = null
 | 
					 | 
				
			||||||
		if(inner_class_name != null):
 | 
					 | 
				
			||||||
			inst = TheScript.get(inner_class_name).new()
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			inst = TheScript.new()
 | 
					 | 
				
			||||||
		return inst
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func get_full_name():
 | 
					 | 
				
			||||||
		var to_return = path
 | 
					 | 
				
			||||||
		if(inner_class_name != null):
 | 
					 | 
				
			||||||
			to_return += '.' + inner_class_name
 | 
					 | 
				
			||||||
		return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func get_filename():
 | 
					 | 
				
			||||||
		return path.get_file()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func has_inner_class():
 | 
					 | 
				
			||||||
		return inner_class_name != null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func export_to(config_file, section):
 | 
					 | 
				
			||||||
		config_file.set_value(section, 'path', path)
 | 
					 | 
				
			||||||
		config_file.set_value(section, 'inner_class', inner_class_name)
 | 
					 | 
				
			||||||
		var names = []
 | 
					 | 
				
			||||||
		for i in range(tests.size()):
 | 
					 | 
				
			||||||
			names.append(tests[i].name)
 | 
					 | 
				
			||||||
		config_file.set_value(section, 'tests', names)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func _remap_path(path):
 | 
					 | 
				
			||||||
		var to_return = path
 | 
					 | 
				
			||||||
		if(!_utils.file_exists(path)):
 | 
					 | 
				
			||||||
			_lgr.debug('Checking for remap for:  ' + path)
 | 
					 | 
				
			||||||
			var remap_path = path.get_basename() + '.gd.remap'
 | 
					 | 
				
			||||||
			if(_utils.file_exists(remap_path)):
 | 
					 | 
				
			||||||
				var cf = ConfigFile.new()
 | 
					 | 
				
			||||||
				cf.load(remap_path)
 | 
					 | 
				
			||||||
				to_return = cf.get_value('remap', 'path')
 | 
					 | 
				
			||||||
			else:
 | 
					 | 
				
			||||||
				_lgr.warn('Could not find remap file ' + remap_path)
 | 
					 | 
				
			||||||
		return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	func import_from(config_file, section):
 | 
					 | 
				
			||||||
		path = config_file.get_value(section, 'path')
 | 
					 | 
				
			||||||
		path = _remap_path(path)
 | 
					 | 
				
			||||||
		var test_names = config_file.get_value(section, 'tests')
 | 
					 | 
				
			||||||
		for i in range(test_names.size()):
 | 
					 | 
				
			||||||
			var t = Test.new()
 | 
					 | 
				
			||||||
			t.name = test_names[i]
 | 
					 | 
				
			||||||
			tests.append(t)
 | 
					 | 
				
			||||||
		# Null is an acceptable value, but you can't pass null as a default to
 | 
					 | 
				
			||||||
		# get_value since it thinks you didn't send a default...then it spits
 | 
					 | 
				
			||||||
		# out red text.  This works around that.
 | 
					 | 
				
			||||||
		var inner_name = config_file.get_value(section, 'inner_class', 'Placeholder')
 | 
					 | 
				
			||||||
		if(inner_name != 'Placeholder'):
 | 
					 | 
				
			||||||
			inner_class_name = inner_name
 | 
					 | 
				
			||||||
		else: # just being explicit
 | 
					 | 
				
			||||||
			inner_class_name = null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
# start test_collector, I don't think I like the name.
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
var scripts = []
 | 
					 | 
				
			||||||
var _test_prefix = 'test_'
 | 
					 | 
				
			||||||
var _test_class_prefix = 'Test'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _utils = load('res://addons/gut/utils.gd').new()
 | 
					 | 
				
			||||||
var _lgr = _utils.get_logger()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _parse_script(script):
 | 
					 | 
				
			||||||
	var file = File.new()
 | 
					 | 
				
			||||||
	var line = ""
 | 
					 | 
				
			||||||
	var line_count = 0
 | 
					 | 
				
			||||||
	var inner_classes = []
 | 
					 | 
				
			||||||
	var scripts_found = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	file.open(script.path, 1)
 | 
					 | 
				
			||||||
	while(!file.eof_reached()):
 | 
					 | 
				
			||||||
		line_count += 1
 | 
					 | 
				
			||||||
		line = file.get_line()
 | 
					 | 
				
			||||||
		#Add a test
 | 
					 | 
				
			||||||
		if(line.begins_with("func " + _test_prefix)):
 | 
					 | 
				
			||||||
			var from = line.find(_test_prefix)
 | 
					 | 
				
			||||||
			var line_len = line.find("(") - from
 | 
					 | 
				
			||||||
			var new_test = Test.new()
 | 
					 | 
				
			||||||
			new_test.name = line.substr(from, line_len)
 | 
					 | 
				
			||||||
			new_test.line_number = line_count
 | 
					 | 
				
			||||||
			script.tests.append(new_test)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if(line.begins_with('class ')):
 | 
					 | 
				
			||||||
			var iclass_name = line.replace('class ', '')
 | 
					 | 
				
			||||||
			iclass_name = iclass_name.replace(':', '')
 | 
					 | 
				
			||||||
			if(iclass_name.begins_with(_test_class_prefix)):
 | 
					 | 
				
			||||||
				inner_classes.append(iclass_name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	scripts_found.append(script.path)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i in range(inner_classes.size()):
 | 
					 | 
				
			||||||
		var ts = TestScript.new(_utils, _lgr)
 | 
					 | 
				
			||||||
		ts.path = script.path
 | 
					 | 
				
			||||||
		ts.inner_class_name = inner_classes[i]
 | 
					 | 
				
			||||||
		if(_parse_inner_class_tests(ts)):
 | 
					 | 
				
			||||||
			scripts.append(ts)
 | 
					 | 
				
			||||||
			scripts_found.append(script.path + '[' + inner_classes[i] +']')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	file.close()
 | 
					 | 
				
			||||||
	return scripts_found
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func _parse_inner_class_tests(script):
 | 
					 | 
				
			||||||
	var inst = script.get_new()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if(!inst is _utils.Test):
 | 
					 | 
				
			||||||
		_lgr.warn('Ignoring ' + script.inner_class_name + ' because it starts with "' + _test_class_prefix + '" but does not extend addons/gut/test.gd')
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var methods = inst.get_method_list()
 | 
					 | 
				
			||||||
	for i in range(methods.size()):
 | 
					 | 
				
			||||||
		var name = methods[i]['name']
 | 
					 | 
				
			||||||
		if(name.begins_with(_test_prefix) and methods[i]['flags'] == 65):
 | 
					 | 
				
			||||||
			var t = Test.new()
 | 
					 | 
				
			||||||
			t.name = name
 | 
					 | 
				
			||||||
			script.tests.append(t)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return true
 | 
					 | 
				
			||||||
# -----------------
 | 
					 | 
				
			||||||
# Public
 | 
					 | 
				
			||||||
# -----------------
 | 
					 | 
				
			||||||
func add_script(path):
 | 
					 | 
				
			||||||
	# SHORTCIRCUIT
 | 
					 | 
				
			||||||
	if(has_script(path)):
 | 
					 | 
				
			||||||
		return []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var f = File.new()
 | 
					 | 
				
			||||||
	# SHORTCIRCUIT
 | 
					 | 
				
			||||||
	if(!f.file_exists(path)):
 | 
					 | 
				
			||||||
		_lgr.error('Could not find script:  ' + path)
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var ts = TestScript.new(_utils, _lgr)
 | 
					 | 
				
			||||||
	ts.path = path
 | 
					 | 
				
			||||||
	scripts.append(ts)
 | 
					 | 
				
			||||||
	return _parse_script(ts)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func to_s():
 | 
					 | 
				
			||||||
	var to_return = ''
 | 
					 | 
				
			||||||
	for i in range(scripts.size()):
 | 
					 | 
				
			||||||
		to_return += scripts[i].to_s() + "\n"
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
func get_logger():
 | 
					 | 
				
			||||||
	return _lgr
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_logger(logger):
 | 
					 | 
				
			||||||
	_lgr = logger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_test_prefix():
 | 
					 | 
				
			||||||
	return _test_prefix
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_test_prefix(test_prefix):
 | 
					 | 
				
			||||||
	_test_prefix = test_prefix
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_test_class_prefix():
 | 
					 | 
				
			||||||
	return _test_class_prefix
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func set_test_class_prefix(test_class_prefix):
 | 
					 | 
				
			||||||
	_test_class_prefix = test_class_prefix
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func clear():
 | 
					 | 
				
			||||||
	scripts.clear()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func has_script(path):
 | 
					 | 
				
			||||||
	var found = false
 | 
					 | 
				
			||||||
	var idx = 0
 | 
					 | 
				
			||||||
	while(idx < scripts.size() and !found):
 | 
					 | 
				
			||||||
		if(scripts[idx].path == path):
 | 
					 | 
				
			||||||
			found = true
 | 
					 | 
				
			||||||
		else:
 | 
					 | 
				
			||||||
			idx += 1
 | 
					 | 
				
			||||||
	return found
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func export_tests(path):
 | 
					 | 
				
			||||||
	var success = true
 | 
					 | 
				
			||||||
	var f = ConfigFile.new()
 | 
					 | 
				
			||||||
	for i in range(scripts.size()):
 | 
					 | 
				
			||||||
		scripts[i].export_to(f, str('TestScript-', i))
 | 
					 | 
				
			||||||
	var result = f.save(path)
 | 
					 | 
				
			||||||
	if(result != OK):
 | 
					 | 
				
			||||||
		_lgr.error(str('Could not save exported tests to [', path, '].  Error code:  ', result))
 | 
					 | 
				
			||||||
		success = false
 | 
					 | 
				
			||||||
	return success
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func import_tests(path):
 | 
					 | 
				
			||||||
	var success = false
 | 
					 | 
				
			||||||
	var f = ConfigFile.new()
 | 
					 | 
				
			||||||
	var result = f.load(path)
 | 
					 | 
				
			||||||
	if(result != OK):
 | 
					 | 
				
			||||||
		_lgr.error(str('Could not load exported tests from [', path, '].  Error code:  ', result))
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		var sections = f.get_sections()
 | 
					 | 
				
			||||||
		for key in sections:
 | 
					 | 
				
			||||||
			var ts = TestScript.new(_utils, _lgr)
 | 
					 | 
				
			||||||
			ts.import_from(f, key)
 | 
					 | 
				
			||||||
			scripts.append(ts)
 | 
					 | 
				
			||||||
		success = true
 | 
					 | 
				
			||||||
	return success
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,43 +0,0 @@
 | 
				
			||||||
var things = {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_unique_count():
 | 
					 | 
				
			||||||
	return things.size()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func add(thing):
 | 
					 | 
				
			||||||
	if(things.has(thing)):
 | 
					 | 
				
			||||||
		things[thing] += 1
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		things[thing] = 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func has(thing):
 | 
					 | 
				
			||||||
	return things.has(thing)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get(thing):
 | 
					 | 
				
			||||||
	var to_return = 0
 | 
					 | 
				
			||||||
	if(things.has(thing)):
 | 
					 | 
				
			||||||
		to_return = things[thing]
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func sum():
 | 
					 | 
				
			||||||
	var count = 0
 | 
					 | 
				
			||||||
	for key in things:
 | 
					 | 
				
			||||||
		count += things[key]
 | 
					 | 
				
			||||||
	return count
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func to_s():
 | 
					 | 
				
			||||||
	var to_return = ""
 | 
					 | 
				
			||||||
	for key in things:
 | 
					 | 
				
			||||||
		to_return += str(key, ":  ", things[key], "\n")
 | 
					 | 
				
			||||||
	to_return += str("sum: ", sum())
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_max_count():
 | 
					 | 
				
			||||||
	var max_val = null
 | 
					 | 
				
			||||||
	for key in things:
 | 
					 | 
				
			||||||
		if(max_val == null or things[key] > max_val):
 | 
					 | 
				
			||||||
			max_val = things[key]
 | 
					 | 
				
			||||||
	return max_val
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func add_array_items(array):
 | 
					 | 
				
			||||||
	for i in range(array.size()):
 | 
					 | 
				
			||||||
		add(array[i])
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,122 +0,0 @@
 | 
				
			||||||
var _Logger = load('res://addons/gut/logger.gd') # everything should use get_logger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var Doubler = load('res://addons/gut/doubler.gd')
 | 
					 | 
				
			||||||
var HookScript = load('res://addons/gut/hook_script.gd')
 | 
					 | 
				
			||||||
var MethodMaker = load('res://addons/gut/method_maker.gd')
 | 
					 | 
				
			||||||
var Spy = load('res://addons/gut/spy.gd')
 | 
					 | 
				
			||||||
var Stubber = load('res://addons/gut/stubber.gd')
 | 
					 | 
				
			||||||
var StubParams = load('res://addons/gut/stub_params.gd')
 | 
					 | 
				
			||||||
var Summary = load('res://addons/gut/summary.gd')
 | 
					 | 
				
			||||||
var Test = load('res://addons/gut/test.gd')
 | 
					 | 
				
			||||||
var TestCollector = load('res://addons/gut/test_collector.gd')
 | 
					 | 
				
			||||||
var ThingCounter = load('res://addons/gut/thing_counter.gd')
 | 
					 | 
				
			||||||
var OneToMany = load('res://addons/gut/one_to_many.gd')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const GUT_METADATA = '__gut_metadata_'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
enum DOUBLE_STRATEGY{
 | 
					 | 
				
			||||||
	FULL,
 | 
					 | 
				
			||||||
	PARTIAL
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var _file_checker = File.new()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func is_version_30():
 | 
					 | 
				
			||||||
	var info = Engine.get_version_info()
 | 
					 | 
				
			||||||
	return info.major == 3 and info.minor == 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func is_version_31():
 | 
					 | 
				
			||||||
	var info = Engine.get_version_info()
 | 
					 | 
				
			||||||
	return info.major == 3 and info.minor == 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
# Everything should get a logger through this.
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# Eventually I want to make this get a single instance of a logger but I'm not
 | 
					 | 
				
			||||||
# sure how to do that without everything having to be in the tree which I
 | 
					 | 
				
			||||||
# DO NOT want to to do.  I'm thinking of writings some instance ids to a file
 | 
					 | 
				
			||||||
# and loading them in the _init for this.
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
func get_logger():
 | 
					 | 
				
			||||||
	return _Logger.new()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
# Returns an array created by splitting the string by the delimiter
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
func split_string(to_split, delim):
 | 
					 | 
				
			||||||
	var to_return = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var loc = to_split.find(delim)
 | 
					 | 
				
			||||||
	while(loc != -1):
 | 
					 | 
				
			||||||
		to_return.append(to_split.substr(0, loc))
 | 
					 | 
				
			||||||
		to_split = to_split.substr(loc + 1, to_split.length() - loc)
 | 
					 | 
				
			||||||
		loc = to_split.find(delim)
 | 
					 | 
				
			||||||
	to_return.append(to_split)
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
# Returns a string containing all the elements in the array separated by delim
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
func join_array(a, delim):
 | 
					 | 
				
			||||||
	var to_return = ''
 | 
					 | 
				
			||||||
	for i in range(a.size()):
 | 
					 | 
				
			||||||
		to_return += str(a[i])
 | 
					 | 
				
			||||||
		if(i != a.size() -1):
 | 
					 | 
				
			||||||
			to_return += str(delim)
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
# return if_null if value is null otherwise return value
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
func nvl(value, if_null):
 | 
					 | 
				
			||||||
	if(value == null):
 | 
					 | 
				
			||||||
		return if_null
 | 
					 | 
				
			||||||
	else:
 | 
					 | 
				
			||||||
		return value
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
# returns true if the object has been freed, false if not
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# From what i've read, the weakref approach should work.  It seems to work most
 | 
					 | 
				
			||||||
# of the time but sometimes it does not catch it.  The str comparison seems to
 | 
					 | 
				
			||||||
# fill in the gaps.  I've not seen any errors after adding that check.
 | 
					 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					 | 
				
			||||||
func is_freed(obj):
 | 
					 | 
				
			||||||
	var wr = weakref(obj)
 | 
					 | 
				
			||||||
	return !(wr.get_ref() and str(obj) != '[Deleted Object]')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func is_not_freed(obj):
 | 
					 | 
				
			||||||
	return !is_freed(obj)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func is_double(obj):
 | 
					 | 
				
			||||||
	return obj.get(GUT_METADATA) != null
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func extract_property_from_array(source, property):
 | 
					 | 
				
			||||||
	var to_return = []
 | 
					 | 
				
			||||||
	for i in (source.size()):
 | 
					 | 
				
			||||||
		to_return.append(source[i].get(property))
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func file_exists(path):
 | 
					 | 
				
			||||||
	return _file_checker.file_exists(path)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func write_file(path, content):
 | 
					 | 
				
			||||||
	var f = File.new()
 | 
					 | 
				
			||||||
	f.open(path, f.WRITE)
 | 
					 | 
				
			||||||
	f.store_string(content)
 | 
					 | 
				
			||||||
	f.close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func is_null_or_empty(text):
 | 
					 | 
				
			||||||
	return text == null or text == ''
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func get_native_class_name(thing):
 | 
					 | 
				
			||||||
	var to_return = null
 | 
					 | 
				
			||||||
	if(is_native_class(thing)):
 | 
					 | 
				
			||||||
		to_return = thing.new().get_class()
 | 
					 | 
				
			||||||
	return to_return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func is_native_class(thing):
 | 
					 | 
				
			||||||
	var it_is = false
 | 
					 | 
				
			||||||
	if(typeof(thing) == TYPE_OBJECT):
 | 
					 | 
				
			||||||
		it_is = str(thing).begins_with("[GDScriptNativeClass:")
 | 
					 | 
				
			||||||
	return it_is
 | 
					 | 
				
			||||||
							
								
								
									
										3
									
								
								justfile
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								justfile
									
										
									
									
									
								
							| 
						 | 
					@ -5,9 +5,6 @@ itchio := "damantisshrimp/taqin"
 | 
				
			||||||
edit:
 | 
					edit:
 | 
				
			||||||
	godot --editor --quiet &
 | 
						godot --editor --quiet &
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test:
 | 
					 | 
				
			||||||
    godot --debug --script {{src_dir}}/addons/gut/gut_cmdln.gd
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export-android:
 | 
					export-android:
 | 
				
			||||||
	mkdir -p {{build_dir}}/android
 | 
						mkdir -p {{build_dir}}/android
 | 
				
			||||||
	godot --export "Android" {{build_dir}}/android
 | 
						godot --export "Android" {{build_dir}}/android
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,10 +40,6 @@ window/size/height.mobile=1200
 | 
				
			||||||
window/stretch/mode.mobile="2d"
 | 
					window/stretch/mode.mobile="2d"
 | 
				
			||||||
window/stretch/aspect.mobile="expand"
 | 
					window/stretch/aspect.mobile="expand"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[editor_plugins]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
enabled=PoolStringArray( "gut" )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[input_devices]
 | 
					[input_devices]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pointing/emulate_touch_from_mouse=true
 | 
					pointing/emulate_touch_from_mouse=true
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,38 +0,0 @@
 | 
				
			||||||
[gd_scene load_steps=2 format=2]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[ext_resource path="res://addons/gut/gut.gd" type="Script" id=1]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[node name="Gut" type="Control"]
 | 
					 | 
				
			||||||
self_modulate = Color( 1, 1, 1, 0 )
 | 
					 | 
				
			||||||
anchor_right = 1.0
 | 
					 | 
				
			||||||
anchor_bottom = 1.0
 | 
					 | 
				
			||||||
rect_min_size = Vector2( 740, 250 )
 | 
					 | 
				
			||||||
script = ExtResource( 1 )
 | 
					 | 
				
			||||||
__meta__ = {
 | 
					 | 
				
			||||||
"_edit_use_anchors_": false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
_select_script = ""
 | 
					 | 
				
			||||||
_tests_like = ""
 | 
					 | 
				
			||||||
_inner_class_name = ""
 | 
					 | 
				
			||||||
_run_on_load = false
 | 
					 | 
				
			||||||
_should_maximize = false
 | 
					 | 
				
			||||||
_should_print_to_console = true
 | 
					 | 
				
			||||||
_log_level = 1
 | 
					 | 
				
			||||||
_yield_between_tests = false
 | 
					 | 
				
			||||||
_disable_strict_datatype_checks = false
 | 
					 | 
				
			||||||
_test_prefix = "test_"
 | 
					 | 
				
			||||||
_file_prefix = "test_"
 | 
					 | 
				
			||||||
_file_extension = ".gd"
 | 
					 | 
				
			||||||
_inner_class_prefix = "Test"
 | 
					 | 
				
			||||||
_temp_directory = "user://gut_temp_directory"
 | 
					 | 
				
			||||||
_export_path = ""
 | 
					 | 
				
			||||||
_include_subdirectories = false
 | 
					 | 
				
			||||||
_directory1 = "res://test/unit"
 | 
					 | 
				
			||||||
_directory2 = ""
 | 
					 | 
				
			||||||
_directory3 = ""
 | 
					 | 
				
			||||||
_directory4 = ""
 | 
					 | 
				
			||||||
_directory5 = ""
 | 
					 | 
				
			||||||
_directory6 = ""
 | 
					 | 
				
			||||||
_double_strategy = 1
 | 
					 | 
				
			||||||
_pre_run_script = ""
 | 
					 | 
				
			||||||
_post_run_script = ""
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,27 +0,0 @@
 | 
				
			||||||
extends "res://addons/gut/test.gd"
 | 
					 | 
				
			||||||
func before_each():
 | 
					 | 
				
			||||||
	gut.p("ran setup", 2)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func after_each():
 | 
					 | 
				
			||||||
	gut.p("ran teardown", 2)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func before_all():
 | 
					 | 
				
			||||||
	gut.p("ran run setup", 2)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func after_all():
 | 
					 | 
				
			||||||
	gut.p("ran run teardown", 2)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func test_assert_eq_number_not_equal():
 | 
					 | 
				
			||||||
	assert_eq(1, 2, "Should fail.  1 != 2")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func test_assert_eq_number_equal():
 | 
					 | 
				
			||||||
	assert_eq('asdf', 'asdf', "Should pass")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func test_assert_true_with_true():
 | 
					 | 
				
			||||||
	assert_true(true, "Should pass, true is true")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func test_assert_true_with_false():
 | 
					 | 
				
			||||||
	assert_true(false, "Should fail")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func test_something_else():
 | 
					 | 
				
			||||||
	assert_true(false, "didn't work")
 | 
					 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue