Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Блоги > e4xu

Всякие разные штуки сомнительной полезности сделанные в свободное от работы время.
Оценить эту запись

Велосипед с гиперкубическими колесами

Запись от wvxvw размещена 15.04.2011 в 16:40
Обновил(-а) wvxvw 16.04.2011 в 16:47

Для того, чтобы кататься в четвертом измерении, естественно.
Четвертое измерение можно воспринимать как своего рода надстройку над известными нам тремя (геометрическими, речь не идет о времени, как измерении, но, если ты хочешь, любознательный читатель, то ты можешь его тоже добавить).
Если вы посмотрите на написание программы, как на проектирование изображений в трехмерном измерении, то, создание программы, которая создает программы будет, по-своему, четвертым измерением. Написание программ которые пишут другие программы часто называют мета-программированием. Как правило, практические задачи решаемые мета-программированием сводятся к минимизации нудной работы - автоматизации создания служебного кода, который не интересно писать вручную. Но, это всего лишь маленькая толика тех задач, которые способно решать мета-программирование. В любой не-тривиальной программе направлений на получение ввода от пользователя (а не другой программы) рано или поздно возникает задача автоматизации. А именно:
  • Автоматическое тестирование, которое включает имитацию действий которые мог бы совершить пользователь во время работы с программой.
  • Макросы, самый минималистический макрос - запись всех действий пользователя и воспроизведение их в необходимом порядке (undo). Но помимо этого программа может предоставлять интерфейс для выполнения последовательностей операций ее составляющих. Наверняка вы знаете о том, что во Flash IDE есть JSFL, в FlashDevelop можно писать на C# Script, для Эклипса есть множество разных вариантов, включая реализации Lua, Python и Lisp на Java.
Для флеша было сделано несколько попыток создания универсального языка, который бы позволил, при желании, автоматизировать выполнение любой программы. Например http://www.poonya.com/ написали VB-подобный скриптовый язык для таких целей.
То, что я сейчас делаю, и собираюсь вам показать, это совсем только начало, наверняка с кучей недостатков и будет еще сто раз переделываться, но, тем не менее результаты кое-каие уже есть. Начал я этим заниматься не совсем от безделья, а по причине невозможности как-то автоматизировать тестирование приложения, написанного задолго до того, как меня приняли на работу и работающего из рук вон плохо... Естественно, приложение не маленькое, и написанное в худших традиция AS3 "фреймворков" с кучей синглтонов и кросс-референсов, которые делают модульное тестирование невозможным. Но нужно латать дыры и в уже существующей программе, потому, что переписать всю, за короткое время нет возможности. Латание дыр уже само по себе муторная задача, но гораздо тяжелее отыскивать нештатным ситуации. Это, в том числе связано с тем, что какие-то ситуации могут происходить только по истечении значительного количества времени, или, активная продолжительная деятельность пользователя может внезапно что-то сломать - посадить программиста воспроизводить баг, на воспроизведение которого нужно более часа - расточительство, да и сама программа так никогда не будет написана.
Вот так и появилась идея написания скриптового языка, который бы мог управлять любой программой, ну, на сколько это возможно. А еще лучше с возможностью передачи команд в реальном времени, изменении хода программы во время выполнения и, желательно, полностью автономного, т.е. такого, что не нужно было бы менять существующий код, для того, чтобы его можно было тестировать.

