diff options
| author | Eugene Sandulenko | 2010-09-12 08:08:37 +0000 | 
|---|---|---|
| committer | Eugene Sandulenko | 2010-09-12 08:08:37 +0000 | 
| commit | 74118a70878f0b6efcba73f8410a7f661949bcf6 (patch) | |
| tree | a4c22ffcbb86fbad22b50c5b4a6405a60920be0b | |
| parent | 6bc9340df55d05ad7aa6b14348a567304ff41cc7 (diff) | |
| parent | a4a28eb16e3f2c9d1f4e79120b359821d731eecf (diff) | |
| download | scummvm-rg350-74118a70878f0b6efcba73f8410a7f661949bcf6.tar.gz scummvm-rg350-74118a70878f0b6efcba73f8410a7f661949bcf6.tar.bz2 scummvm-rg350-74118a70878f0b6efcba73f8410a7f661949bcf6.zip | |
TESTBED: Merge gsoc2010-testbed branch
svn-id: r52681
29 files changed, 4176 insertions, 0 deletions
| diff --git a/base/plugins.cpp b/base/plugins.cpp index b1273b2d21..f5e51f3228 100644 --- a/base/plugins.cpp +++ b/base/plugins.cpp @@ -157,6 +157,9 @@ public:  		#if PLUGIN_ENABLED_STATIC(TEENAGENT)  		LINK_PLUGIN(TEENAGENT)  		#endif +		#if PLUGIN_ENABLED_STATIC(TESTBED) +		LINK_PLUGIN(TESTBED) +		#endif  		#if PLUGIN_ENABLED_STATIC(TINSEL)  		LINK_PLUGIN(TINSEL)  		#endif @@ -106,6 +106,7 @@ add_engine sky "Beneath a Steel Sky" yes  add_engine sword1 "Broken Sword" yes  add_engine sword2 "Broken Sword II" yes  add_engine teenagent "Teen Agent" yes +add_engine testbed "TestBed: the Testing framework" no  add_engine tinsel "Tinsel" yes  add_engine touche "Touche: The Adventures of the Fifth Musketeer" yes  add_engine tucker "Bud Tucker in Double Trouble" yes diff --git a/dists/engine-data/create-testbed-data.sh b/dists/engine-data/create-testbed-data.sh new file mode 100755 index 0000000000..0200803caa --- /dev/null +++ b/dists/engine-data/create-testbed-data.sh @@ -0,0 +1,52 @@ +#!/bin/sh + +# Create the directory structure +# Avoided bash shortcuts / file-seperators in interest of portability + +if [ -e testbed ]; then +	echo "Game-data already present as testbed/" +	echo "To regenerate, remove and rerun" +	exit 0 +fi + +mkdir testbed + +cd testbed + +# For game detection +echo "ScummVM rocks!" > TESTBED + +mkdir test1 +mkdir Test2 +mkdir TEST3 +mkdir tEST4 +mkdir test5 + + +cd test1 +echo "It works!" > file.txt +cd .. + +cd Test2 +echo "It works!" > File.txt +cd .. + +cd TEST3 +echo "It works!" > FILE.txt +cd .. + +cd tEST4 +echo "It works!" > fILe.txt +cd .. + +cd test5 +echo "It works!" > file. +cd .. + +# back to the top +cd .. + +# move the audiocd data to newly created directory +cp -r testbed-audiocd-files testbed/audiocd-files + +echo "Game data created" diff --git a/dists/engine-data/testbed-audiocd-files/track01.mp3 b/dists/engine-data/testbed-audiocd-files/track01.mp3Binary files differ new file mode 100755 index 0000000000..53d057ee96 --- /dev/null +++ b/dists/engine-data/testbed-audiocd-files/track01.mp3 diff --git a/dists/engine-data/testbed-audiocd-files/track02.mp3 b/dists/engine-data/testbed-audiocd-files/track02.mp3Binary files differ new file mode 100755 index 0000000000..daf8e4860d --- /dev/null +++ b/dists/engine-data/testbed-audiocd-files/track02.mp3 diff --git a/dists/engine-data/testbed-audiocd-files/track03.mp3 b/dists/engine-data/testbed-audiocd-files/track03.mp3Binary files differ new file mode 100755 index 0000000000..1ef385d640 --- /dev/null +++ b/dists/engine-data/testbed-audiocd-files/track03.mp3 diff --git a/dists/engine-data/testbed-audiocd-files/track04.mp3 b/dists/engine-data/testbed-audiocd-files/track04.mp3Binary files differ new file mode 100755 index 0000000000..7607087f08 --- /dev/null +++ b/dists/engine-data/testbed-audiocd-files/track04.mp3 diff --git a/engines/engines.mk b/engines/engines.mk index e542ffd933..f20d46a8f3 100644 --- a/engines/engines.mk +++ b/engines/engines.mk @@ -141,6 +141,11 @@ DEFINES += -DENABLE_SWORD2=$(ENABLE_SWORD2)  MODULES += engines/sword2  endif +ifdef ENABLE_TESTBED +DEFINES += -DENABLE_TESTBED=$(ENABLE_TESTBED) +MODULES += engines/testbed +endif +  ifdef ENABLE_TEENAGENT  DEFINES += -DENABLE_TEENAGENT=$(ENABLE_TEENAGENT)  MODULES += engines/teenagent diff --git a/engines/testbed/config.cpp b/engines/testbed/config.cpp new file mode 100644 index 0000000000..1cfb1bc395 --- /dev/null +++ b/engines/testbed/config.cpp @@ -0,0 +1,292 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "common/fs.h" +#include "common/stream.h" +#include "common/config-manager.h" +#include "engines/engine.h" +#include "testbed/config.h" + +namespace Testbed { + +TestbedOptionsDialog::TestbedOptionsDialog(Common::Array<Testsuite *> &tsList, TestbedConfigManager *tsConfMan) : GUI::Dialog("Browser"), _testbedConfMan(tsConfMan) { +	 +	new GUI::StaticTextWidget(this, "Browser.Headline", "Select Testsuites to Execute"); +	new GUI::StaticTextWidget(this, "Browser.Path", "Use Doubleclick to select/deselect"); + +	// Construct a String Array +	Common::Array<Testsuite *>::const_iterator iter; +	Common::String description; +	uint selected = 0; + +	for (iter = tsList.begin(); iter != tsList.end(); iter++) { +		_testSuiteArray.push_back(*iter); +		description = (*iter)->getDescription(); +		if ((*iter)->isEnabled()) { +			_testSuiteDescArray.push_back(description + "(selected)"); +			selected++; +			_colors.push_back(GUI::ThemeEngine::kFontColorNormal); +		} else { +			_testSuiteDescArray.push_back(description); +			_colors.push_back(GUI::ThemeEngine::kFontColorAlternate); +		} +	} +	 +	_testListDisplay = new TestbedListWidget(this, "Browser.List", _testSuiteArray); +	_testListDisplay->setNumberingMode(GUI::kListNumberingOff); +	_testListDisplay->setList(_testSuiteDescArray, &_colors); + +	// This list shouldn't be editable +	_testListDisplay->setEditable(false); + +	if (selected > (tsList.size() - selected)) { +		_selectButton = new GUI::ButtonWidget(this, "Browser.Up", "Deselect All", 0, kTestbedDeselectAll, 0); +	} else { +		_selectButton = new GUI::ButtonWidget(this, "Browser.Up", "Select All", 0, kTestbedSelectAll, 0); +	} +	new GUI::ButtonWidget(this, "Browser.Cancel", "Run tests", 0, GUI::kCloseCmd); +	new GUI::ButtonWidget(this, "Browser.Choose", "Exit Testbed", 0, kTestbedQuitCmd); +} + +TestbedOptionsDialog::~TestbedOptionsDialog() {} + +void TestbedOptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) { +	Testsuite *ts; +	Common::WriteStream *ws; +	switch (cmd) { +	case GUI::kListItemDoubleClickedCmd: +		ts  = _testSuiteArray[_testListDisplay->getSelected()]; +		if (ts) { +			if (ts->isEnabled()) { +				ts->enable(false); +				_testListDisplay->markAsDeselected(_testListDisplay->getSelected()); +			} else { +				ts->enable(true); +				_testListDisplay->markAsSelected(_testListDisplay->getSelected()); +			} +		} +		break; + +	case kTestbedQuitCmd: +		Engine::quitGame(); +		close(); +		break; + +	case kTestbedDeselectAll: +		_selectButton->setLabel("Select All"); +		_selectButton->setCmd(kTestbedSelectAll); +		for (uint i = 0; i < _testSuiteArray.size(); i++) { +			_testListDisplay->markAsDeselected(i); +			ts  = _testSuiteArray[i]; +			if (ts) { +				ts->enable(false); +			} +		} +		break; + +	case kTestbedSelectAll: +		_selectButton->setLabel("Deselect All"); +		_selectButton->setCmd(kTestbedDeselectAll); +		for (uint i = 0; i < _testSuiteArray.size(); i++) { +			_testListDisplay->markAsSelected(i); +			ts  = _testSuiteArray[i]; +			if (ts) { +				ts->enable(true); +			} +		} +		break; +	case GUI::kCloseCmd: +		// This is final selected state, write it to config file. +		ws = _testbedConfMan->getConfigWriteStream(); +		_testbedConfMan->writeTestbedConfigToStream(ws); +		delete ws; +	default: +		GUI::Dialog::handleCommand(sender, cmd, data); +	 +	} +} + +void TestbedInteractionDialog::addText(uint w, uint h, const Common::String text, Graphics::TextAlign textAlign, uint xOffset, uint yPadding) { +	if (!xOffset) { +		xOffset = _xOffset; +	} +	_yOffset += yPadding; +	new GUI::StaticTextWidget(this, xOffset, _yOffset, w, h, text, textAlign); +	_yOffset += h; +} + +void TestbedInteractionDialog::addButton(uint w, uint h, const Common::String name, uint32 cmd, uint xOffset, uint yPadding) { +	if (!xOffset) { +		xOffset = _xOffset; +	} +	_yOffset += yPadding; +	_buttonArray.push_back(new GUI::ButtonWidget(this, xOffset, _yOffset, w, h, name, 0, cmd)); +	_yOffset += h; +} + +void TestbedInteractionDialog::addList(uint x, uint y, uint w, uint h, Common::Array<Common::String> &strArray, uint yPadding) { +	_yOffset += yPadding; +	GUI::ListWidget *list = new GUI::ListWidget(this, x, y, w, h); +	list->setEditable(false); +	list->setNumberingMode(GUI::kListNumberingOff); +	list->setList(strArray); +	_yOffset += h; +} + +void TestbedInteractionDialog::addButtonXY(uint x, uint y, uint w, uint h, const Common::String name, uint32 cmd) {	 +	_buttonArray.push_back(new GUI::ButtonWidget(this, x, _yOffset, w, h, name, 0, cmd)); +} + +void TestbedInteractionDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) { +	switch (cmd) { +	default: +		GUI::Dialog::handleCommand(sender, cmd, data); +	} +} + +void TestbedConfigManager::initDefaultConfiguration() { +	// Default Configuration +	// Add Global configuration Parameters here. +	_configFileInterface.setKey("isSessionInteractive", "Global", "true"); +} + +void TestbedConfigManager::writeTestbedConfigToStream(Common::WriteStream *ws) { +	Common::String wStr; +	for (Common::Array<Testsuite *>::const_iterator i = _testsuiteList.begin(); i < _testsuiteList.end(); i++) { +		_configFileInterface.setKey("this", (*i)->getName(), boolToString((*i)->isEnabled())); +		const Common::Array<Test *> &testList = (*i)->getTestList(); +		for (Common::Array<Test *>::const_iterator j = testList.begin(); j != testList.end(); j++) { +			_configFileInterface.setKey((*j)->featureName, (*i)->getName(), boolToString((*j)->enabled)); +		} +	} +	_configFileInterface.saveToStream(*ws); +	_configFileInterface.clear(); +	ws->flush(); +} + +Common::SeekableReadStream *TestbedConfigManager::getConfigReadStream() { +	// Look for config file in game-path +	const Common::String &path = ConfMan.get("path"); +	Common::FSDirectory gameRoot(path); +	Common::SeekableReadStream *rs = gameRoot.createReadStreamForMember(_configFileName); +	return rs; +} + +Common::WriteStream *TestbedConfigManager::getConfigWriteStream() { +	// Look for config file in game-path +	const Common::String &path = ConfMan.get("path"); +	Common::WriteStream *ws; +	Common::FSNode gameRoot(path); +	Common::FSNode config = gameRoot.getChild(_configFileName); +	ws = config.createWriteStream(); +	return ws; +} + +void TestbedConfigManager::parseConfigFile() {	 +	Common::SeekableReadStream *rs = getConfigReadStream(); +	 +	if (!rs) { +		Testsuite::logPrintf("Info! No config file found, using default configuration.\n"); +		initDefaultConfiguration(); +		return; +	} +	_configFileInterface.loadFromStream(*rs); +	Common::ConfigFile::SectionList sections = _configFileInterface.getSections(); +	Testsuite *currTS = 0; + +	for (Common::ConfigFile::SectionList::const_iterator i = sections.begin(); i != sections.end(); i++) { +		if (i->name.equalsIgnoreCase("Global")) { +			// Global params may be directly queried, ignore them	 +		} else { +			// A testsuite, process it. +			currTS = getTestsuiteByName(i->name); +			Common::ConfigFile::SectionKeyList kList = i->getKeys(); +			if (!currTS) { +				Testsuite::logPrintf("Warning! Error in config: Testsuite %s not found\n", i->name.c_str()); +			} + +			for (Common::ConfigFile::SectionKeyList::const_iterator j = kList.begin(); j != kList.end(); j++) { +				if (j->key.equalsIgnoreCase("this")) { +					currTS->enable(stringToBool(j->value)); +				} else { +					if (!currTS->enableTest(j->key, stringToBool(j->value))) { +						Testsuite::logPrintf("Warning! Error in config: Test %s not found\n", j->key.c_str()); +					} +				} +			} +		} +	} +	delete rs; +} + +int TestbedConfigManager::getNumSuitesEnabled() { +	int count = 0; +	for (uint i = 0; i < _testsuiteList.size(); i++) { +        if (_testsuiteList[i]->isEnabled()) { +        	count++; +		} +    } +    return count; +} + +Testsuite *TestbedConfigManager::getTestsuiteByName(const Common::String &name) { +	for (uint i = 0; i < _testsuiteList.size(); i++) { +		if (name.equalsIgnoreCase(_testsuiteList[i]->getName())) { +			return _testsuiteList[i]; +		} +	} +	return 0; +} + +void TestbedConfigManager::selectTestsuites() { + +	parseConfigFile(); +	 +	if (_configFileInterface.hasKey("isSessionInteractive", "Global")) { +		Common::String in; +		_configFileInterface.getKey("isSessionInteractive", "Global", in); +		Testsuite::isSessionInteractive = stringToBool(in); +	} + +	if (!Testsuite::isSessionInteractive) { +		// Non interactive sessions don't need to go beyond +		return; +	} + +	// XXX: disabling these as of now for fastly testing other tests +	// Testsuite::isSessionInteractive = false; +	Common::String prompt("Welcome to the ScummVM testbed!\n" +						"It is a framework to test the various ScummVM subsystems namely GFX, Sound, FS, events etc.\n" +						"If you see this, it means interactive tests would run on this system :)"); + +	Testsuite::logPrintf("Info! : Interactive tests are also being executed.\n"); +	 +	if (Testsuite::handleInteractiveInput(prompt, "Proceed?", "Customize", kOptionRight)) { +		// Select testsuites using checkboxes +		TestbedOptionsDialog tbd(_testsuiteList, this); +		tbd.runModal(); +	} +} + +}	// End of namespace Testbed diff --git a/engines/testbed/config.h b/engines/testbed/config.h new file mode 100644 index 0000000000..0c734c95fb --- /dev/null +++ b/engines/testbed/config.h @@ -0,0 +1,135 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef TESTBED_CONFIG_H +#define TESTBED_CONFIG_H + + +#include "common/array.h" +#include "common/config-file.h" +#include "common/str-array.h" +#include "common/tokenizer.h" + +#include "gui/ListWidget.h" +#include "gui/options.h" +#include "gui/ThemeEngine.h" + +#include "testbed/testsuite.h" + +namespace Testbed { + +enum { +	kTestbedQuitCmd = 'Quit', +	kTestbedSelectAll = 'sAll', +	kTestbedDeselectAll = 'dAll' +}; + + + +class TestbedConfigManager { +public: +	TestbedConfigManager(Common::Array<Testsuite *> &tList, const Common::String fName) : _testsuiteList(tList), _configFileName(fName) {} +	~TestbedConfigManager() {} +	void selectTestsuites(); +	void setConfigFile(const Common::String fName) { _configFileName = fName; } +	Common::SeekableReadStream *getConfigReadStream(); +	Common::WriteStream *getConfigWriteStream(); +	void writeTestbedConfigToStream(Common::WriteStream *ws); +	Testsuite *getTestsuiteByName(const Common::String &name); +	bool stringToBool(const Common::String str) { return str.equalsIgnoreCase("true") ? true : false; } +	Common::String boolToString(bool val) { return val ? "true" : "false"; } +	void initDefaultConfiguration(); +	int getNumSuitesEnabled(); + +private: +	Common::Array<Testsuite *> &_testsuiteList; +	Common::String	_configFileName; +	Common::ConfigFile	_configFileInterface; +	void parseConfigFile(); +}; + +class TestbedListWidget : public GUI::ListWidget { +public: +	TestbedListWidget(GUI::Dialog *boss, const Common::String &name, Common::Array<Testsuite *> tsArray) : GUI::ListWidget(boss, name), _testSuiteArray(tsArray) {} + +	void markAsSelected(int i) { +		if (!_list[i].contains("selected")) { +			_list[i] += " (selected)"; +		} +		_listColors[i] = GUI::ThemeEngine::kFontColorNormal; +		draw(); +	} +	 +	void markAsDeselected(int i) { +		if (_list[i].contains("selected")) { +			_list[i] = _testSuiteArray[i]->getDescription(); +		} +		_listColors[i] = GUI::ThemeEngine::kFontColorAlternate; +		draw(); +	} +	 +	void setColor(uint32 indx, GUI::ThemeEngine::FontColor color) { +		assert(indx < _listColors.size()); +		_listColors[indx] = color; +		draw(); +	} + +private: +	Common::Array<Testsuite *>	_testSuiteArray; +}; + +class TestbedOptionsDialog : public GUI::Dialog { +public: +	TestbedOptionsDialog(Common::Array<Testsuite *> &tsList, TestbedConfigManager *tsConfMan); +	~TestbedOptionsDialog(); +	void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); + +private: +	GUI::ListWidget::ColorList _colors; +	GUI::ButtonWidget	*_selectButton; +	Common::Array<Testsuite *> _testSuiteArray; +	Common::StringArray _testSuiteDescArray; +	TestbedListWidget *_testListDisplay; +	TestbedConfigManager *_testbedConfMan; +}; + +class TestbedInteractionDialog : public GUI::Dialog { +public: +	TestbedInteractionDialog(uint x, uint y, uint w, uint h) : GUI::Dialog(x, y, w, h) {} +	~TestbedInteractionDialog() {} +	virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); +	void addButton(uint w, uint h, const Common::String name, uint32 cmd, uint xOffset = 0, uint yPadding = 8); +	void addButtonXY(uint x, uint y, uint w, uint h, const Common::String name, uint32 cmd); +	void addText(uint w, uint h, const Common::String text, Graphics::TextAlign textAlign, uint xOffset, uint yPadding = 8); +	void addList(uint x, uint y, uint w, uint h, Common::Array<Common::String> &strArray, uint yPadding = 8); +protected: +	Common::Array<GUI::ButtonWidget *> _buttonArray; +	uint _xOffset; +	uint _yOffset; + +}; + +} // End of namespace Testbed + +#endif // TESTBED_CONFIG_H diff --git a/engines/testbed/detection.cpp b/engines/testbed/detection.cpp new file mode 100644 index 0000000000..bb633e9812 --- /dev/null +++ b/engines/testbed/detection.cpp @@ -0,0 +1,92 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/config-manager.h" +#include "engines/advancedDetector.h" +#include "common/system.h" +#include "common/fs.h" + +#include "base/plugins.h" + +#include "testbed/testbed.h" + +static const PlainGameDescriptor testbed_setting[] = { +	{ "testbed", "Testbed: The backend testing framework" }, +	{ 0, 0 } +}; + +static const ADGameDescription testbedDescriptions[] = { +	{ +		"testbed", +		"", +		AD_ENTRY1(NULL, 0),	// No data files required +		Common::EN_ANY, +		Common::kPlatformPC, +		ADGF_NO_FLAGS, +		Common::GUIO_NONE +	}, +	AD_TABLE_END_MARKER +}; + +static const ADParams detectionParams = { +	(const byte *)testbedDescriptions, +	sizeof(ADGameDescription), +	512, +	testbed_setting, +	0, +	"testbed", +	0, +	ADGF_NO_FLAGS, +	Common::GUIO_NONE, +	1, +	0 +}; + +class TestbedMetaEngine : public AdvancedMetaEngine { +public: +	TestbedMetaEngine() : AdvancedMetaEngine(detectionParams) { +	} + +	virtual const char *getName() const { +		return "TestBed: The backend testing framework"; +	} + +	virtual const char *getOriginalCopyright() const { +		return "Copyright (C) ScummVM"; +	} + +	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { +		// Instantiate Engine even if the game data is not found. +		*engine = new Testbed::TestbedEngine(syst); +		return true; +	} + +}; + +#if PLUGIN_ENABLED_DYNAMIC(TESTBED) +	REGISTER_PLUGIN_DYNAMIC(TESTBED, PLUGIN_TYPE_ENGINE, TestbedMetaEngine); +#else +	REGISTER_PLUGIN_STATIC(TESTBED, PLUGIN_TYPE_ENGINE, TestbedMetaEngine); +#endif diff --git a/engines/testbed/events.cpp b/engines/testbed/events.cpp new file mode 100644 index 0000000000..a8e2816266 --- /dev/null +++ b/engines/testbed/events.cpp @@ -0,0 +1,294 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "common/events.h" +#include "common/keyboard.h" + +#include "engines/engine.h" + +#include "graphics/cursorman.h" + +#include "testbed/events.h" +#include "testbed/graphics.h" + +namespace Testbed { + +struct keycodeToChar { +	Common::KeyCode code; +	char value; +} keyCodeLUT[] = { +	{Common::KEYCODE_a, 'a'}, +	{Common::KEYCODE_b, 'b'}, +	{Common::KEYCODE_c, 'c'}, +	{Common::KEYCODE_d, 'd'}, +	{Common::KEYCODE_e, 'e'}, +	{Common::KEYCODE_f, 'f'}, +	{Common::KEYCODE_g, 'g'}, +	{Common::KEYCODE_h, 'h'}, +	{Common::KEYCODE_i, 'i'}, +	{Common::KEYCODE_j, 'j'}, +	{Common::KEYCODE_k, 'k'}, +	{Common::KEYCODE_l, 'l'}, +	{Common::KEYCODE_m, 'm'}, +	{Common::KEYCODE_n, 'n'}, +	{Common::KEYCODE_o, 'o'}, +	{Common::KEYCODE_p, 'p'}, +	{Common::KEYCODE_q, 'q'}, +	{Common::KEYCODE_r, 'r'}, +	{Common::KEYCODE_s, 's'}, +	{Common::KEYCODE_t, 't'}, +	{Common::KEYCODE_u, 'u'}, +	{Common::KEYCODE_v, 'v'}, +	{Common::KEYCODE_w, 'w'}, +	{Common::KEYCODE_x, 'x'}, +	{Common::KEYCODE_y, 'y'}, +	{Common::KEYCODE_z, 'z'}, +	{Common::KEYCODE_SPACE, ' '} +}; + +char EventTests::keystrokeToChar() { +	Common::EventManager *eventMan = g_system->getEventManager(); +	bool quitLoop = false; +	Common::Event event; + +	// handle all keybd events +	while (!quitLoop) { +		while (eventMan->pollEvent(event)) { +			// Quit if explicitly requested! +			if (Engine::shouldQuit()) { +				return 0; +			} + +			switch (event.type) { +			case Common::EVENT_KEYDOWN: +				if (event.kbd.keycode == Common::KEYCODE_ESCAPE) { +					return 0; +				} +				for (int i = 0; i < ARRAYSIZE(keyCodeLUT); i++) { +					if (event.kbd.keycode == keyCodeLUT[i].code) { +						return keyCodeLUT[i].value; +					} +				} +				break; +			default: +				break;	// Ignore other events +			} +		} +	} + +	return 0; +} + +Common::Rect EventTests::drawFinishZone() { +	Graphics::Surface *screen = g_system->lockScreen(); +	const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont)); +	int width = 35; +	int height = 20; +	int right = g_system->getWidth(); +	Common::Rect rect(0, 0, right, height); +	Common::Rect rect2(0, 0, right - width, height); +	screen->fillRect(rect, kColorSpecial); +	screen->fillRect(rect2, kColorBlack); +	g_system->unlockScreen(); +	font.drawString(screen, "Close", rect.left, rect.top, screen->w, kColorBlack, Graphics::kTextAlignRight); +	g_system->updateScreen(); +	return Common::Rect(right - width, 0, right, height); +} + +bool EventTests::mouseEvents() { +	 +	Testsuite::clearScreen(); +	Common::String info = "Testing Mouse events.\n " +	"Any movement/click generated by L/R/M mouse buttons or the mouse wheel should be detected.\n" +	"Press X to exit"; + +	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { +		Testsuite::logPrintf("Info! Skipping test : keyboard events\n"); +		return true; +	} +	 +	Common::EventManager *eventMan = g_system->getEventManager(); + +	Common::Point pt(0, 100); +	Common::Rect rect = Testsuite::writeOnScreen("Generate mouse events make L/R/M button clicks", pt); +	pt.y = 120; +	Testsuite::writeOnScreen("Testbed should be able to detect them, Press X to exit", pt); + +	// Init Mouse Palette +	GFXtests::initMousePalette(); +	Common::Rect finishZone = drawFinishZone(); + +	bool quitLoop = false; +	bool passed = true; +	// handle all mouse events +	Common::Event event; +	while (!quitLoop) { +		// Show mouse +		CursorMan.showMouse(true); +		g_system->updateScreen(); + +		while (eventMan->pollEvent(event)) { +			// Quit if explicitly requested +			if (Engine::shouldQuit()) { +				return passed; +			} +			switch (event.type) { +			case Common::EVENT_MOUSEMOVE: +				// Movements havee already been tested in GFX +				break; +			case Common::EVENT_LBUTTONDOWN: +				Testsuite::clearScreen(rect); +				Testsuite::writeOnScreen("Mouse left-button pressed", pt); +				break; +			case Common::EVENT_RBUTTONDOWN: +				Testsuite::clearScreen(rect); +				Testsuite::writeOnScreen("Mouse right-button pressed", pt); +				break; +			case Common::EVENT_WHEELDOWN: +				Testsuite::clearScreen(rect); +				Testsuite::writeOnScreen("Mouse wheel moved down", pt); +				break; +			case Common::EVENT_MBUTTONDOWN: +				Testsuite::clearScreen(rect); +				Testsuite::writeOnScreen("Mouse middle-button pressed ", pt); +				break; +			case Common::EVENT_LBUTTONUP: +				Testsuite::clearScreen(rect); +				if (finishZone.contains(eventMan->getMousePos())) { +					quitLoop = true; +				} +				Testsuite::writeOnScreen("Mouse left-button released", pt); +				break; +			case Common::EVENT_RBUTTONUP: +				Testsuite::clearScreen(rect); +				Testsuite::writeOnScreen("Mouse right-button released", pt); +				break; +			case Common::EVENT_WHEELUP: +				Testsuite::clearScreen(rect); +				Testsuite::writeOnScreen("Mouse wheel moved up", pt); +				break; +			case Common::EVENT_MBUTTONUP: +				Testsuite::clearScreen(rect); +				Testsuite::writeOnScreen("Mouse middle-button released ", pt); +				break; +			case Common::EVENT_KEYDOWN: +				if (event.kbd.keycode == Common::KEYCODE_x) { +					Testsuite::clearScreen(rect); +					Testsuite::writeOnScreen("Exit requested", pt); +					quitLoop = true; +				} +				break; +			default: +				break; +			} + +		} +	} + +	CursorMan.showMouse(false); + +	// Verify results now! +	if (Testsuite::handleInteractiveInput("Were mouse clicks L/R/M buttons identfied?", "Yes", "No", kOptionRight)) { +		Testsuite::logDetailedPrintf("Mouse clicks (L/R/M buttons) failed"); +		passed = false; +	} +	if (Testsuite::handleInteractiveInput("Were mouse wheel movements identified?", "Yes", "No", kOptionRight)) { +		Testsuite::logDetailedPrintf("Mouse wheel movements failed"); +		passed = false; +	} + +	return passed; +} + +bool EventTests::kbdEvents() { +	 +	Testsuite::clearScreen(); +	Common::String info = "Testing keyboard events.\n " +	"Testbed should be able to figure out any alphanumeric keystrokes made by the user and display them back.\n" +	"Press ESC key when done of the input."; + +	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { +		Testsuite::logPrintf("Info! Skipping test : keyboard events\n"); +		return true; +	} + + +	// Make user type some word and display the output on screen +	Common::String text = "You Entered : "; +	Common::Point pt(0, 100); +	Testsuite::clearScreen(); +	Testsuite::writeOnScreen("Enter your word, press ESC when done, it will be echoed back", pt); +	pt.y += 20; +	Common::Rect rect = Testsuite::writeOnScreen(text, pt); +	char letter; +	while ((letter = keystrokeToChar()) != 0) { +		Testsuite::clearScreen(rect); +		text += letter; +		rect = Testsuite::writeOnScreen(text, pt); +	} + +	bool passed = true; + +	if (Testsuite::handleInteractiveInput("Was the word you entered same as that displayed on screen?", "Yes", "No", kOptionRight)) { +		Testsuite::logDetailedPrintf("Keyboard Events failed"); +		passed = false; +	} + +	Testsuite::clearScreen(); +	return passed; +} + +bool EventTests::showMainMenu() { +	 +	Testsuite::clearScreen(); +	Common::String info = "Testing Main Menu events.\n " +	"Main Menu event is normally trigerred by user pressing (Ctrl + f5).\n" +	"Click 'resume' to continue testbed."; + +	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { +		Testsuite::logPrintf("Info! Skipping test : Main Menu\n"); +		return true; +	} +	Common::EventManager *eventMan = g_system->getEventManager(); +	Common::Event mainMenuEvent; +	mainMenuEvent.type = Common::EVENT_MAINMENU; +	eventMan->pushEvent(mainMenuEvent); + +	bool passed = true; + +	if (Testsuite::handleInteractiveInput("Were you able to see a main menu widget?", "Yes", "No", kOptionRight)) { +		Testsuite::logDetailedPrintf("Event MAINMENU failed"); +		passed = false; +	} + +	return passed; +} + +EventTestSuite::EventTestSuite() { +	addTest("MouseEvents", &EventTests::mouseEvents); +	addTest("KeyboardEvents", &EventTests::kbdEvents); +	addTest("MainmenuEvent", &EventTests::showMainMenu); +} + +} // End of namespace Testbed diff --git a/engines/testbed/events.h b/engines/testbed/events.h new file mode 100644 index 0000000000..e857deec25 --- /dev/null +++ b/engines/testbed/events.h @@ -0,0 +1,67 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef TESTBED_EVENTS_H +#define TESTBED_EVENTS_H + +#include "testbed/testsuite.h" + +namespace Testbed { + +namespace EventTests { + +// Helper functions for Event tests +char keystrokeToChar(); +Common::Rect drawFinishZone(); +// will contain function declarations for Event tests +bool mouseEvents(); +bool kbdEvents(); +bool showMainMenu(); +// add more here + +} // End of namespace EventTests + +class EventTestSuite : public Testsuite { +public: +	/** +	 * The constructor for the EventTestSuite +	 * For every test to be executed one must: +	 * 1) Create a function that would invoke the test +	 * 2) Add that test to list by executing addTest() +	 * +	 * @see addTest() +	 */ +	EventTestSuite(); +	~EventTestSuite() {} +	const char *getName() const { +		return "Events"; +	} +	const char *getDescription() const { +		return "Events : Keyboard/Mouse/RTL"; +	} +}; + +} // End of namespace Testbed + +#endif // TESTBED_EVENTS_H diff --git a/engines/testbed/fs.cpp b/engines/testbed/fs.cpp new file mode 100644 index 0000000000..6bd67022ee --- /dev/null +++ b/engines/testbed/fs.cpp @@ -0,0 +1,169 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "common/config-manager.h" +#include "common/stream.h" +#include "common/util.h" + +#include "testbed/fs.h" + +namespace Testbed { + +/** + * This test does the following: + * 1) acquires the game-data path + * 2) In the game-root it navigates to "directory" and opens the file "file" + * + * The code accesses the appropriate file using the fileSystem API, creates a read stream of it and + * compares the message contained in it, with what it expects. + * + */ +bool FStests::readDataFromFile(Common::FSDirectory *directory, const char *file) { + +	Common::SeekableReadStream *readStream = directory->createReadStreamForMember(file); + +	if (!readStream) { +		Testsuite::logDetailedPrintf("Can't open game file for reading\n"); +		return false; +	} + +	Common::String msg = readStream->readLine(); +	delete readStream; + +	Testsuite::logDetailedPrintf("Message Extracted from %s/%s : %s\n", directory->getFSNode().getName().c_str(), file, msg.c_str()); + +	Common::String expectedMsg = "It works!"; + +	if (!msg.equals(expectedMsg)) { +		Testsuite::logDetailedPrintf("Can't read Correct data from file\n"); +		return false; +	} + +	return true; +} + +bool FStests::testReadFile() { +	const Common::String &path = ConfMan.get("path"); +	Common::FSDirectory gameRoot(path); +	int numFailed = 0; + +	if (!gameRoot.getFSNode().isDirectory()) { +		Testsuite::logDetailedPrintf("game Path should be a directory"); +		return false; +	} + +	const char *dirList[] = {"test1" ,"Test2", "TEST3" , "tEST4", "test5"}; +	const char *file[] = {"file.txt", "File.txt", "FILE.txt", "fILe.txt", "file"}; + +	for (unsigned int i = 0; i < ARRAYSIZE(dirList); i++) { +		Common::String dirName = dirList[i]; +		Common::String fileName = file[i]; +		Common::FSDirectory *directory = gameRoot.getSubDirectory(dirName); + +		if (!readDataFromFile(directory, fileName.c_str())) { +			Testsuite::logDetailedPrintf("Reading from %s/%s failed\n", dirName.c_str(), fileName.c_str()); +			numFailed++; +		} + +		dirName.toLowercase(); +		fileName.toLowercase(); +		delete directory; +		directory = gameRoot.getSubDirectory(dirName); + +		if (!readDataFromFile(directory, fileName.c_str())) { +			Testsuite::logDetailedPrintf("Reading from %s/%s failed\n", dirName.c_str(), fileName.c_str()); +			numFailed++; +		} + +		dirName.toUppercase(); +		fileName.toUppercase(); +		delete directory; +		directory = gameRoot.getSubDirectory(dirName); + +		if (!readDataFromFile(directory, fileName.c_str())) { +			Testsuite::logDetailedPrintf("Reading from %s/%s failed\n", dirName.c_str(), fileName.c_str()); +			numFailed++; +		} +		delete directory; +	} + +	Testsuite::logDetailedPrintf("Failed %d out of 15\n", numFailed); +	return false; +} + +/** + * This test creates a file testbed.out, writes a sample data and confirms if + * it is same by reading the file again. + */ +bool FStests::testWriteFile() { +	const Common::String &path = ConfMan.get("path"); +	Common::FSNode gameRoot(path); + +	Common::FSNode fileToWrite = gameRoot.getChild("testbed.out"); + +	Common::WriteStream *ws = fileToWrite.createWriteStream(); + +	if (!ws) { +		Testsuite::logDetailedPrintf("Can't open writable file in game data dir\n"); +		return false; +	} + +	ws->writeString("ScummVM Rocks!"); +	ws->flush(); +	delete ws; + +	Common::SeekableReadStream *rs = fileToWrite.createReadStream(); +	Common::String readFromFile = rs->readLine(); +	delete rs; + +	if (readFromFile.equals("ScummVM Rocks!")) { +		// All good +		Testsuite::logDetailedPrintf("Data written and read correctly\n"); +		return true; +	} + +	return false; +} + + + +FSTestSuite::FSTestSuite() { +	addTest("ReadingFile", &FStests::testReadFile, false); +	addTest("WritingFile", &FStests::testWriteFile, false); +} + +void FSTestSuite::enable(bool flag) { +	const Common::String &path = ConfMan.get("path"); +	Common::FSNode gameRoot(path); + +	Common::FSNode gameIdentificationFile = gameRoot.getChild("TESTBED"); +	if (!gameIdentificationFile.exists()) { +		logPrintf("WARNING! : Game Data not found. Skipping FS tests\n"); +		Testsuite::enable(false); +		return; +	} +	Testsuite::enable(flag); +} + +} // End of namespace Testbed diff --git a/engines/testbed/fs.h b/engines/testbed/fs.h new file mode 100644 index 0000000000..a5e79c10ce --- /dev/null +++ b/engines/testbed/fs.h @@ -0,0 +1,74 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef TESTBED_FS_H +#define TESTBED_FS_H + +#include "common/fs.h" + +#include "testbed/testsuite.h" + +namespace Testbed { + +namespace FStests { + +// Note: These tests require a game-data directory +// So would work if game-path is set in the launcher or invoked as ./scummvm --path="path-to-testbed-data" testbed +// from commandline + +// Helper functions for FS tests +bool readDataFromFile(Common::FSDirectory *directory, const char *file); + +// will contain function declarations for FS tests +bool testReadFile(); +bool testWriteFile(); +bool testOpeningSaveFile(); +// add more here + +} // End of namespace FStests + +class FSTestSuite : public Testsuite { +public: +	/** +	 * The constructor for the FSTestSuite +	 * For every test to be executed one must: +	 * 1) Create a function that would invoke the test +	 * 2) Add that test to list by executing addTest() +	 * +	 * @see addTest() +	 */ +	FSTestSuite(); +	~FSTestSuite() {} +	const char *getName() const { +		return "FS"; +	} +	const char *getDescription() const { +		return "File system tests (Navigation, Read/Write)"; +	} +	void enable(bool flag); +}; + +} // End of namespace Testbed + +#endif // TESTBED_FS_H diff --git a/engines/testbed/graphics.cpp b/engines/testbed/graphics.cpp new file mode 100644 index 0000000000..38f5daf35d --- /dev/null +++ b/engines/testbed/graphics.cpp @@ -0,0 +1,1102 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "common/events.h" +#include "common/list.h" +#include "common/random.h" + +#include "engines/engine.h" + +#include "testbed/graphics.h" +#include "testbed/testsuite.h" + +#include "graphics/cursorman.h" +#include "graphics/fontman.h" +#include "graphics/surface.h" +#include "graphics/VectorRendererSpec.h" + +namespace Testbed { + +byte GFXTestSuite::_palette[256 * 4] = {0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0}; + +GFXTestSuite::GFXTestSuite() { +	// Initialize color palettes +	// The fourth field is for alpha channel which is unused +	// Assuming 8bpp as of now +	g_system->setPalette(_palette, 0, 3); + +	// Init Mouse Palette (White-black-yellow) +	GFXtests::initMousePalette(); + +	// Add tests here + +	// Blitting buffer on screen +	addTest("BlitBitmaps", &GFXtests::copyRectToScreen); + +	// GFX Transcations +	addTest("FullScreenMode", &GFXtests::fullScreenMode); +	addTest("AspectRatio", &GFXtests::aspectRatio); +	addTest("IconifyingWindow", &GFXtests::iconifyWindow); + +	// Mouse Layer tests (Palettes and movements) +	addTest("PalettizedCursors", &GFXtests::palettizedCursors); +	addTest("MouseMovements", &GFXtests::mouseMovements); +	// FIXME: Scaled cursor crsh with odd dimmensions +	addTest("ScaledCursors", &GFXtests::scaledCursors); + +	// Effects +	addTest("shakingEffect", &GFXtests::shakingEffect); +	// addTest("focusRectangle", &GFXtests::focusRectangle); + +	// Overlay +	addTest("Overlays", &GFXtests::overlayGraphics); + +	// Specific Tests: +	addTest("PaletteRotation", &GFXtests::paletteRotation); +	addTest("cursorTrailsInGUI", &GFXtests::cursorTrails); +	//addTest("Pixel Formats", &GFXtests::pixelFormats); +} + +void GFXTestSuite::setCustomColor(uint r, uint g, uint b) { +	_palette[8] = r; +	_palette[9] = g; +	_palette[10] = b; +	 +	// Set colorNum kColorSpecial with a special color. +	int absIndx = kColorSpecial * 4; +	_palette[absIndx + 1] = 173; +	_palette[absIndx + 2] = 255; +	_palette[absIndx + 3] = 47; +	g_system->setPalette(_palette, 0, 256); +} + +// Helper functions used by GFX tests + +void GFXtests::initMousePalette() { +	byte palette[3 * 4]; // Black, white and yellow + +	palette[0] = palette[1] = palette[2] = 0; +	palette[4] = palette[5] = palette[6] = 255; +	palette[8] = palette[9] = 255; +	palette[10] = 0; + +	CursorMan.replaceCursorPalette(palette, 0, 3); +} + +Common::Rect GFXtests::computeSize(Common::Rect &cursorRect, int scalingFactor, int cursorTargetScale) { +	if (cursorTargetScale == 1 || scalingFactor == 1) { +		// Game data and cursor would be scaled equally. +		// so dimensions would be same. +		return Common::Rect(cursorRect.width(), cursorRect.height()); +	} + +	if (scalingFactor == 2) { +		// Game data is scaled by 2, cursor is said to be scaled by 2 or 3. so it wud not be scaled any further +		// So a w/2 x h/2 rectangle when scaled would match the cursor +		return Common::Rect(cursorRect.width() / 2, cursorRect.height() / 2); +	} + +	if (scalingFactor == 3) { +		// Cursor traget scale is 2 or 3. +		return Common::Rect((cursorRect.width() / cursorTargetScale), (cursorRect.height() / cursorTargetScale)); +	} else { +		Testsuite::logPrintf("Unsupported scaler %dx\n", scalingFactor); +		return Common::Rect(); +	} +} + +void GFXtests::HSVtoRGB(int &rComp, int &gComp, int &bComp, int hue, int sat, int val) { +	float r = rComp; +	float g = gComp; +	float b = bComp; + +	float h = hue * (360 / 256.0); // All colors are tried +	float s = sat; +	float v = val; + +	int i; +	float f, p, q, t; + +	if (s == 0) { +		r = g = b = v * 255; +		return; +	} + +	h /= 60; +	i = (int)h; +	f = h - i; +	p = v * (1 - s); +	q = v * (1 - s * f); +	t = v * (1 - s * (1 - f)); + +	switch (i) { +	case 0: +		r = v; +		g = t; +		b = p; +		break; +	case 1: +		r = q; +		g = v; +		b = p; +		break; +	case 2: +		r = p; +		g = v; +		b = t; +		break; +	case 3: +		r = p; +		g = q; +		b = v; +		break; +	case 4: +		r = t; +		g = p; +		b = v; +		break; +	default: +		r = v; +		g = p; +		b = q; +		break; +	} + +	rComp = (int)(r * 255); +	gComp = (int)(g * 255); +	bComp = (int)(b * 255); +} + +Common::Rect GFXtests::drawCursor(bool cursorPaletteDisabled, const char *gfxModeName, int cursorTargetScale) { +	// Buffer initialized with yellow color +	byte buffer[500]; +	memset(buffer, 2, sizeof(buffer)); + +	int cursorWidth = 12; +	int cursorHeight = 12; + +	/* Disable HotSpot highlighting as of now + +	// Paint the cursor with yellow, except the hotspot +	for (int i = 0; i < 16; i++) { +		for (int j = 0; j < 16; j++) { +			if (i != j && i != 15 - j) { +				buffer[i * 16 + j] = 2; +			} +		} +	} + +	*/ + +	// Uncommenting the next line and commenting the line after that would reproduce the crash +	// CursorMan.replaceCursor(buffer, 11, 11, 0, 0, 255, cursorTargetScale); +	CursorMan.replaceCursor(buffer, 12, 12, 0, 0, 255, cursorTargetScale); +	CursorMan.showMouse(true); + +	if (cursorPaletteDisabled) { +		CursorMan.disableCursorPalette(true); +	} else { +		initMousePalette(); +		CursorMan.disableCursorPalette(false); +	} + +	g_system->updateScreen(); +	return Common::Rect(0, 0, cursorWidth, cursorHeight); +} + +void rotatePalette(byte *palette, int size) { +	// Rotate the colors starting from address palette "size" times + +	// take a temporary palette color +	byte tColor[4] = {0}; +	// save first color in it. +	memcpy(tColor, &palette[0], 4 * sizeof(byte)); + +	// Move each color upward by 1 +	for (int i = 0; i < size - 1; i++) { +		memcpy(&palette[i * 4], &palette[(i + 1) * 4], 4 * sizeof(byte)); +	} +	// Assign last color to tcolor +	memcpy(&palette[(size - 1) * 4], tColor, 4 * sizeof(byte)); +} + +/** + * Sets up mouse loop, exits when user clicks any of the mouse button + */ +void GFXtests::setupMouseLoop(bool disableCursorPalette, const char *gfxModeName, int cursorTargetScale) { +	bool isFeaturePresent; +	isFeaturePresent = g_system->hasFeature(OSystem::kFeatureCursorHasPalette); +	Common::Rect cursorRect; + +	if (isFeaturePresent) { + +		cursorRect = GFXtests::drawCursor(disableCursorPalette, gfxModeName, cursorTargetScale); + +		Common::EventManager *eventMan = g_system->getEventManager(); +		Common::Event event; +		Common::Point pt(0, 100); + +		bool quitLoop = false; +		uint32 lastRedraw = 0; +		const uint32 waitTime = 1000 / 45; + +		Testsuite::clearScreen(); +		Common::String info = disableCursorPalette ? "Using Game Palette" : "Using cursor palette"; +		info += " to render the cursor, Click to finish"; + +		Common::String gfxScalarMode(gfxModeName); + +		if (!gfxScalarMode.equals("")) { +			info = "The cursor size (yellow) should match the red rectangle."; +		} +		 +		Testsuite::writeOnScreen(info, pt); + +		info = "GFX Mode"; +		info += gfxModeName; +		info += " "; + +		char cScale = cursorTargetScale + '0'; +		info += "Cursor scale: "; +		info += cScale; + +		Common::Rect estimatedCursorRect; + +		if (!gfxScalarMode.equals("")) { + +			if (gfxScalarMode.contains("1x")) { +				estimatedCursorRect = computeSize(cursorRect, 1, cursorTargetScale); +			} else if (gfxScalarMode.contains("2x")) { +				estimatedCursorRect = computeSize(cursorRect, 2, cursorTargetScale); +			} else if (gfxScalarMode.contains("3x")) { +				estimatedCursorRect = computeSize(cursorRect, 3, cursorTargetScale); +			} else { +				// If unable to detect scaler, default to 2 +				Testsuite::writeOnScreen("Unable to detect scaling factor, assuming 2x", Common::Point(0, 5)); +				estimatedCursorRect = computeSize(cursorRect, 2, cursorTargetScale); +			} +			Testsuite::writeOnScreen(info, Common::Point(0, 120)); + +			// Move cursor to (20, 20) +			g_system->warpMouse(20, 20); +			// Move estimated rect to (20, 20) +			estimatedCursorRect.moveTo(20, 20); + +			Graphics::Surface *screen = g_system->lockScreen(); +			GFXTestSuite::setCustomColor(255, 0, 0); +			screen->fillRect(estimatedCursorRect, 2); +			g_system->unlockScreen(); +			g_system->updateScreen(); +		} + +		while (!quitLoop) { +			while (eventMan->pollEvent(event)) { +				if (Engine::shouldQuit()) { +					// Quit directly +					return; +				} +				if (lastRedraw + waitTime < g_system->getMillis()) { +					g_system->updateScreen(); +					lastRedraw = g_system->getMillis(); +				} + +				switch (event.type) { +				case Common::EVENT_MOUSEMOVE: +					break; +				case Common::EVENT_LBUTTONDOWN: +				case Common::EVENT_RBUTTONDOWN: +					quitLoop = true; +					Testsuite::clearScreen(); +					Testsuite::writeOnScreen("Mouse clicked", pt); +					g_system->delayMillis(1000); +					break; +				default: +					break;// Ignore handling any other event + +				} +			} +		} +	} else { +		Testsuite::displayMessage("feature not supported"); +	} +} + +/** + * Used by aspectRatio() + */ +void GFXtests::drawEllipse(int cx, int cy, int a, int b) { +	 +	// Take a buffer of screen size +	int width = g_system->getWidth(); +	int height = Testsuite::getDisplayRegionCoordinates().y; +	byte *buffer = new byte[height * width]; +	float theta; +	int x, y, x1, y1; + +	memset(buffer, 0, sizeof(byte) * width * height); +	// Illuminate the center +	buffer[cx * width + cy] = 1; + +	// Illuminate the points lying on ellipse + +	for (theta = 0; theta <= PI / 2; theta += PI / 360) { +		x = (int)(b * sin(theta) + 0.5); +		y = (int)(a * cos(theta) + 0.5); + +		// This gives us four points + +		x1 = x + cx; +		y1 = y + cy; + +		buffer[x1 * width + y1] = 1; + +		x1 = (-1) * x + cx; +		y1 = y + cy; + +		buffer[x1 * width + y1] = 1; + +		x1 = x + cx; +		y1 = (-1) * y + cy; + +		buffer[x1 * width + y1] = 1; + +		x1 = (-1) * x + cx; +		y1 = (-1) * y + cy; + +		buffer[x1 * width + y1] = 1; +	} + +	g_system->copyRectToScreen(buffer, width, 0, 0, width, height); +	g_system->updateScreen(); +	delete[] buffer; +} + +// GFXtests go here + +/** + * Tests the fullscreen mode by: toggling between fullscreen and windowed mode + */ +bool GFXtests::fullScreenMode() { +	Testsuite::clearScreen(); +	Common::String info = "Fullscreen test. Here you should expect a toggle between windowed and fullscreen states depending " +	"upon your initial state."; + +	Common::Point pt(0, 100); +	Common::Rect rect = Testsuite::writeOnScreen("Testing fullscreen mode", pt); +	 +	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { +		Testsuite::logPrintf("Info! Skipping test : FullScreenMode\n"); +		return true; +	} + +	bool isFeaturePresent; +	bool isFeatureEnabled; +	bool passed = true; +	Common::String prompt; +	OptionSelected shouldSelect; + +	isFeaturePresent = g_system->hasFeature(OSystem::kFeatureFullscreenMode); + +	if (isFeaturePresent) { +		// Toggle +		isFeatureEnabled = g_system->getFeatureState(OSystem::kFeatureFullscreenMode); +		shouldSelect = isFeatureEnabled ? kOptionLeft : kOptionRight; + +		g_system->delayMillis(1000); + +		if (isFeatureEnabled) { +			Testsuite::logDetailedPrintf("Current Mode is Fullsecreen\n"); +		} else { +			Testsuite::logDetailedPrintf("Current Mode is Windowed\n"); +		} + +		prompt = " Which mode do you see currently ?  "; + +		if (!Testsuite::handleInteractiveInput(prompt, "Fullscreen", "Windowed", shouldSelect)) { +			// User selected incorrect current state +			passed = false; +			Testsuite::logDetailedPrintf("g_system->getFeatureState() failed\n"); +		} + +		g_system->beginGFXTransaction(); +		g_system->setFeatureState(OSystem::kFeatureFullscreenMode, !isFeatureEnabled); +		g_system->endGFXTransaction(); + +		// Current state should be now !isFeatureEnabled +		isFeatureEnabled = g_system->getFeatureState(OSystem::kFeatureFullscreenMode); +		shouldSelect = isFeatureEnabled ? kOptionLeft : kOptionRight; + +		g_system->delayMillis(1000); + +		prompt = "  Which screen mode do you see now ?   "; + +		if (!Testsuite::handleInteractiveInput(prompt, "Fullscreen", "Windowed", shouldSelect)) { +			// User selected incorrect mode +			passed = false; +			Testsuite::logDetailedPrintf("g_system->setFeatureState() failed\n"); +		} + +		g_system->beginGFXTransaction(); +		g_system->setFeatureState(OSystem::kFeatureFullscreenMode, !isFeatureEnabled); +		g_system->endGFXTransaction(); + +		g_system->delayMillis(1000); + +		prompt = "This should be your initial state. Is it?"; + +		if (!Testsuite::handleInteractiveInput(prompt, "Yes, it is", "Nopes", shouldSelect)) { +			// User selected incorrect mode +			Testsuite::logDetailedPrintf("switching back to initial state failed\n"); +			passed = false; +		} + +	} else { +		Testsuite::displayMessage("feature not supported"); +	} + +	return passed; +} + +/** + * Tests the aspect ratio correction by: drawing an ellipse, when corrected the ellipse should render to a circle + */ +bool GFXtests::aspectRatio() { +	 +	Testsuite::clearScreen(); +	Common::String info = "Aspect Ratio Correction test. If aspect ratio correction is enabled you should expect a circle on screen," +	" an ellipse otherwise."; + +	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { +		Testsuite::logPrintf("Info! Skipping test : Aspect Ratio\n"); +		return true; +	} +	// Draw an ellipse on the screen +	drawEllipse(80, 160, 72, 60); + +	bool isFeaturePresent; +	bool isFeatureEnabled; +	bool passed; +	Common::String prompt; +	OptionSelected shouldSelect; + +	isFeaturePresent = g_system->hasFeature(OSystem::kFeatureAspectRatioCorrection); +	isFeatureEnabled = g_system->getFeatureState(OSystem::kFeatureAspectRatioCorrection); +	g_system->delayMillis(1000); + +	if (isFeaturePresent) { +		// Toggle +		shouldSelect = isFeatureEnabled ? kOptionLeft : kOptionRight; +		prompt = " What does the curve on screen appears to you ?"; +		if (!Testsuite::handleInteractiveInput(prompt, "Circle", "Ellipse", shouldSelect)) { +			// User selected incorrect option +			passed = false; +			Testsuite::logDetailedPrintf("Aspect Ratio Correction failed\n"); +		} + +		g_system->beginGFXTransaction(); +		g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, !isFeatureEnabled); +		g_system->endGFXTransaction(); + +		g_system->delayMillis(1000); + +		shouldSelect = !isFeatureEnabled ? kOptionLeft : kOptionRight; +		prompt = " What does the curve on screen appears to you ?"; +		if (!Testsuite::handleInteractiveInput(prompt, "Circle", "Ellipse", shouldSelect)) { +			// User selected incorrect option +			passed = false; +			Testsuite::logDetailedPrintf("Aspect Ratio Correction failed\n"); +		} + +		g_system->beginGFXTransaction(); +		g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, isFeatureEnabled); +		g_system->endGFXTransaction(); +	} else { +		Testsuite::displayMessage("feature not supported"); +	} + +	g_system->delayMillis(500); + +	if (Testsuite::handleInteractiveInput("This should definetely be your initial state?", "Yes, it is", "Nopes", kOptionRight)) { +		// User selected incorrect mode +		Testsuite::logDetailedPrintf("Switching back to initial state failed\n"); +		passed = false; +	} + +	return passed; +} + +/** + * Tests Palettized cursors. + * Method: Create a yellow colored cursor, should be able to move it. Once you click test terminates + */ +bool GFXtests::palettizedCursors() { + +	Testsuite::clearScreen(); +	Common::String info = "Palettized Cursors test.\n " +		"Here you should expect to see a yellow mouse cursor rendered with mouse graphics.\n" +		"You would be able to move the cursor. Later we use game graphics to render the cursor.\n" +		"For cursor palette it should be yellow and will be red if rendered by the game palette.\n" +		"The test finishes when mouse (L/R) is clicked."; + + +	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { +		Testsuite::logPrintf("Info! Skipping test : Palettized Cursors\n"); +		return true; +	} +	 +	bool passed = true; + +	// Testing with cursor Palette +	setupMouseLoop(); +	 +	if (Testsuite::handleInteractiveInput("Which color did the cursor appeared to you?", "Yellow", "Any other", kOptionRight)) { +		Testsuite::logDetailedPrintf("Couldn't use cursor palette for rendering cursor\n"); +		passed = false; +	} + +	// Testing with game Palette +	GFXTestSuite::setCustomColor(255, 0, 0); +	setupMouseLoop(true); + +	if (Testsuite::handleInteractiveInput("Which color did the cursor appeared to you?", "Red", "Any other", kOptionRight)) { +		Testsuite::logDetailedPrintf("Couldn't use Game palette for rendering cursor\n"); +		passed = false; +	} + +	if (!Testsuite::handleInteractiveInput("Did test run as was described?")) { +		passed = false; +	} + +	// re-enable cursor palette +	CursorMan.disableCursorPalette(false); +	// Done with cursors, make them invisible, any other test the could simply make it visible +	CursorMan.showMouse(false); +	return passed; +} + +/** + * Tests automated mouse movements. "Warp" functionality provided by the backend. + */ + +bool GFXtests::mouseMovements() { +	Testsuite::clearScreen(); +	// Make mouse visible +	CursorMan.showMouse(true); +	 +	Common::String info = "Testing Automated Mouse movements.\n" +						"You should expect cursor hotspot(top-left corner) to automatically move from (0, 0) to (100, 100).\n" +						"There we have a rectangle drawn, finally the cursor would lie centred in that rectangle."; + +	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { +		Testsuite::logPrintf("Info! Skipping test : Mouse Movements\n"); +		return true; +	} +	 +	// Draw Rectangle +	Graphics::Surface *screen = g_system->lockScreen(); +	screen->fillRect(Common::Rect::center(106, 106, 14, 14), 2); +	g_system->unlockScreen(); + +	// Testing Mouse Movements now! +	Common::Point pt(0, 10); +	Testsuite::writeOnScreen("Moving mouse hotspot automatically from (0, 0) to (100, 100)", pt); +	g_system->warpMouse(0, 0); +	g_system->updateScreen(); +	g_system->delayMillis(1000); + +	for (int i = 0; i <= 100; i++) { +		g_system->delayMillis(20); +		g_system->warpMouse(i, i); +		g_system->updateScreen(); +	} + +	Testsuite::writeOnScreen("Mouse hotspot Moved to (100, 100)", pt); +	g_system->delayMillis(1500); +	CursorMan.showMouse(false); + +	if (Testsuite::handleInteractiveInput("Was the cursor symmetrically contained in the rectangle at (100, 100)?", "Yes", "No", kOptionRight)) { +		return false; +	} + +	return true; +} + + + +/** + * This basically blits the screen by the contents of its buffer. + * + */ +bool GFXtests::copyRectToScreen() { +	 +	Testsuite::clearScreen(); +	Common::String info = "Testing Blitting a Bitmap to screen.\n" +		"You should expect to see a 20x40 yellow horizontal rectangle centred at the screen."; + +	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { +		Testsuite::logPrintf("Info! Skipping test : Blitting Bitmap\n"); +		return true; +	} +	 +	GFXTestSuite::setCustomColor(255, 255, 0); +	byte buffer[20 * 40]; +	memset(buffer, 2, 20 * 40); + +	uint x = g_system->getWidth() / 2 - 20; +	uint y = g_system->getHeight() / 2 - 10; + +	g_system->copyRectToScreen(buffer, 40, x, y, 40, 20); +	g_system->updateScreen(); +	g_system->delayMillis(1000); + +	if (Testsuite::handleInteractiveInput("Did you see yellow rectangle?", "Yes", "No", kOptionRight)) { +		return false; +	} + +	return true; +} + +/** + * Testing feature : Iconifying window + * It is expected the screen minimizes when this feature is enabled + */ +bool GFXtests::iconifyWindow() { +	 +	Testsuite::clearScreen(); +	Common::String info = "Testing Iconify Window mode.\n If the feature is supported by the backend, " +		"you should expect the window to be minimized.\n However you would manually need to de-iconify."; + +	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { +		Testsuite::logPrintf("Info! Skipping test : Iconifying window\n"); +		return true; +	} +	 +	Common::Point pt(0, 100); +	Common::Rect rect = Testsuite::writeOnScreen("Testing Iconifying window", pt); + +	bool isFeaturePresent; +	bool isFeatureEnabled; + +	isFeaturePresent = g_system->hasFeature(OSystem::kFeatureIconifyWindow); +	isFeatureEnabled = g_system->getFeatureState(OSystem::kFeatureIconifyWindow); +	g_system->delayMillis(1000); + +	if (isFeaturePresent) { +		// Toggle + +		g_system->beginGFXTransaction(); +		g_system->setFeatureState(OSystem::kFeatureIconifyWindow, !isFeatureEnabled); +		g_system->endGFXTransaction(); + +		g_system->delayMillis(1000); + +		g_system->beginGFXTransaction(); +		g_system->setFeatureState(OSystem::kFeatureIconifyWindow, isFeatureEnabled); +		g_system->endGFXTransaction(); +	} else { +		Testsuite::displayMessage("feature not supported"); +	} + +	if (Testsuite::handleInteractiveInput("Did you see window minimized?", "Yes", "No", kOptionRight)) { +		return false; +	} + +	return true; +} + +/** + * Testing feature: Scaled cursors + */ +bool GFXtests::scaledCursors() { + +	Testsuite::clearScreen(); +	Common::String info = "Testing : Scaled cursors\n" +		"Here every graphics mode is tried with a cursorTargetScale of 1, 2 and 3.\n" +		"The expected cursor size is drawn as a rectangle, the cursor should entirely cover that rectangle.\n" +		"This may take time, You may skip the later scalers and just examine the first three i.e 1x, 2x and 3x"; + +	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { +		Testsuite::logPrintf("Info! Skipping test : Scaled Cursors\n"); +		return true; +	} + +	int maxLimit = 1000; +	if (!Testsuite::handleInteractiveInput("Do You want to restrict scalers to 1x, 2x and 3x only?", "Yes", "No", kOptionRight)) { +		maxLimit = 3; +	} + +	const int currGFXMode = g_system->getGraphicsMode(); +	const OSystem::GraphicsMode *gfxMode = g_system->getSupportedGraphicsModes(); + +	while (gfxMode->name && maxLimit > 0) { +		// for every graphics mode display cursors for cursorTargetScale 1, 2 and 3 +		// Switch Graphics mode +		// FIXME: Crashes with "3x" mode now.: +		g_system->beginGFXTransaction(); + +		bool isGFXModeSet = g_system->setGraphicsMode(gfxMode->id); +		g_system->initSize(320, 200); + +		OSystem::TransactionError gfxError = g_system->endGFXTransaction(); + +		if (gfxError == OSystem::kTransactionSuccess && isGFXModeSet) { +			setupMouseLoop(false, gfxMode->name, 1); +			Testsuite::clearScreen(); + +			setupMouseLoop(false, gfxMode->name, 2); +			Testsuite::clearScreen(); + +			setupMouseLoop(false, gfxMode->name, 3); +			Testsuite::clearScreen(); +		} else { +			Testsuite::logDetailedPrintf("Switching to graphics mode %s failed\n", gfxMode->name); +			return false; +		} +		gfxMode++; +		maxLimit--; +	} + +	// Restore Original State +	g_system->beginGFXTransaction(); +	bool isGFXModeSet = g_system->setGraphicsMode(currGFXMode); +	g_system->initSize(320, 200); +	OSystem::TransactionError gfxError = g_system->endGFXTransaction(); + +	if (gfxError != OSystem::kTransactionSuccess || !isGFXModeSet) { +		Testsuite::logDetailedPrintf("Switcing to initial state failed\n"); +		return false; +	} + +	// Done with cursors, Make them invisible, any other test may enable and use it. +	CursorMan.showMouse(false); +	return true; +} + +bool GFXtests::shakingEffect() { +	 +	Testsuite::clearScreen(); +	Common::String info = "Shaking test. You should expect the graphics(text/bars etc) drawn on the screen to shake!"; + +	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { +		Testsuite::logPrintf("Info! Skipping test : Shaking Effect\n"); +		return true; +	} + +	Common::Point pt(0, 100); +	Testsuite::writeOnScreen("If Shaking Effect works, this should shake!", pt); +	int times = 35; +	while (times--) { +		g_system->setShakePos(25); +		g_system->updateScreen(); +		g_system->setShakePos(0); +		g_system->updateScreen(); +	} +	g_system->delayMillis(1500); + +	if (Testsuite::handleInteractiveInput("Did the Shaking test worked as you were expecting?", "Yes", "No", kOptionRight)) { +		Testsuite::logDetailedPrintf("Shaking Effect didn't worked"); +		return false; +	} +	return true; +} + +bool GFXtests::focusRectangle() { +	 +	Testsuite::clearScreen(); +	Common::String info = "Testing : Setting and hiding Focus \n" +		"If this feature is implemented, the focus should be toggled between the two rectangles on the corners"; + +	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { +		Testsuite::logPrintf("Info! Skipping test : focus Rectangle\n"); +		return true; +	} +	 +	const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kConsoleFont)); + +	Graphics::Surface *screen = g_system->lockScreen(); +	int screenHeight = g_system->getHeight(); +	int screenWidth = g_system->getWidth(); + +	int height = font.getFontHeight(); +	int width = screenWidth / 2; + +	Common::Rect rectLeft(0, 0, width, height * 2); +	screen->fillRect(rectLeft, kColorWhite); +	font.drawString(screen, "Focus 1", rectLeft.left, rectLeft.top, width, kColorBlack, Graphics::kTextAlignLeft); + +	Common::Rect rectRight(screenWidth - width, screenHeight - height * 2 , screenWidth, screenHeight); +	screen->fillRect(rectRight, kColorWhite); +	font.drawString(screen, "Focus 2", rectRight.left, rectRight.top, width, kColorBlack, Graphics::kTextAlignRight); +	g_system->unlockScreen(); +	g_system->updateScreen(); + +	g_system->clearFocusRectangle(); + +	g_system->setFocusRectangle(rectLeft); +	g_system->updateScreen(); + +	g_system->delayMillis(1000); + +	g_system->setFocusRectangle(rectRight); +	g_system->updateScreen(); + +	g_system->clearFocusRectangle(); + +	if (Testsuite::handleInteractiveInput("Did you noticed a variation in focus?", "Yes", "No", kOptionRight)) { +		Testsuite::logDetailedPrintf("Focus Rectangle feature doesn't works. Check platform.\n"); +	} + +	return true; +} + +bool GFXtests::overlayGraphics() { +	Testsuite::clearScreen(); +	Common::String info = "Overlay Graphics. You should expect to see a green colored rectangle on the screen"; + +	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { +		Testsuite::logPrintf("Info! Skipping test : Overlay Graphics\n"); +		return true; +	} +	 +	Graphics::PixelFormat pf = g_system->getOverlayFormat(); + +	OverlayColor buffer[50 * 100]; +	OverlayColor value = pf.RGBToColor(0, 255, 0); + +	for (int i = 0; i < 50 * 100; i++) { +		buffer[i] = value; +	} + +	g_system->showOverlay(); +	g_system->copyRectToOverlay(buffer, 100, 270, 175, 100, 50); +	g_system->updateScreen(); + +	g_system->delayMillis(1000); + +	g_system->hideOverlay(); +	g_system->updateScreen(); + +	if (Testsuite::handleInteractiveInput("Did you see a green overlayed rectangle?", "Yes", "No", kOptionRight)) { +		Testsuite::logDetailedPrintf("Overlay Rectangle feature doesn't works\n"); +		return false; +	} + +	return true; +} + +bool GFXtests::paletteRotation() { +	 +	Common::String info = "Palette rotation. Here we draw a full 256 colored rainbow and then rotate it.\n" +						"Note that the screen graphics change without having to draw anything.\n" +						"The palette should appear to rotate, Click the mouse button to exit."; + +	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { +		Testsuite::logPrintf("Info! Skipping test : palette Rotation\n"); +		return true; +	} +	Common::Point pt(0, 10); +	Testsuite::clearEntireScreen(); + +	// Use 256 colors +	byte palette[256 * 4] = {0}; + +	int r, g, b; +	int colIndx; + +	for (int i = 0; i < 256; i++) { +		HSVtoRGB(r, g, b, i, 1, 1); +		colIndx = i * 4; +		palette[colIndx] = r; +		palette[colIndx + 1] = g; +		palette[colIndx + 2] = b; +	} + +	// Initialize this palette. +	g_system->setPalette(palette, 0, 256); + +	// Draw 256 Rectangles, each 1 pixel wide and 10 pixels long +	// one for 0-255 color range other for 0-127-255 range +	byte buffer[256 * 30] = {0}; + +	for (int i = 0; i < 30; i++) { +		for (int j = 0; j < 256; j++) { +			if (i < 10) { +				buffer[i * 256 + j] = j + 2; +			} else if (i < 20) { +				buffer[i * 256 + j] = 0; +			} else { +				buffer[i * 256 + j] = ((j + 127) % 256) + 2; +			} +		} +	} + +	g_system->copyRectToScreen(buffer, 256, 22, 50, 256, 30); +	 +	// Show mouse +	CursorMan.showMouse(true); +	g_system->updateScreen(); + + +	bool toRotate = true; +	Common::Event event; +	 +	while (toRotate) { +		while (g_system->getEventManager()->pollEvent(event)) { +			if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_RBUTTONDOWN) { +				toRotate = false; +			} +		} + +		rotatePalette(palette, 256); + +		g_system->delayMillis(10); +		g_system->setPalette(palette, 0, 256); +		g_system->updateScreen(); +	} + +	CursorMan.showMouse(false); +	// Reset initial palettes +	GFXTestSuite::setCustomColor(255, 0, 0); +	Testsuite::clearScreen(); + +	if(Testsuite::handleInteractiveInput("Did you saw a rotation in colors of rectangles displayed on screen?", "Yes", "No", kOptionRight)) { +		return false; +	} + +	return true; +} + +bool GFXtests::cursorTrails() { +	Common::String info = "With some shake offset the cursor was known to leave trails in the GUI\n" +						"Here we set some offset and ask user to check for mouse trails, \n" +						"the test is passed when there are no trails"; + +	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { +		Testsuite::logPrintf("Info! Skipping test : Cursor Trails\n"); +		return true; +	} +	bool passed = false; +	g_system->setShakePos(25); +	g_system->updateScreen(); +	if (Testsuite::handleInteractiveInput("Does the cursor leaves trails while moving?", "Yes", "No", kOptionRight)) { +		passed = true; +	} +	g_system->setShakePos(0); +	g_system->updateScreen(); +	return passed; +} + +bool GFXtests::pixelFormats() { +	Testsuite::clearScreen(); +	Common::String info = "Testing pixel formats. Here we iterate over all the supported pixel formats and display some colors using them\n" +		"This may take long, especially if the backend supports many pixel formats"; + +	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { +		Testsuite::logPrintf("Info! Skipping test : focus Rectangle\n"); +		return true; +	} + +	Common::List<Graphics::PixelFormat> pfList = g_system->getSupportedFormats(); +	Common::List<Graphics::PixelFormat>::const_iterator iter = pfList.begin(); + +	int numFormatsTested = 0; +	int numPassed = 0; +	bool numFailed = 0; + +	Testsuite::logDetailedPrintf("Testing Pixel Formats. Size of list : %d\n", pfList.size()); + +	for (iter = pfList.begin(); iter != pfList.end(); iter++) { +		numFormatsTested++; +		if (iter->bytesPerPixel == 1) { +			// Palettes already tested +			continue; +		} else if (iter->bytesPerPixel > 2) { +			Testsuite::logDetailedPrintf("Can't test pixels with bpp > 2\n"); +			continue; +		} + +		// Switch to that pixel Format +		g_system->beginGFXTransaction(); +		g_system->initSize(320, 200, &(*iter)); +		g_system->endGFXTransaction(); +		Testsuite::clearScreen(true); + +		// Draw some nice gradients +		// Pick up some colors +		uint colors[6]; + +		colors[0] = iter->RGBToColor(255, 255, 255); +		colors[1] = iter->RGBToColor(135, 48, 21); +		colors[2] = iter->RGBToColor(205, 190, 87); +		colors[3] = iter->RGBToColor(0, 32, 64); +		colors[4] = iter->RGBToColor(181, 126, 145); +		colors[5] = iter->RGBToColor(47, 78, 36); + +		Common::Point pt(0, 170); +		Common::String msg; +		// XXX: Can use snprintf? +		msg = Common::String::printf("Testing Pixel Formats, %d of %d", numFormatsTested, pfList.size()); +		Testsuite::writeOnScreen(msg, pt, true); + +		// CopyRectToScreen could have been used, but that may involve writing code which +		// already resides in graphics/surface.h +		// So using Graphics::Surface + +		Graphics::Surface *screen = g_system->lockScreen(); + +		// Draw 6 rectangles centred at (50, 160), piled over one another +		// each with color in colors[] +		for (int i = 0; i < 6; i++) { +			screen->fillRect(Common::Rect::center(160, 20 + i * 10, 100, 10), colors[i]); +		} + +		g_system->unlockScreen(); +		g_system->updateScreen(); +		g_system->delayMillis(500); + +		if(Testsuite::handleInteractiveInput("Were you able to notice the colored rectangles on the screen for this format?", "Yes", "No", kOptionLeft)) { +			numPassed++; +		} else { +			numFailed++; +			Testsuite::logDetailedPrintf("Testing pixel format failed for format #%d on the list\n", numFormatsTested); +		} +	} + +	// Revert back to 8bpp +	g_system->beginGFXTransaction(); +	g_system->initSize(320, 200); +	g_system->endGFXTransaction(); +	GFXTestSuite::setCustomColor(255, 0, 0); +	initMousePalette(); +	Testsuite::clearScreen(); + +	if (numFailed) { +		Testsuite::logDetailedPrintf("Pixel Format test: Failed : %d, Passed : %d, Ignored %d\n",numFailed, numPassed, numFormatsTested - (numPassed + numFailed)); +		return false; +	} + +	return true; +} + +} // End of namespace Testbed diff --git a/engines/testbed/graphics.h b/engines/testbed/graphics.h new file mode 100644 index 0000000000..42ab5df59e --- /dev/null +++ b/engines/testbed/graphics.h @@ -0,0 +1,94 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef TESTBED_GRAPHICS_H +#define TESTBED_GRAPHICS_H + +#include "testbed/testsuite.h" + +namespace Testbed { + +namespace GFXtests { + +// Helper functions for GFX tests +void drawEllipse(int x, int y, int a, int b); +void setupMouseLoop(bool disableCursorPalette = false, const char *gfxModeName = "", int cursorTargetScale = 1); +void initMousePalette(); +Common::Rect computeSize(Common::Rect &cursorRect, int scalingFactor, int cursorTargetScale); +void HSVtoRGB(int &rComp, int &gComp, int &bComp, int hue, int sat, int val); +Common::Rect drawCursor(bool cursorPaletteDisabled = false, const char *gfxModeName = "", int cursorTargetScale = 1); + +// will contain function declarations for GFX tests +bool cursorTrails(); +bool fullScreenMode(); +bool aspectRatio(); +bool palettizedCursors(); +bool mouseMovements(); +bool copyRectToScreen(); +bool iconifyWindow(); +bool scaledCursors(); +bool shakingEffect(); +bool focusRectangle(); +bool overlayGraphics(); +bool paletteRotation(); +bool pixelFormats(); +// add more here + +} // End of namespace GFXtests + +class GFXTestSuite : public Testsuite { +public: +	/** +	 * The constructor for the GFXTestSuite +	 * For every test to be executed one must: +	 * 1) Create a function that would invoke the test +	 * 2) Add that test to list by executing addTest() +	 * +	 * @see addTest() +	 */ +	GFXTestSuite(); +	~GFXTestSuite() {} +	const char *getName() const { +		return "GFX"; +	} +	const char *getDescription() const { +		return "Graphics Subsystem"; +	} +	static void setCustomColor(uint r, uint g, uint b); + +private: +	/** +	 * A Palette consists of 4 components RGBA. +	 * As of now we only take 3 colors +	 * 0 (R:0, G:0, B:0) Black (kColorBlack) +	 * 1 (R:255, G:255, B:255) White (kColorWhite) +	 * 2 (R:255, G:255, B:255) your customized color (by default white) (kColorCustom) +	 * The remaining values are zero +	 */ +	static byte _palette[256 * 4]; +}; + +} // End of namespace Testbed + +#endif // TESTBED_GRAPHICS_H diff --git a/engines/testbed/misc.cpp b/engines/testbed/misc.cpp new file mode 100644 index 0000000000..74f0af2948 --- /dev/null +++ b/engines/testbed/misc.cpp @@ -0,0 +1,172 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "testbed/misc.h" +#include "common/timer.h" + +namespace Testbed { + +Common::String MiscTests::getHumanReadableFormat(TimeDate &td) { +	return Common::String::printf("%d:%d:%d on %d/%d/%d (dd/mm/yyyy)", td.tm_hour, td.tm_min, td.tm_sec, td.tm_mday, td.tm_mon + 1, td.tm_year + 1900); +} + +void MiscTests::timerCallback(void *arg) { +	// Increment arg which actually points to an int +	// arg must point to a static data, threads otherwise have their own stack +	int &valToModify = *((int *) arg); +	valToModify = 999; // some arbitrary value +} + +void MiscTests::criticalSection(void *arg) { +	SharedVars &sv = *((SharedVars *)arg); + +	Testsuite::logDetailedPrintf("Before critical section: %d %d\n", sv.first, sv.second); +	g_system->lockMutex(sv.mutex); + +	// In any case, the two vars must be equal at entry, if mutex works fine. +	// verify this here. +	if (sv.first != sv.second) { +		sv.resultSoFar = false; +	} + +	sv.first++; +	g_system->delayMillis(1000); + +	// This should bring no change as well in the difference between vars +	// verify this too. +	if (sv.second + 1 != sv.first) { +		sv.resultSoFar = false; +	} + +	sv.second *= sv.first; +	Testsuite::logDetailedPrintf("After critical section: %d %d\n", sv.first, sv.second); +	g_system->unlockMutex(sv.mutex); + +	g_system->getTimerManager()->removeTimerProc(criticalSection); +} + +bool MiscTests::testDateTime() { +	 +	if (Testsuite::isSessionInteractive) { +		if (Testsuite::handleInteractiveInput("Testing the date time API implementation", "Continue", "Skip", kOptionRight)) { +			Testsuite::logPrintf("Info! Date time tests skipped by the user.\n"); +			return true; +		} +		 +		Testsuite::writeOnScreen("Verifying Date-Time...", Common::Point(0, 100)); +	} +	 +	TimeDate t1, t2; +	g_system->getTimeAndDate(t1); +	Testsuite::logDetailedPrintf("Current Time and Date: "); +	Common::String dateTimeNow; +	dateTimeNow = getHumanReadableFormat(t1); + +	if (Testsuite::isSessionInteractive) { +		// Directly verify date +		dateTimeNow = "We expect the current date time to be " + dateTimeNow; +		if (Testsuite::handleInteractiveInput(dateTimeNow, "Correct!", "Wrong", kOptionRight)) { +			return false; +		} +	} + +	g_system->getTimeAndDate(t1); +	dateTimeNow = getHumanReadableFormat(t1); +	Testsuite::logDetailedPrintf("%s\n", dateTimeNow.c_str()); +	// Now, Put some delay +	g_system->delayMillis(2000); +	g_system->getTimeAndDate(t2); +	Testsuite::logDetailedPrintf("Time and Date 2s later: "); +	dateTimeNow = getHumanReadableFormat(t2); +	Testsuite::logDetailedPrintf("%s\n", dateTimeNow.c_str()); + +	if (t1.tm_year == t2.tm_year && t1.tm_mon == t2.tm_mon && t1.tm_mday == t2.tm_mday) { +		if (t1.tm_mon == t2.tm_mon && t1.tm_year == t2.tm_year) { +			// Ignore lag due to processing time +			if (t1.tm_sec + 2 == t2.tm_sec) { +				return true; +			} +		} +	} +	return false; +} + +bool MiscTests::testTimers() { +	static int valToModify = 0; +	if (g_system->getTimerManager()->installTimerProc(timerCallback, 100000, &valToModify)) { +		g_system->delayMillis(150); +		g_system->getTimerManager()->removeTimerProc(timerCallback); + +		if (999 == valToModify) { +			return true; +		} +	} +	return false; +} + +bool MiscTests::testMutexes() { +	 +	if (Testsuite::isSessionInteractive) { +		if (Testsuite::handleInteractiveInput("Testing the Mutual Exclusion API implementation", "Continue", "Skip", kOptionRight)) { +			Testsuite::logPrintf("Info! Mutex tests skipped by the user.\n"); +			return true; +		} +		Testsuite::writeOnScreen("Installing mutex", Common::Point(0, 100)); +	} + +	static SharedVars sv = {1, 1, true, g_system->createMutex()}; + +	if (g_system->getTimerManager()->installTimerProc(criticalSection, 100000, &sv)) { +		g_system->delayMillis(150); +	} + +	g_system->lockMutex(sv.mutex); +	sv.first++; +	g_system->delayMillis(1000); +	sv.second *= sv.first; +	g_system->unlockMutex(sv.mutex); + +	// wait till timed process exits +	if (Testsuite::isSessionInteractive) { +		Testsuite::writeOnScreen("Waiting for 3s so that timed processes finish", Common::Point(0, 100)); +	} +	g_system->delayMillis(3000); + +	Testsuite::logDetailedPrintf("Final Value: %d %d\n", sv.first, sv.second); +	g_system->deleteMutex(sv.mutex); + +	if (sv.resultSoFar && 6 == sv.second) { +		return true; +	} + +	return false; +} + +MiscTestSuite::MiscTestSuite() { +	addTest("Datetime", &MiscTests::testDateTime, false); +	addTest("Timers", &MiscTests::testTimers, false); +	addTest("Mutexes", &MiscTests::testMutexes, false); +} + +} // End of namespace Testbed diff --git a/engines/testbed/misc.h b/engines/testbed/misc.h new file mode 100644 index 0000000000..1ca01224e5 --- /dev/null +++ b/engines/testbed/misc.h @@ -0,0 +1,80 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef TESTBED_MISC_H +#define TESTBED_MISC_H + +#include "testbed/testsuite.h" + + +namespace Testbed { + +// Shared variables used in mutex handling test +struct SharedVars { +	int first; +	int second; +	bool resultSoFar; +	OSystem::MutexRef mutex; +}; + +namespace MiscTests { + +// Miscellaneous tests include testing datetime, timers and mutexes + +// Helper functions for Misc tests +Common::String getHumanReadableFormat(TimeDate &td); +void timerCallback(void *arg); +void criticalSection(void *arg); + +// will contain function declarations for Misc tests +bool testDateTime(); +bool testTimers(); +bool testMutexes(); +// add more here + +} // End of namespace MiscTests + +class MiscTestSuite : public Testsuite { +public: +	/** +	 * The constructor for the MiscTestSuite +	 * For every test to be executed one must: +	 * 1) Create a function that would invoke the test +	 * 2) Add that test to list by executing addTest() +	 * +	 * @see addTest() +	 */ +	MiscTestSuite(); +	~MiscTestSuite() {} +	const char *getName() const { +		return "Misc"; +	} +	const char *getDescription() const { +		return "Miscellaneous: Timers/Mutexes/Datetime"; +	} +}; + +} // End of namespace Testbed + +#endif // TESTBED_MISC_H diff --git a/engines/testbed/module.mk b/engines/testbed/module.mk new file mode 100644 index 0000000000..4c530e412a --- /dev/null +++ b/engines/testbed/module.mk @@ -0,0 +1,24 @@ +MODULE := engines/testbed + +MODULE_OBJS := \ +	config.o \ +	detection.o \ +	events.o \ +	fs.o \ +	graphics.o \ +	misc.o \ +	savegame.o \ +	sound.o \ +	testbed.o \ +	testsuite.o + +MODULE_DIRS += \ +	engines/testbed + +# This module can be built as a plugin +ifeq ($(ENABLE_TESTBED), DYNAMIC_PLUGIN) +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/testbed/savegame.cpp b/engines/testbed/savegame.cpp new file mode 100644 index 0000000000..80c83725d2 --- /dev/null +++ b/engines/testbed/savegame.cpp @@ -0,0 +1,199 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "common/savefile.h" + +#include "testbed/savegame.h" + +namespace Testbed { + +/** + * This test creates a savefile for the given testbed-state and could be reloaded using the saveFile API. + * It is intended to test saving and loading from savefiles. + */ +bool SaveGametests::writeDataToFile(const char *fileName, const char *msg) { +	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); +	Common::OutSaveFile *saveFile = saveFileMan->openForSaving(fileName); + +	if (!saveFile) { +		Testsuite::logDetailedPrintf("Can't open saveFile %s\n", fileName); +		return false; +	} + +	saveFile->writeString(msg); +	saveFile->finalize(); +	delete saveFile; + +	return true; +} + +bool SaveGametests::readAndVerifyData(const char *fileName, const char *expected) { +	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); +	Common::InSaveFile *loadFile = saveFileMan->openForLoading(fileName); + +	if (!loadFile) { +		Testsuite::logDetailedPrintf("Can't open save File to load\n"); +		return false; +	} + +	Common::String lineToRead = loadFile->readLine(); +	delete loadFile; + +	if (lineToRead.equals(expected)) { +		return true; +	} + +	return false; +} + +bool SaveGametests::testSaveLoadState() { +	// create a savefile with "ScummVM Rocks!" written on it +	if (!writeDataToFile("tBedSavefile.0", "ScummVM Rocks!")) { +		Testsuite::logDetailedPrintf("Writing data to savefile failed\n"); +		return false; +	} + +	// Verify if it contains the same data +	if (!readAndVerifyData("tBedSavefile.0", "ScummVM Rocks!")) { +		Testsuite::logDetailedPrintf("Reading data from savefile failed\n"); +		return false; +	} + +	return true; +} + +bool SaveGametests::testRemovingSavefile() { +	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + +	// Create a dummy savefile +	if (!writeDataToFile("tBedSavefileToRemove.0", "Dummy Savefile!")) { +		Testsuite::logDetailedPrintf("Writing data to savefile failed\n"); +		return false; +	} + +	// Remove it +	saveFileMan->removeSavefile("tBedSavefileToRemove.0"); + +	// Try opening it Now +	Common::InSaveFile *loadFile = saveFileMan->openForLoading("saveFile.0"); +	if (loadFile) { +		// Removing failed +		Testsuite::logDetailedPrintf("Removing savefile failed\n"); +		return false; +	} + +	return true; +} + +bool SaveGametests::testRenamingSavefile() { +	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); +	// Open a file for renaming +	if (!writeDataToFile("tBedSomeWeirdName.0", "Rename me!")) { +		Testsuite::logDetailedPrintf("Writing data to savefile failed\n"); +		return false; +	} + +	// Rename it +	saveFileMan->renameSavefile("tBedSomeWeirdName.0", "tBedSomeCoolName.0"); + +	// Verify if it contains the same data +	if (!readAndVerifyData("tBedSomeCoolName.0", "Rename me!")) { +		Testsuite::logDetailedPrintf("Renaming savefile failed\n"); +		return false; +	} + +	return true; +} + +bool SaveGametests::testListingSavefile() { +	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); +	saveFileMan->clearError(); + +	// create some savefiles +	const char *savefileName[] = {"tBedSavefileToList.0", "tBedSavefileToList.1", "tBedSavefileToList.2"}; +	writeDataToFile("tBedSavefileToList.0", "Save me!"); +	writeDataToFile("tBedSavefileToList.1", "Save me!"); +	writeDataToFile("tBedSavefileToList.2", "Save me!"); + +	Common::Error error = saveFileMan->getError(); + +	if (error != Common::kNoError) { +		// Abort. Some Error in writing files +		Testsuite::logDetailedPrintf("Error while creating savefiles: %s\n", Common::errorToString(error)); +		return false; +	} + +	Common::StringArray savefileList = saveFileMan->listSavefiles("tBedSavefileToList.?"); +	if (savefileList.size() == ARRAYSIZE(savefileName)) { +		// Match them exactly +		// As the order of savefileList may be platform specific, match them exhaustively +		for (uint i = 0; i < ARRAYSIZE(savefileName); i++) { +			for (uint j = 0; j < savefileList.size(); j++) { +				if (savefileList[j].equals(savefileName[i])) { +					break; +				} +				if (savefileList.size() == j) { +					// A match for this name not found +					Testsuite::logDetailedPrintf("Listed Names don't match\n"); +					return false; +				} +			} +		} +		return true; +	} else { +		Testsuite::logDetailedPrintf("listing Savefiles failed!\n"); +		return false; +	} + +	return false; +} + +bool SaveGametests::testErrorMessages() { +	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); +	saveFileMan->clearError(); + +	// Try opening a non existing file +	readAndVerifyData("tBedSomeNonExistentSaveFile.0", "File doesn't exists!"); + +	Common::Error error = saveFileMan->getError(); +	if (error == Common::kNoError) { +		// blunder! how come? +		Testsuite::logDetailedPrintf("SaveFileMan.getError() failed\n"); +		return false; +	} +	// Can't actually predict whether which error, kInvalidPath or kPathDoesNotExist or some other? +	// So just return true if some error +	Testsuite::logDetailedPrintf("getError returned : %s\n", saveFileMan->getErrorDesc().c_str()); +	return true; +} + +SaveGameTestSuite::SaveGameTestSuite() { +	addTest("OpeningSaveFile", &SaveGametests::testSaveLoadState, false); +	addTest("RemovingSaveFile", &SaveGametests::testRemovingSavefile, false); +	addTest("RenamingSaveFile", &SaveGametests::testRenamingSavefile, false); +	addTest("ListingSaveFile", &SaveGametests::testListingSavefile, false); +	addTest("VerifyErrorMessages", &SaveGametests::testErrorMessages, false); +} + +} // End of namespace Testbed diff --git a/engines/testbed/savegame.h b/engines/testbed/savegame.h new file mode 100644 index 0000000000..98d630d237 --- /dev/null +++ b/engines/testbed/savegame.h @@ -0,0 +1,69 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef TESTBED_SAVEGAME_H +#define TESTBED_SAVEGAME_H + +#include "testbed/testsuite.h" + +namespace Testbed { + +namespace SaveGametests { + +// Helper functions for SaveGame tests +bool writeDataToFile(const char *fileName, const char *msg); +bool readAndVerifyData(const char *fileName, const char *expected); +// will contain function declarations for SaveGame tests +bool testSaveLoadState(); +bool testRemovingSavefile(); +bool testRenamingSavefile(); +bool testListingSavefile(); +bool testErrorMessages(); +// add more here + +} // End of namespace SaveGametests + +class SaveGameTestSuite : public Testsuite { +public: +	/** +	 * The constructor for the SaveGameTestSuite +	 * For every test to be executed one must: +	 * 1) Create a function that would invoke the test +	 * 2) Add that test to list by executing addTest() +	 * +	 * @see addTest() +	 */ +	SaveGameTestSuite(); +	~SaveGameTestSuite() {} +	const char *getName() const { +		return "SaveGames"; +	} +	const char *getDescription() const { +		return "Saving Game state tests"; +	} +}; + +} // End of namespace Testbed + +#endif // TESTBED_SAVEGAME_H diff --git a/engines/testbed/sound.cpp b/engines/testbed/sound.cpp new file mode 100644 index 0000000000..89aede7ef8 --- /dev/null +++ b/engines/testbed/sound.cpp @@ -0,0 +1,264 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "sound/audiocd.h" +#include "sound/softsynth/pcspk.h" + +#include "testbed/sound.h" + +namespace Testbed { + +enum { +	kPlayChannel1 = 'pch1', +	kPlayChannel2 = 'pch2', +	kPlayChannel3 = 'pch3', +	kPauseChannel1 = 'pac1', +	kPauseChannel2 = 'pac2', +	kPauseChannel3 = 'pac3' +}; + +SoundSubsystemDialog::SoundSubsystemDialog() : TestbedInteractionDialog(80, 60, 400, 170) { +	_xOffset = 25; +	_yOffset = 0; +	Common::String text = "Sound Subsystem Tests: Test Mixing of Audio Streams."; +	addText(350, 20, text, Graphics::kTextAlignCenter, _xOffset, 15); +	addButton(200, 20, "Play Channel #1", kPlayChannel1); +	addButton(200, 20, "Play Channel #2", kPlayChannel2); +	addButton(200, 20, "Play Channel #3", kPlayChannel3); +	addButton(50, 20, "Close", GUI::kCloseCmd, 160, 15); + +	_mixer = g_system->getMixer(); + +	// the three streams to be mixed	 +	Audio::PCSpeaker *s1 = new Audio::PCSpeaker(); +	Audio::PCSpeaker *s2 = new Audio::PCSpeaker(); +	Audio::PCSpeaker *s3 = new Audio::PCSpeaker(); +	 +	s1->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1); +	s2->play(Audio::PCSpeaker::kWaveFormSine, 1200, -1); +	s3->play(Audio::PCSpeaker::kWaveFormSine, 1400, -1); +	 +	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_h1, s1); +	_mixer->pauseHandle(_h1, true); + +	_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_h2, s2); +	_mixer->pauseHandle(_h2, true); + +	_mixer->playStream(Audio::Mixer::kSFXSoundType, &_h3, s3); +	_mixer->pauseHandle(_h3, true); + +} + + +void SoundSubsystemDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) { +	 +	switch (cmd) { +		case kPlayChannel1: +			_buttonArray[0]->setLabel("Pause Channel #1"); +			_buttonArray[0]->setCmd(kPauseChannel1); +			_mixer->pauseHandle(_h1, false); +			break; +		case kPlayChannel2: +			_buttonArray[1]->setLabel("Pause Channel #2"); +			_buttonArray[1]->setCmd(kPauseChannel2); +			_mixer->pauseHandle(_h2, false); +			break; +		case kPlayChannel3: +			_buttonArray[2]->setLabel("Pause Channel #3"); +			_buttonArray[2]->setCmd(kPauseChannel3); +			_mixer->pauseHandle(_h3, false); +			break; +		case kPauseChannel1: +			_buttonArray[0]->setLabel("Play Channel #1"); +			_buttonArray[0]->setCmd(kPlayChannel1); +			_mixer->pauseHandle(_h1, true); +			break; +		case kPauseChannel2: +			_buttonArray[1]->setLabel("Play Channel #2"); +			_buttonArray[1]->setCmd(kPlayChannel2); +			_mixer->pauseHandle(_h2, true); +			break; +		case kPauseChannel3: +			_buttonArray[2]->setLabel("Play Channel #3"); +			_buttonArray[2]->setCmd(kPlayChannel3); +			_mixer->pauseHandle(_h3, true); +			break; +		default: +			_mixer->stopAll(); +			GUI::Dialog::handleCommand(sender, cmd, data); +	} +} + +bool SoundSubsystem::playBeeps() { +	Testsuite::clearScreen(); +	bool passed = true;  +	Common::String info = "Testing Sound Output by generating beeps\n" +	"You should hear a left beep followed by a right beep\n"; + +	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { +		Testsuite::logPrintf("Info! Skipping test : Play Beeps\n"); +		return true; +	} +	 +	Audio::PCSpeaker *speaker = new Audio::PCSpeaker(); +	Audio::Mixer *mixer = g_system->getMixer(); +	Audio::SoundHandle handle; +	mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, speaker); +	 +	// Left Beep +	Testsuite::writeOnScreen("Left Beep", Common::Point(0, 100)); +	mixer->setChannelBalance(handle, -127); +	speaker->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1); +	g_system->delayMillis(500); +	mixer->pauseHandle(handle, true); +	 +	if (Testsuite::handleInteractiveInput("Were you able to hear the left beep?", "Yes", "No", kOptionRight)) { +		Testsuite::logDetailedPrintf("Error! Left Beep couldn't be detected : Error with Mixer::setChannelBalance()\n"); +		passed = false; +	} +	 +	// Right Beep +	Testsuite::writeOnScreen("Right Beep", Common::Point(0, 100)); +	mixer->setChannelBalance(handle, 127); +	mixer->pauseHandle(handle, false); +	g_system->delayMillis(500); +	mixer->stopAll(); +	 +	if (Testsuite::handleInteractiveInput("Were you able to hear the right beep?", "Yes", "No", kOptionRight)) { +		Testsuite::logDetailedPrintf("Error! Right Beep couldn't be detected : Error with Mixer::setChannelBalance()\n"); +		passed = false; +	} +	return passed; +} + +bool SoundSubsystem::mixSounds() { +	Testsuite::clearScreen(); +	bool passed = true;  +	Common::String info = "Testing Mixer Output by generating multichannel sound output using PC speaker emulator.\n" +	"The mixer should be able to play them simultaneously\n"; + +	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { +		Testsuite::logPrintf("Info! Skipping test : Mix Sounds\n"); +		return true; +	} + +	SoundSubsystemDialog sDialog; +	sDialog.runModal(); +	if (Testsuite::handleInteractiveInput("Was the mixer able to simultaneously play multiple channels?", "Yes", "No", kOptionRight)) { +		Testsuite::logDetailedPrintf("Error! Multiple channels couldn't be played : Error with Mixer Class\n"); +		passed = false; +	} +	return passed; +} + +bool SoundSubsystem::audiocdOutput() { +	Testsuite::clearScreen(); +	bool passed = true;  +	Common::String info = "Testing AudioCD API implementation.\n" +	"Here we have four tracks, we play them in order i.e 1-2-3-last.\n" +	"The user should verify if the tracks were run in correct order or not."; + +	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { +		Testsuite::logPrintf("Info! Skipping test : AudioCD API\n"); +		return true; +	} +	 +	Common::Point pt(0, 100); +	Testsuite::writeOnScreen("Playing the tracks of testCD in order i.e 1-2-3-last", pt); + +	// Make audio-files discoverable +	Common::FSNode gameRoot(ConfMan.get("path")); +	SearchMan.addSubDirectoryMatching(gameRoot, "audiocd-files"); + +	// Play all tracks +	for (int i = 1; i < 5; i++) {  +		AudioCD.play(i, 1, 0, 0); +		while (AudioCD.isPlaying()) { +			g_system->delayMillis(500); +			Testsuite::writeOnScreen(Common::String::printf("Playing Now: track%02d", i), pt); +		} +		g_system->delayMillis(500); +	} + +	Testsuite::clearScreen(); +	if (Testsuite::handleInteractiveInput("Were all the tracks played in order i.e 1-2-3-last ?", "Yes", "No", kOptionRight)) { +		Testsuite::logPrintf("Error! Error in AudioCD.play() or probably sound files were not detected, try -d1 (debuglevel 1)\n"); +		passed = false; +	} +	 +	return passed; +} + +bool SoundSubsystem::sampleRates() { +	bool passed = true; +	Audio::Mixer *mixer = g_system->getMixer(); + +	Audio::PCSpeaker *s1 = new Audio::PCSpeaker(); +	// Stream at half sampling rate +	Audio::PCSpeaker *s2 = new Audio::PCSpeaker(s1->getRate() - 10000); +	// Stream at twice sampling rate +	Audio::PCSpeaker *s3 = new Audio::PCSpeaker(s1->getRate() + 10000); +	 +	s1->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1); +	s2->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1); +	s3->play(Audio::PCSpeaker::kWaveFormSine, 1000, -1); +	 +	Audio::SoundHandle handle; +	Common::Point pt(0, 100); +	 +	mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, s1); +	Testsuite::writeOnScreen(Common::String::printf("Playing at smaple rate: %d", s1->getRate()), pt); +	g_system->delayMillis(1000); +	mixer->stopHandle(handle); +	g_system->delayMillis(1000); + +	mixer->playStream(Audio::Mixer::kSpeechSoundType, &handle, s2); +	Testsuite::writeOnScreen(Common::String::printf("Playing at sample rate : %d", s2->getRate()), pt); +	g_system->delayMillis(1000); +	mixer->stopHandle(handle); +	g_system->delayMillis(1000); + +	mixer->playStream(Audio::Mixer::kSFXSoundType, &handle, s3); +	Testsuite::writeOnScreen(Common::String::printf("Playing at sample rate : %d", s3->getRate()), pt); +	g_system->delayMillis(1000); +	mixer->stopHandle(handle); +	g_system->delayMillis(1000); +	 +	Testsuite::clearScreen(); +	if (Testsuite::handleInteractiveInput("Was the mixer able to play beeps with variable sample rates?", "Yes", "No", kOptionRight)) { +		Testsuite::logDetailedPrintf("Error! Error with variable sample rates\n"); +		passed = false; +	} + +	return passed; +} + +SoundSubsystemTestSuite::SoundSubsystemTestSuite() { +	addTest("SimpleBeeps", &SoundSubsystem::playBeeps, true); +	addTest("MixSounds", &SoundSubsystem::mixSounds, true); +	addTest("AudiocdOutput", &SoundSubsystem::audiocdOutput, true); +	addTest("SampleRates", &SoundSubsystem::sampleRates, true); +} + +}	// End of namespace Testbed diff --git a/engines/testbed/sound.h b/engines/testbed/sound.h new file mode 100644 index 0000000000..53ff96cedb --- /dev/null +++ b/engines/testbed/sound.h @@ -0,0 +1,80 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef TESTBED_SOUND_H +#define TESTBED_SOUND_H + +#include "gui/dialog.h" +#include "sound/mixer.h" +#include "testbed/config.h" +#include "testbed/testsuite.h" + +namespace Testbed { + +class SoundSubsystemDialog : public TestbedInteractionDialog { +public: +	SoundSubsystemDialog(); +	~SoundSubsystemDialog() {} +	void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); +	Audio::Mixer *_mixer; +	Audio::SoundHandle _h1, _h2, _h3; +}; + +namespace SoundSubsystem { + +// Helper functions for SoundSubsystem tests + +// will contain function declarations for SoundSubsystem tests +bool playBeeps(); +bool mixSounds(); +bool audiocdOutput(); +bool sampleRates(); +} + +class SoundSubsystemTestSuite : public Testsuite { +public: +	/** +	 * The constructor for the SoundSubsystemTestSuite +	 * For every test to be executed one must: +	 * 1) Create a function that would invoke the test +	 * 2) Add that test to list by executing addTest() +	 * +	 * @see addTest() +	 */ +	SoundSubsystemTestSuite(); +	~SoundSubsystemTestSuite() {} +	 +	const char *getName() const { +		return "SoundSubsystem"; +	} +	 +	const char *getDescription() const { +		return "Sound Subsystem"; +	} + +}; + +} // End of namespace Testbed + +#endif // TESTBED_SOUND_H diff --git a/engines/testbed/template.h b/engines/testbed/template.h new file mode 100644 index 0000000000..a2efca1157 --- /dev/null +++ b/engines/testbed/template.h @@ -0,0 +1,67 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef TESTBED_TEMPLATE_H +#define TESTBED_TEMPLATE_H + +#include "testbed/testsuite.h" + +// This file can be used as template for header files of other newer testsuites. + +namespace Testbed { + +namespace XXXtests { + +// Helper functions for XXX tests + +// will contain function declarations for XXX tests +// add more here + +} // End of namespace XXXtests + +class XXXTestSuite : public Testsuite { +public: +	/** +	 * The constructor for the XXXTestSuite +	 * For every test to be executed one must: +	 * 1) Create a function that would invoke the test +	 * 2) Add that test to list by executing addTest() +	 * +	 * @see addTest() +	 */ +	XXXTestSuite(); +	~XXXTestSuite() {} +	const char *getName() const { +		return "Dummy Template"; +	} +	 +	const char *getDescription() const { +		return "Some Arbit description"; +	} + +}; + +} // End of namespace Testbed + +#endif // TESTBED_TEMPLATE_H diff --git a/engines/testbed/testbed.cpp b/engines/testbed/testbed.cpp new file mode 100644 index 0000000000..ef61cd7c13 --- /dev/null +++ b/engines/testbed/testbed.cpp @@ -0,0 +1,169 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "common/debug-channels.h" +#include "common/scummsys.h" +#include "common/system.h" + +#include "engines/util.h" + +#include "testbed/events.h" +#include "testbed/fs.h" +#include "testbed/graphics.h" +#include "testbed/misc.h" +#include "testbed/savegame.h" +#include "testbed/sound.h" +#include "testbed/testbed.h" + +namespace Testbed { + +void TestbedExitDialog::init() { +	_xOffset = 25; +	_yOffset = 0; +	Common::String text = "Thank you for using ScummVM testbed! Here are yor summarized results:"; +	addText(450, 20, text, Graphics::kTextAlignCenter, _xOffset, 15); +	Common::Array<Common::String> strArray; +	 +	for (Common::Array<Testsuite *>::const_iterator i = _testsuiteList.begin(); i != _testsuiteList.end(); ++i) { +		strArray.push_back(Common::String::printf("%s    (%d/%d tests failed)", (*i)->getName(), (*i)->getNumTestsFailed(),  +		(*i)->getNumTestsEnabled())); +	} +	 +	addList(0, _yOffset, 500, 200, strArray); +	text = "More Details can be viewed in the Log file : " + Testsuite::getLogFile(); +	addText(450, 20, text, Graphics::kTextAlignLeft, 0, 0); +	text = "Directory : " + Testsuite::getLogDir(); +	addText(500, 20, text, Graphics::kTextAlignLeft, 0, 0); +	_yOffset += 5; +	addButtonXY(_xOffset + 80, _yOffset, 120, 20, "Rerun Tests", kCmdRerunTestbed); +	addButtonXY(_xOffset + 240, _yOffset, 60, 20, "Close", GUI::kCloseCmd); +} + +void TestbedExitDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) { +	switch (cmd) { +	case kCmdRerunTestbed : +		_rerun = true; +		GUI::Dialog::close(); +	default: +		GUI::Dialog::handleCommand(sender, cmd, data); +	} +} + +bool TestbedEngine::hasFeature(EngineFeature f) const { +	return (f == kSupportsRTL) ? true : false; +} + +TestbedEngine::TestbedEngine(OSystem *syst) + : Engine(syst) { +	// Put your engine in a sane state, but do nothing big yet; +	// in particular, do not load data from files; rather, if you +	// need to do such things, do them from init(). + +	// Do not initialize graphics here + +	// However this is the place to specify all default directories + +	DebugMan.addDebugChannel(kTestbedLogOutput, "LOG", "Log of test results generated by testbed"); +	DebugMan.addDebugChannel(kTestbedEngineDebug, "Debug", "Engine-specific debug statements"); +	DebugMan.enableDebugChannel("LOG"); + +	// Initialize testsuites here +	// GFX +	Testsuite *ts = new GFXTestSuite(); +	_testsuiteList.push_back(ts); +	// FS +	ts = new FSTestSuite(); +	_testsuiteList.push_back(ts); +	// Savegames +	ts = new SaveGameTestSuite(); +	_testsuiteList.push_back(ts); +	// Misc. +	ts = new MiscTestSuite(); +	_testsuiteList.push_back(ts); +	// Events +	ts = new EventTestSuite(); +	_testsuiteList.push_back(ts); +	// Sound +	ts = new SoundSubsystemTestSuite(); +	_testsuiteList.push_back(ts); +} + +TestbedEngine::~TestbedEngine() { +	Testsuite::deleteWriteStream(); +	// Remove all of our debug levels here +	DebugMan.clearAllDebugChannels(); + +	for (Common::Array<Testsuite *>::const_iterator i = _testsuiteList.begin(); i != _testsuiteList.end(); ++i) { +		delete (*i); +	} +} + +void TestbedEngine::invokeTestsuites(TestbedConfigManager &cfMan) { +	Common::Array<Testsuite *>::const_iterator iter; +	uint count = 1; +	Common::Point pt = Testsuite::getDisplayRegionCoordinates(); +	int numSuitesEnabled = cfMan.getNumSuitesEnabled(); + +	for (iter = _testsuiteList.begin(); iter != _testsuiteList.end(); iter++) { +		(*iter)->reset(); +		if ((*iter)->isEnabled()) { +			Testsuite::updateStats("Testsuite", (*iter)->getName(), count++, numSuitesEnabled, pt); +			(*iter)->execute(); +		} +	} +} + +Common::Error TestbedEngine::run() { +	// Initialize graphics using following: +	initGraphics(320, 200, false); + +	// As of now we are using GUI::MessageDialog for interaction, Test if it works. +	// interactive mode could also be modified by a config parameter "non-interactive=1" +	// TODO: Implement that + +	TestbedConfigManager cfMan(_testsuiteList, "testbed.config"); +	 +	// Keep running if rerun requested  +	TestbedExitDialog tbDialog(_testsuiteList); + +	do { +		Testsuite::clearEntireScreen(); +		cfMan.selectTestsuites(); +		// Init logging +		Testsuite::initLogging(true); +		// Check if user wanted to exit. +		if (Engine::shouldQuit()) { +			return Common::kNoError; +		} +		 +		invokeTestsuites(cfMan); +		tbDialog.init(); +		tbDialog.run(); + +	} while (tbDialog.rerunRequired()); +	 +	return Common::kNoError; +} + +} // End of namespace Testbed diff --git a/engines/testbed/testbed.h b/engines/testbed/testbed.h new file mode 100644 index 0000000000..3959865cfd --- /dev/null +++ b/engines/testbed/testbed.h @@ -0,0 +1,85 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef TESTBED_H +#define TESTBED_H + +#include "engines/engine.h" + +#include "gui/options.h" + +#include "testbed/config.h" +#include "testbed/testsuite.h" + +namespace Testbed { + +class TestbedConfigManager; + +enum { +	kTestbedLogOutput = 1 << 0, +	kTestbedEngineDebug = 1 << 2,  +	kCmdRerunTestbed = 'crtb' +}; + +class TestbedEngine : public Engine { +public: +	TestbedEngine(OSystem *syst); +	~TestbedEngine(); + +	virtual Common::Error run(); + +	/** +	 * Invokes configured testsuites. +	 */ +	void invokeTestsuites(TestbedConfigManager &cfMan); + +	bool hasFeature(EngineFeature f) const; +	 +private: +	Common::Array<Testsuite *> _testsuiteList; +}; + +class TestbedExitDialog : public TestbedInteractionDialog { +public: +	TestbedExitDialog(Common::Array<Testsuite *> &testsuiteList) : TestbedInteractionDialog(80, 60, 500, 320), _rerun(false),  +	_testsuiteList(testsuiteList) {} +	~TestbedExitDialog() {} +	void init(); +	void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); +	void run() { runModal(); } +	bool rerunRequired() { +		if (_rerun) { +			_rerun = false; +			return true; +		} +		return false; +	} +private: +	bool _rerun; +	Common::Array<Testsuite *> &_testsuiteList; +}; + +} // End of namespace Testbed + +#endif // TESTBED_H diff --git a/engines/testbed/testsuite.cpp b/engines/testbed/testsuite.cpp new file mode 100644 index 0000000000..efd31c051d --- /dev/null +++ b/engines/testbed/testsuite.cpp @@ -0,0 +1,373 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "common/config-manager.h" +#include "common/events.h" +#include "common/stream.h" + +#include "graphics/fontman.h" +#include "graphics/surface.h" + +#include "gui/message.h" + +#include "testbed/graphics.h" +#include "testbed/testbed.h" +#include "testbed/testsuite.h" + +namespace Testbed { + +// Static public variable of Testsuite +bool Testsuite::isSessionInteractive = true; + +// Static private variable of Testsuite +Common::String Testsuite::_logDirectory = ""; +Common::String Testsuite::_logFilename = ""; +Graphics::FontManager::FontUsage Testsuite::_displayFont = Graphics::FontManager::kGUIFont; +Common::WriteStream *Testsuite::_ws = 0; +uint Testsuite::toQuit = kLoopNormal; + +void Testsuite::setLogDir(const char *dirname) { +	_logDirectory = dirname; +} + +void Testsuite::setLogFile(const char *filename) { +	_logFilename = filename; +} + +void Testsuite::deleteWriteStream() { +	if (_ws) { +		delete _ws; +	} +} + +void Testsuite::initLogging(const char *logdir, const char *filename, bool enable) { +	setLogDir(logdir); +	setLogFile(filename); + +	if (enable) { +		_ws = Common::FSNode(_logDirectory).getChild(_logFilename).createWriteStream(); +	} else { +		_ws = 0; +	} +} + +void Testsuite::initLogging(bool enable) { +	setLogDir(ConfMan.get("path").c_str()); +	setLogFile("testbed.log"); + +	if (enable) { +		_ws = Common::FSNode(_logDirectory).getChild(_logFilename).createWriteStream(); +	} else { +		_ws = 0; +	} +} + +void Testsuite::logPrintf(const char *fmt, ...) { +	// Assuming log message size to be not greater than STRINGBUFLEN i.e 256 +	char buffer[STRINGBUFLEN]; +	va_list vl; +	va_start(vl, fmt); +	vsnprintf(buffer, STRINGBUFLEN, fmt, vl); +	va_end(vl); + +	if (_ws) { +		_ws->writeString(buffer); +		debugCN(kTestbedLogOutput, "%s", buffer); +	} else { +		debugCN(kTestbedLogOutput, "%s", buffer); +	} +} + +void Testsuite::logDetailedPrintf(const char *fmt, ...) { +	// Assuming log message size to be not greater than STRINGBUFLEN i.e 256 +	// Messages with this function would only be displayed if -d1 is specified on command line +	char buffer[STRINGBUFLEN]; +	va_list vl; +	va_start(vl, fmt); +	vsnprintf(buffer, STRINGBUFLEN, fmt, vl); +	va_end(vl); + +	if (_ws) { +		_ws->writeString(buffer); +		debugCN(1, kTestbedLogOutput, "%s", buffer); +	} else { +		debugCN(1, kTestbedLogOutput, "%s", buffer); +	} +} + +Testsuite::Testsuite() { +	_numTestsPassed = 0; +	_numTestsExecuted = 0; +	// Initially all testsuites are enabled, disable them by calling enableTestSuite(name, false) +	_isTsEnabled = true; +	// Set custom color for progress bar +	GFXTestSuite::setCustomColor(0, 0, 0); +} + +Testsuite::~Testsuite() { +	for (Common::Array<Test *>::iterator i = _testsToExecute.begin(); i != _testsToExecute.end(); ++i) { +		delete (*i); +	} +} + +void Testsuite::reset() { +	_numTestsPassed = 0; +	_numTestsExecuted = 0; +	toQuit = kLoopNormal; +	for (Common::Array<Test *>::iterator i = _testsToExecute.begin(); i != _testsToExecute.end(); ++i) { +		(*i)->passed = false; +	} +} + +void Testsuite::genReport() const { +	logPrintf("\n"); +	logPrintf("Consolidating results...\n"); +	logPrintf("Subsystem: %s ", getName()); +	logPrintf("(Tests Executed: %d)\n", _numTestsExecuted); +	logPrintf("Passed: %d ", _numTestsPassed); +	logPrintf("Failed: %d\n", getNumTestsFailed()); +	logPrintf("\n"); +} + +bool Testsuite::handleInteractiveInput(const Common::String &textToDisplay, const char *opt1, const char *opt2, OptionSelected result) { +	GUI::MessageDialog prompt(textToDisplay, opt1, opt2); +	return prompt.runModal() == result ? true : false; +} + +void Testsuite::displayMessage(const Common::String &textToDisplay, const char *defaultButton, const char *altButton) { +	GUI::MessageDialog prompt(textToDisplay, defaultButton); +	prompt.runModal(); +} + +Common::Rect Testsuite::writeOnScreen(const Common::String &textToDisplay, const Common::Point &pt, bool flag) { +	const Graphics::Font &font(*FontMan.getFontByUsage(Graphics::FontManager::kConsoleFont)); +	uint fillColor = kColorBlack; +	uint textColor = kColorWhite; + +	Graphics::Surface *screen = g_system->lockScreen(); + +	int height = font.getFontHeight(); +	int width = screen->w; + +	Common::Rect rect(pt.x, pt.y, pt.x + width, pt.y + height); + +	if (flag) { +		Graphics::PixelFormat pf = g_system->getScreenFormat(); +		fillColor = pf.RGBToColor(0, 0, 0); +		textColor = pf.RGBToColor(255, 255, 255); +	} + +	screen->fillRect(rect, fillColor); +	font.drawString(screen, textToDisplay, rect.left, rect.top, screen->w, textColor, Graphics::kTextAlignCenter); + +	g_system->unlockScreen(); +	g_system->updateScreen(); + +	return rect; +} + +void Testsuite::clearScreen(const Common::Rect &rect) { +	Graphics::Surface *screen = g_system->lockScreen(); + +	screen->fillRect(rect, kColorBlack); + +	g_system->unlockScreen(); +	g_system->updateScreen(); +} + +void Testsuite::clearScreen() { +	int numBytesPerLine = g_system->getWidth() * g_system->getScreenFormat().bytesPerPixel; +	int height = getDisplayRegionCoordinates().y; +	 +	// Don't clear test info display region +	int size =  height * numBytesPerLine; +	byte *buffer = new byte[size]; +	memset(buffer, 0, size); +	g_system->copyRectToScreen(buffer, numBytesPerLine, 0, 0, g_system->getWidth(), height); +	g_system->updateScreen(); +	delete[] buffer; +} + +void Testsuite::clearScreen(bool flag) { +	Graphics::Surface *screen = g_system->lockScreen(); +	uint fillColor = kColorBlack; + +	if (flag) { +		fillColor = g_system->getScreenFormat().RGBToColor(0, 0, 0); +	} + +	screen->fillRect(Common::Rect(0, 0, g_system->getWidth(), g_system->getHeight()), fillColor); + +	g_system->unlockScreen(); +	g_system->updateScreen(); +} + +void Testsuite::addTest(const Common::String &name, InvokingFunction f, bool isInteractive) { +	Test *featureTest = new Test(name, f, isInteractive); +	_testsToExecute.push_back(featureTest); +} + +int Testsuite::getNumTestsEnabled() { +	int count = 0; +	Common::Array<Test *>::const_iterator iter; + +	if (!isEnabled()) { +		return 0; +	} + +	for (iter = _testsToExecute.begin(); iter != _testsToExecute.end(); iter++) { +		if ((*iter)->enabled) { +			count++; +		} +	} +	return count; +} + +uint Testsuite::parseEvents() { +	uint startTime = g_system->getMillis(); +	uint end = startTime + kEventHandlingTime; +	do { +		Common::Event ev; +		while (g_system->getEventManager()->pollEvent(ev)) { +			switch (ev.type) { +			case Common::EVENT_KEYDOWN: +				if (ev.kbd.keycode == Common::KEYCODE_ESCAPE) { +					return kSkipNext; +				} +				break; +			case Common::EVENT_QUIT: +			case Common::EVENT_RTL: +				return kEngineQuit; +				break; +			default: +				break; +			} +		} +		g_system->delayMillis(10); +		startTime = g_system->getMillis(); +	} while (startTime <= end); + +	return kLoopNormal; +} + +void Testsuite::updateStats(const char *prefix, const char *info, uint testNum, uint numTests, Common::Point pt) { +	Common::String text = Common::String::printf(" Running %s: %s (%d of %d) ", prefix, info, testNum, numTests); +	writeOnScreen(text, pt); +	uint barColor = kColorSpecial;  +	// below the text a rectangle denoting the progress in the testsuite can be drawn. +	int separation = getLineSeparation(); +	pt.y += separation; +	int wRect = 200; +	int lRect = 7; +	pt.x = g_system->getWidth() / 2 - 100; +	byte *buffer = new byte[lRect * wRect]; +	memset(buffer, 0, sizeof(byte) * lRect * wRect); + +	int wShaded = (int) (wRect * (((float)testNum) / numTests)); + +	// draw the boundary +	memset(buffer, barColor, sizeof(byte) * wRect); +	memset(buffer + (wRect * (lRect - 1)) , barColor, sizeof(byte) * wRect); +	 +	for (int i = 0; i < lRect; i++) { +		for (int j = 0; j < wRect; j++) { +			if (j < wShaded) { +				buffer[i * wRect + j] = barColor; +			} +		} +		buffer[i * wRect + 0] = barColor; +		buffer[i * wRect + wRect - 1] = barColor; +	} +	g_system->copyRectToScreen(buffer, wRect, pt.x, pt.y, wRect, lRect); +	g_system->updateScreen(); +	delete[] buffer; +} + +bool Testsuite::enableTest(const Common::String &testName, bool toEnable) { +	for (uint i = 0; i < _testsToExecute.size(); i++) { +		if (_testsToExecute[i]->featureName.equalsIgnoreCase(testName)) { +			_testsToExecute[i]->enabled = toEnable; +			return true; +		} +	} +	return false; +} + + +void Testsuite::execute() { +	// Main Loop for a testsuite + +	// Do nothing if meant to exit +	if (toQuit == kEngineQuit) { +		return; +	} + +	uint count = 0; +	Common::Point pt = getDisplayRegionCoordinates(); +	pt.y += getLineSeparation(); +	int numEnabledTests = getNumTestsEnabled(); + +	for (Common::Array<Test *>::iterator i = _testsToExecute.begin(); i != _testsToExecute.end(); ++i) { +		if (!(*i)->enabled) { +			logPrintf("Info! Skipping Test: %s, Skipped by configuration.\n", ((*i)->featureName).c_str()); +			continue;	 +		} + +		if (toQuit == kSkipNext) { +			logPrintf("Info! Skipping Test: %s, Skipped by user.\n", ((*i)->featureName).c_str()); +			toQuit = kLoopNormal; +			continue; +		} + +		if((*i)->isInteractive && !isSessionInteractive) { +			logPrintf("Info! Skipping Test: %s, non-interactive environment is selected\n", ((*i)->featureName).c_str()); +			continue; +		} + +		logPrintf("Info! Executing Test: %s\n", ((*i)->featureName).c_str()); +		updateStats("Test", ((*i)->featureName).c_str(), count++, numEnabledTests, pt); +		_numTestsExecuted++; +		if ((*i)->driver()) { +			logPrintf("Result: Passed\n"); +			_numTestsPassed++; +		} else { +			logPrintf("Result: Failed\n"); +		} +		updateStats("Test", ((*i)->featureName).c_str(), count, numEnabledTests, pt); +		// TODO: Display a screen here to user with details of upcoming test, he can skip it or Quit or RTL +		// Check if user wants to quit/RTL/Skip next test by parsing events. +		// Quit directly if explicitly requested + +		if (Engine::shouldQuit()) { +			toQuit = kEngineQuit; +			genReport(); +			return; +		} + +		toQuit = parseEvents(); +	} +	genReport(); +} + +} // End of namespace Testebed diff --git a/engines/testbed/testsuite.h b/engines/testbed/testsuite.h new file mode 100644 index 0000000000..bd05f36e87 --- /dev/null +++ b/engines/testbed/testsuite.h @@ -0,0 +1,214 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef TESTBED_TESTSUITE_H +#define TESTBED_TESTSUITE_H + +#include "common/system.h" +#include "common/str.h" +#include "common/array.h" + +#include "graphics/fontman.h" + +namespace Testbed { + +enum { +	kColorBlack = 0, +	kColorWhite = 1, +	kColorCustom = 2, +	kColorSpecial = 5 	///< some random number +}; + +enum OptionSelected { +	kOptionLeft = 1, +	kOptionRight = 0 +}; + +enum { +	kEngineQuit = 0, +	kSkipNext = 1, +	kLoopNormal = 2, +	// Event handling time,(in ms) used in parseEvent() +	kEventHandlingTime = 50 +}; + +typedef bool (*InvokingFunction)(); + +/** + * This represents a feature to be tested + */ + +struct Test { +	Test(Common::String name, InvokingFunction f, bool interactive) : featureName(name) { +		driver = f; +		enabled = true; +		passed = false; +		isInteractive = interactive; +	} +	const Common::String featureName;	///< Name of feature to be tested +	InvokingFunction driver;		///< Pointer to the function that will invoke this feature test +	bool enabled;				    ///< Decides whether or not this test is to be executed +	bool passed;					///< Collects and stores result of this feature test +	bool isInteractive;				///< Decides if the test is interactive or not, An interactive testsuite may have non-interactive tests, hence this change. +}; + + +/** + * The basic Testsuite class + * All the other testsuites would inherit it and override its virtual methods + */ + +class Testsuite { +public: +	Testsuite(); +	virtual ~Testsuite(); +	int getNumTests() const { return _testsToExecute.size(); } +	int getNumTestsPassed() const { return _numTestsPassed; } +	int getNumTestsFailed() const { return _numTestsExecuted - _numTestsPassed; } +	void genReport() const; +	bool isEnabled() const { return _isTsEnabled; } +	virtual void enable(bool flag) { +		_isTsEnabled = flag; +	} +	bool enableTest(const Common::String &testName, bool enable); +	void reset(); +	 +	/** +	 * Prompts for User Input in form of "Yes" or "No" for interactive tests +	 * e.g: "Is this like you expect?" "Yes" or "No" +	 * +	 * @param	textToDisplay Display text +	 * @return	true if "Yes" false otherwise +	 */ +	static bool handleInteractiveInput(const Common::String &textToDisplay, const char *opt1 = "Yes", const char *opt2 = "No", OptionSelected result = kOptionLeft); + +	static void displayMessage(const Common::String &textToDisplay, const char *defaultButton = "OK", const char *altButton = 0); +	static Common::Rect writeOnScreen(const Common::String &textToDisplay, const Common::Point &pt, bool flag = false); +	static void clearScreen(const Common::Rect &rect); +	static void clearEntireScreen() { +		const int width = g_system->getWidth(); +		const int height = g_system->getHeight(); +		Common::Rect r(0, 0, width, height); +		clearScreen(r); +	} +	static void clearScreen(); +	static void clearScreen(bool flag); + +	/** +	 * Adds a test to the list of tests to be executed +	 * +	 * @param	name the string description of the test, for display purposes +	 * @param	f pointer to the function that invokes this test +	 * @param	isInteractive	decides if the test is to be executed in interactive mode/ default true +	 */ +	void addTest(const Common::String &name, InvokingFunction f, bool isInteractive = true); + +	/** +	 * The driver function for the testsuite +	 * All code should go in here. +	 */ +	virtual void execute(); +	static uint parseEvents(); + +	virtual const char *getName() const = 0; +	virtual const char *getDescription() const = 0; + +	static void logPrintf(const char *s, ...) GCC_PRINTF(1, 2); +	static void logDetailedPrintf(const char *s, ...) GCC_PRINTF(1, 2); +	/** +	 * Note: To enable logging, this function must be called once first. +	 */ +	static void initLogging(const char *dirname, const char *filename, bool enable = true); +	static void initLogging(bool enable = true); +	static void setLogDir(const char *dirname); +	static void setLogFile(const char *filename); +	static Common::String getLogDir() { return _logDirectory; } +	static Common::String getLogFile() { return _logFilename; } + +	static void deleteWriteStream(); + +	// Progress bar (Information Display) related methods. +	/**  +	 * Display region is in the bottom. Probably 1/4th of the game screen. +	 * It contains: +	 * 1) Information about executing testsuite. +	 * 2) Total progress within this testsuite. +	 * 3) Total overall progress in the number of testsuites +	 */ + +	static Common::Point getDisplayRegionCoordinates() { +		Common::Point pt(0, 0); +		// start from bottom +		pt.y = g_system->getHeight(); +		// Will Contain 3 lines +		pt.y -= (FontMan.getFontByUsage(_displayFont)->getFontHeight() * 3 + 15); // Buffer of 5 pixels per line +		return pt; +	} + +	static uint getLineSeparation() { +		return FontMan.getFontByUsage(_displayFont)->getFontHeight() + 5; +	} +	static Graphics::FontManager::FontUsage getCurrentFontUsageType() { return _displayFont; } +	static void setCurrentFontUsageType(Graphics::FontManager::FontUsage f) { _displayFont = f; } + +	static void updateStats(const char *prefix, const char *info, uint numTests, uint testNum, Common::Point pt); +	const Common::Array<Test *>& getTestList() { return _testsToExecute; } +	int getNumTestsEnabled(); + +protected: +	Common::Array<Test *> _testsToExecute;			///< List of tests to be executed +	int		    _numTestsPassed;					///< Number of tests passed +	int			_numTestsExecuted;					///< Number of tests executed +	bool		_isTsEnabled; + +public: + +	/** +	 * Static variable of this class that determines if the user initiated testing session is interactive or not. +	 * Used by various tests to respond accordingly +	 */ +	static bool isSessionInteractive; + +	/** +	 * Used from the code to decide if the engine needs to exit +	 */ +	static uint	toQuit; + +private: +	/** +	 * Private variables related to logging files +	 */ +	static Common::String _logDirectory; +	static Common::String _logFilename; +	static Common::WriteStream *_ws; + +	/** +	 * Private variable used for font +	 */ +	static Graphics::FontManager::FontUsage	_displayFont; +}; + +} // End of namespace Testbed + +#endif // TESTBED_TESTSUITE_H | 