Ниже - буквально один из первых тестов. Язык очень похож на Лисп, но хуже и мало чего еще умеет, но я надеюсь по-тихоньку расти и совершенствовать. Одной из интересных задач было написание декларации функций. В процессе я понял "зачем", например, в PHP существует global для аргументов. Даже не сколько "зачем", а почему это плохо и очень примитивно с точки зрения языкостроительства
Исходники тут: http://code.google.com/p/e4xu/source...s%2Fautomation
Пример, вот он:
Код AS3:
package examples
{
	import flash.display.Sprite;
 
	import org.wvxvws.automation.ParensParser;
 
	public class TestZinScript extends Sprite
	{
		private var _parser:ParensParser = new ParensParser();
 
		public function TestZinScript()
		{
			super();
			this.test();
		}
 
		private function test():void
		{
			// Makes all public methods accessible by names so
			// we don't need to use utils:slot-value all the time.
			this._parser.pushContext(this, "test-runner");
			this._parser.read(
<![CDATA[
				(test-runner:testMethod)
				(test-runner:testMethodWithParameters 100 "Hello from outer space")
 
				(utils:print "Running ZinScript, highly experimental version...")
 
				(lang:defvar "test-var" 42)
 
				(utils:dotimes (math:- 2 1) 
					(lang:getvar "time:interval") ; This is not ideal, will need to pass a list instead
					(lang:getvar "test-runner:testMethodWithParameters") 1000 
						(lang:setvar "test-var" (math:++ (lang:getvar "test-var"))) "Hello...")
 
				(utils:dotimes (math:+ 0 2 -10 9) 
					(lang:getvar "time:timeout") ; Just same as above, need body form for loops and functions
					(lang:getvar "test-runner:testMethod") 1000 5)
 
				(bool:if (math:> (math:random 10) 5)
					(utils:print "Executing true condition")
					(utils:print "Executing false condition"))
 
				; This is line comment
 
				(utils:print (string:+ "foo" "|" "bar"))
 
				(lang:defun "test-function" (lang:arguments "param1" "param2")
					(lang:body-form '(utils:print param1)
						'(utils:print param2)))
 
				(utils:print (lang:getvar "test-function"))
 
				(test-function "hello" "world")
 
				(lang:defpackage "tests")
				(lang:inpackage "tests")
				(lang:defvar "test-sprite" (lang:new (utils:resolve-class "flash.display.Sprite")))
				(lang:defvar "graphics" (utils:slot-value (lang:getvar "test-sprite") "graphics"))
				(utils:funcall (utils:slot-value (lang:getvar "graphics") "beginFill") 255)
				(utils:funcall (utils:slot-value (lang:getvar "graphics") "drawRect") 0 0 100 200)
				(utils:funcall (utils:slot-value (lang:getvar "graphics") "endFill"))
				(test-runner:addChild (lang:getvar "test-sprite"))
				(utils:print "debugging..." (lang:package) (utils:slot-value (lang:package) "name"))
				(utils:print "moar..." (lang:getvar "test-sprite") (lang:getvar "tests:test-sprite"))
				(lang:extern "test-sprite")
 
				(lang:defun "test-handler" (lang:arguments "event")
					(lang:body-form '(utils:print "I am an enterFrame handler" event)
						'(utils:set-slot (lang:getvar "tests:test-sprite") "x" 
							(math:++ (utils:slot-value (lang:getvar "tests:test-sprite") "x")))))
				(test-runner:addEventListener "enterFrame" (lang:getvar "test-handler"))
]]>.toString());
		}
 
		public function testMethod():void
		{
			trace("I am testMethod");
		}
 
		public function testMethodWithParameters(foo:int, bar:String):void
		{
			trace("I am testMethodWithParameters", foo, bar);
		}
	}
}
Если у вас все собралось и запустилось, то вы должны увидеть в консоли что-то в роде:
Код:
I am testMethod
I am testMethodWithParameters 100 Hello from outer space
Running ZinScript, highly experimental version...
Executing false condition
foo|bar
function Function() {}
hello
world
debugging... [object ParensPackage] tests
moar... [object Sprite] [object Sprite]
I am an enterFrame handler [Event type="enterFrame" bubbles=false cancelable=false eventPhase=2]
I am testMethodWithParameters 43 Hello...
I am testMethod
I am an enterFrame handler [Event type="enterFrame" bubbles=false cancelable=false eventPhase=2]
I am an enterFrame handler [Event type="enterFrame" bubbles=false cancelable=false eventPhase=2]
I am an enterFrame handler [Event type="enterFrame" bubbles=false cancelable=false eventPhase=2]
I am an enterFrame handler [Event type="enterFrame" bubbles=false cancelable=false eventPhase=2]
[SWF] /home/wvxvw/Projects/e4xu/bin-debug/TestZinScript.swf - 36,318 bytes after decompression
I am an enterFrame handler ... eventPhase=2]
I am testMethodWithParameters 43 Hello...
I am testMethod
I am an enterFrame handler ... eventPhase=2]
I am testMethodWithParameters 43 Hello...
I am testMethod
А по экрану будет двигаться синий прямоугольник.
Всего комментариев 6

Комментарии

Старый 16.04.2011 16:29 dixlofos вне форума
dixlofos
 
Аватар для dixlofos
Вы, Батюшка, знаток извращений
Старый 16.04.2011 18:12 expl вне форума
expl
Похожая проблема - после предыдущего проекта в стиле "добавляем фитчу - появляются 2 бага, которые выявляются спустя пару дней - латаем - добавляем 3 бага и т.д."
Т.к. в юнит-тестировании до сих пор много чего не понимаю, а слово "интеграционный тест" повергает в ужос - решил налепить скриптовый движок на клиент (на базе hscript).
Но из-за отсутвия опыта применение нашлось только одно - автоматизация читов. Ну т.е. чтобы каждый итем не добавлять в инвентарь по отдельности - пишем скрипт "добавить 1000 итемов в инвентарь" и т.д. Чтобы в коде клиента не мусорить.

Со скриптом в тестировании непонятны следующие моменты:
- как лучше протащить в парсер доступ ко всем данным из модели?
- как обеспечить парсер возможностью заставить клиент сделать всё что надо?
- как обеспечить парсер отслеживанием всех нужных событий на клиенте?
Всмысле как это сделать, не изуродовав код клиента и при этом не сильно завязавшись на струкруру клиента. Ведь порефакторили клиент - скрипты поотвалились (а ведь они, сцуко, не типизированные - компилятор не ткнет даже где надо поправить)
wvxvw, можете просветить, или ссылку на статью какую-нть дать?

P.S. Надо же, по сравнению с hscript (который еще пришлось заставлять работать в виде либы swc для as3) код парсера так просто выглядит
P.S.2. А, это я на импорты еще не посмотрел - скока там всего используется.
Обновил(-а) expl 16.04.2011 в 18:19
Старый 17.04.2011 02:18 wvxvw вне форума
wvxvw
 
Аватар для wvxvw
Хех, я на самом деле совсем не специалист... и я тоже скорее только начал разбираться с проблемой, чем решил ее как-то. Что до существующих систем, что меня в них не устроило категорически:
- как правило предлагается использовать какой-то совсем уж не полноценный скриптовый язык для написания тестов, явно придуманый "по случаю" разработчиками тествой программы. Возможности такого скрипта как правило сильно ограничены и заточены под конвенциональный ГУИ. Т.е. например, они нормально справляются с кликом по кнопке, а вот когда нужно отследить выбор цвета на градиентной палитре - они вдруг "не знают" как это сделать.
- не универсальны (большинство того, что есть для АС - сделано для флекса, и заточено под АПИ автоматизации флексовые, и, естественно в обычном АС работать не будет).
- иногда предлагаестся тестировать только в браузере, и, хуже всего, либо ИЕ либо ФФ на выбор, и только в Виндовс...
- и очень дорого... даже поделки какие-то стоят астрономические сумы, от $500 и дальше больше... Testing Anywhere легко стоит $7000, например.

Проблему как обеспечить доступ к "внутренним" данным, т.е. приватным / протекдед методам с помощью скриптов - я так и не решил (пока). Для этого нужно, как минимум добавить один метод в каждый класс, который нужно тестировать... На ум приходит условная компиляция / модификация байткода / работа с прототипами объектов... но пока хорошего решения я так и не нашел.
Т.е. задумка была такой:
Код AS3:
Object.prototype.rtti = function():XML
{
	return describeType(this);
}
var pareser:ParensParser = new ParensParser();
trace(pareser["rtti"]());
но обламалась т.как вместо имен приватных методов будет "undefined", то же самое вместо свойсвт... вообще странно работает...
Код AS3:
ParensParser["rtti"] = function(object:Object):XML
{
	return describeType(object);
}
trace(ParensParser["rtti"](pareser));
с таким вариантом - такой же облом...
Но, в идеале, если эту проблему как-то решить, то технически можно будет вызвать любой метод любого объекта.
Что до отслеживания событий - одной очень изначально не поняной штукой было "как написать функцию в рантайме" т.е. как создать слушателя "на лету". В итоге оказалось, что не так уж и сложно. Ну, и по кранйней мере на любые события можно подписаться. Что да тяжело отследить или задать - ввод пользователя, особенно если это текст или клики отлавливать... собственно, я попытался сделать функцию имитатор кликов, но есть вещи которые до конца не возможно сделать, особенно это связано с масками и свойством hitArea - когда они попадают в objectsUnderPoint то нет никакой возможности узнать на кого именно кликнули. Т.е. можно написать функцию, которая будет достаточно достоверно имитировать клик, но будет не вегда достоверно информировать о том, куда кликнули.

Я вот даже скорее ищу где бы больше теории почитать, чтобы не изобретать еще больше велосипедов...
Старый 17.04.2011 03:22 Котяра вне форума
Котяра
 
Аватар для Котяра
мне кажется было бы логичнее использовать хурлантовский движок для написания скриптов (т.е. as3)
Старый 17.04.2011 10:30 wvxvw вне форума
wvxvw
 
Аватар для wvxvw
Да, я думал об этом, и, в принципе, это могло бы быть решением. Но есть еще такой момент как NIH syndrome. Кроме того AS3 не очень подходит для консоли, кроме того, поскольку он скомпилируется во все тот же AS3 байткод, то выполнять его будет сам плеер, а это значит, что нужно будет жить по правилам плеера - например, нельзя переопределить класс или функцию в классе и т.п. Не то чтобы критично, конечно.
Старый 17.04.2011 13:40 expl вне форума
expl
Да уж, попишу, я пожалуй, юнит-тесты на 5% того, что понятно как ими покрыть (не усложнив себе жизнь), а остальное пройдусь ручками со скриптованными читами.
Пока своими глазами не увижу проект с тестами на скриптах или сильно не припрет.
 

 


Часовой пояс GMT +4, время: 00:21.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.