Как передать в макрос параметр времени компиляции
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 16:17
Оценка:
Может быть глупый вопрос, но что-то не нашёл (может быть, не очень искал).

Нужно в вызове макроса передать ему, например, ссылку на функцию. Причём проблема в том, что я должен вызвать эту функцию из макроса в момент компиляции, т.е. в момент работы макроса, а не сгенерированного им кода.


Для чего это может быть нужно?
Например, у меня может быть макрос, который должен инструментировать код в таком ключе:
1. Выделить все условия
2. Вставить в код исполняемой функции автологирование факта захода в ветку
3. Запомнить в некотором файле ветки, в которые всё это вставлено

Вот третий пункт должен быть настраиваемым некими параметрами, в частности, функцией, которая, собственно, всё что нужно залогирует и знает, в какой именно файл.
Re: Как передать в макрос параметр времени компиляции
От: para  
Дата: 22.08.11 16:34
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>Может быть глупый вопрос, но что-то не нашёл (может быть, не очень искал).


FDS>Нужно в вызове макроса передать ему, например, ссылку на функцию. Причём проблема в том, что я должен вызвать эту функцию из макроса в момент компиляции, т.е. в момент работы макроса, а не сгенерированного им кода.



FDS>Для чего это может быть нужно?

FDS>Например, у меня может быть макрос, который должен инструментировать код в таком ключе:
FDS>1. Выделить все условия
FDS>2. Вставить в код исполняемой функции автологирование факта захода в ветку
FDS>3. Запомнить в некотором файле ветки, в которые всё это вставлено

FDS>Вот третий пункт должен быть настраиваемым некими параметрами, в частности, функцией, которая, собственно, всё что нужно залогирует и знает, в какой именно файл.

залогирует на этапе компиляции?
на самом деле не очень понятен вопрос.
чтобы макрос выполнил код, надо чтобы этот код был скомпилирован в сборку до работы макроса. т.е. технически можно сказать макросу чтобы он через рефлекшн загрузил нужный код и такой-то сборки и выполнил. но это не очень правильно сточки зрения сути макросов

или вам надо скопировать (по шаблону ) код одной функции в другие?

я так понимаю вас интересует что-то типа АОП 1 2
Re: Как передать в макрос параметр времени компиляции
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 16:40
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>Может быть глупый вопрос, но что-то не нашёл (может быть, не очень искал).


Он скорее странный.

FDS>Нужно в вызове макроса передать ему, например, ссылку на функцию. Причём проблема в том, что я должен вызвать эту функцию из макроса в момент компиляции, т.е. в момент работы макроса, а не сгенерированного им кода.


Ну, если функция к этому времени уже скомпилирована, то особой проблемы быть не должно.
Не ясно зачем только передавать в качестве параметра функцию. Обычно просто делают некий набор флагов. Эти флаги анализируются в макросе и вызваеются те или иные функции (расположенные в некотором, заранее известном, модуле той же макро-библиотеки).

Но можно конечно и функции вызвать. Для этого в качестве параметра макросу нужно передавать имя функции, а саму функцию вызвать динамически (например, через макрос late).

FDS>Для чего это может быть нужно?

FDS>Например, у меня может быть макрос, который должен инструментировать код в таком ключе:
FDS>1. Выделить все условия

Что значит "выделить"?

FDS>2. Вставить в код исполняемой функции автологирование факта захода в ветку


А может проще использовать макрос log из стандартной библиотеки? Или хотя бы создать свой на его основе?

FDS>3. Запомнить в некотором файле ветки, в которые всё это вставлено


FDS>Вот третий пункт должен быть настраиваемым некими параметрами, в частности, функцией, которая, собственно, всё что нужно залогирует и знает, в какой именно файл.


Не пойму зачем тут функция? Достаточно просто ввести некоторые флаги и уже в соответствии с ними легировать все что нужно, кода нужно.

Посмотри как это сделано в том же макросе log. Там есть макроатрибуты уровня сборки: LogFlag, LogFunction, LogCondition и LogFormat, которые позволяют многое настроить.

ЗЫ

Может быть я не понял идею, но зачем передавать функцию я не понял.

Если речь идет о функции в целевой программе (где и производится логирование), то можно просто передавать ее имя и вызывать ее.

Если речь о передачи кода который нужно динамически скомпилировать, то мне кажется ты на неверном пути. Хотя конечно можно скомпилировать код динамически или вызвать его через late.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Как передать в макрос параметр времени компиляции
От: para  
Дата: 22.08.11 16:53
Оценка:
Здравствуйте, para, Вы писали:

P>я так понимаю вас интересует что-то типа АОП


ещё интереснее aop/src/ftests/Target_positive.n
Re[2]: Как передать в макрос параметр времени компиляции
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 17:06
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Если речь идет о функции в целевой программе (где и производится логирование), то можно просто передавать ее имя и вызывать ее.


Как? Она же становится PExpr. Или имеется в виду вызов через рефлекшн? А параметры тогда как передавать?


Грубо говоря, мне нужно, чтобы произошёл лог в файл "coverageInfo.txt", при этом запись форматировалась бы некоторой функцией format, а сама запись обрабатывалась бы функцией customCoverage, определяющей некий кустомный код, который может быть как уже скомпилирован, так и, в идеале, ещё нет (лямбда прямо при вызове макроса).


Я хочу, чтобы это было указать прямо в вызове макроса someMacro(getCoverageLogFunc("coverageInfo.txt", format), customCoverage)
При этом customCoverage может возвращать, например, кортеж list[someType -> bool] * list[someType2 -> bool * list[someType3 -> someType4]

А может быть ещё интереснее: пусть, есть стандартная функция getStandardCoverage, в которой уже определены первый и третий члены кортежа, а программисту предлагается вызвать уже скомпилированную функцию getStandardCoverage с параметром, в который передать вложенную нескомпилированную лямбду, возвращающую list[someType2 -> bool]

Что-то такое можно сделать?
Re[3]: Как передать в макрос параметр времени компиляции
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 17:28
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>Здравствуйте, VladD2, Вы писали:


VD>>Если речь идет о функции в целевой программе (где и производится логирование), то можно просто передавать ее имя и вызывать ее.


FDS>Как? Она же становится PExpr. Или имеется в виду вызов через рефлекшн? А параметры тогда как передавать?


Ты точно правильно понимаешь значение словосочетания "целевая программа"? Целевая программа — это программа в при компиляции которой раскрывается макрос (генерируется код). Чтобы в ней что-то вызвать нужно всего лишь сгенерировать код вызова. Макрос ниже генерирует код вызова функции имя которой предано в качестве параметра макроса.
macro X(func)
{
  <[ func() ]>
}



FDS>Грубо говоря, мне нужно, чтобы произошёл лог в файл "coverageInfo.txt", при этом запись форматировалась бы некоторой функцией format, а сама запись обрабатывалась бы функцией customCoverage, определяющей некий кустомный код, который может быть как уже скомпилирован, так и, в идеале, ещё нет (лямбда прямо при вызове макроса).


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

FDS>Я хочу, чтобы это было указать прямо в вызове макроса someMacro(getCoverageLogFunc("coverageInfo.txt", format), customCoverage)

FDS>При этом customCoverage может возвращать, например, кортеж list[someType -> bool] * list[someType2 -> bool * list[someType3 -> someType4]

Указывай. Кто тебе мешает?

Погляди как реализован стандартный набор макросов log. В нем есть макроатрибут LogFunction, который позволяет задать имя функции используемой для логирования. Думаю, что именно это тебе и нужно. А всю фигню вроде имени файла можно задать уже в ней. Но можно, конечно, передавать и "пирог с капустой". Это проблемы тебя и твоей фантазии. Формат, кстати, стандартные макросы так же позволяют задать, но через макроатрибут LogFormat.

FDS>А может быть ещё интереснее: пусть, есть стандартная функция getStandardCoverage, в которой уже определены первый и третий члены кортежа, а программисту предлагается вызвать уже скомпилированную функцию getStandardCoverage с параметром, в который передать вложенную нескомпилированную лямбду, возвращающую list[someType2 -> bool]


FDS>Что-то такое можно сделать?


Да, все возможно. Ты просто пока не можешь поднять свое сознание на мета-уровень .

Похоже, что ты просто не верно понимаешь саму суть макросов. Макрос генерирует код. Ему плевать, скомпилированно, что-то или нет. Ему даже плевать есть ли функция вызов которой он пытается сгенерировать. Макрос опирирует выражениями. Они не имеют семантики. Только конечный, сгенерированный код что-то значит.

По сему ты можешь передавать в качестве параметров макросов любой код который с синтаксической точки зрения является корректным кодом немерле. А уже то как макрос будет интерпретировать и использовать эти выражения зависит только от создателя макроса. По сему ты можешь передвать в макрос код вроде "x => x * x", а макрос уже будет интерпретировать его как хочет. Захочет подставит туда где требуется лямбда с одним аргументом, а захочет, разберет его на запчасти, и произведет замену все вхождений x (в некотором, другом выражении) на "x * x", или еще как-то по другому проинтерпретирует.

Главное понять, что до возвращения из макроса ложки нет семантики нет. Ее ты задаешь сам. Скомпилированных функций тоже нет. Но ты можешь генерировать код вызова чего угодно. И все что нужно вызовется, если в конечном итоге в скомпилированной программе это что-то будет присутствовать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Как передать в макрос параметр времени компиляции
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 17:38
Оценка:
Здравствуйте, FDSC, Вы писали:

Мой скайп VladD2. Ломись... поговорим интерактивно. А то понимание как-то не приходит.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Как передать в макрос параметр времени компиляции
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 17:44
Оценка:
Здравствуйте, VladD2, Вы писали:

FDS>>Как? Она же становится PExpr. Или имеется в виду вызов через рефлекшн? А параметры тогда как передавать?


VD>Ты точно правильно понимаешь значение словосочетания "целевая программа"? Целевая программа — это программа в при компиляции которой раскрывается макрос (генерируется код). Чтобы в ней что-то вызвать нужно всего лишь сгенерировать код вызова. Макрос ниже генерирует код вызова функции имя которой предано в качестве параметра макроса.

VD>
VD>macro X(func)
VD>{
VD>  <[ func() ]>
VD>}
VD>



В идеале мне нужно вызвать функцию, описанную в целевой программе, но на этапе компиляции.
Т.е. то же, но без квазицитирования.

FDS>>Грубо говоря, мне нужно, чтобы произошёл лог в файл "coverageInfo.txt", при этом запись форматировалась бы некоторой функцией format, а сама запись обрабатывалась бы функцией customCoverage, определяющей некий кустомный код, который может быть как уже скомпилирован, так и, в идеале, ещё нет (лямбда прямо при вызове макроса).


VD>Ну, логировать ты же хочешь что-то из целевой программы (той где макрос применяется)? Тогда опиши нужную функцию логирования в целевой программе (или одной из библиотек подключенных к ней) и генери (в макросе) код обращения к нужной функции.


Нет, логирование идёт дважды: один раз на этапе компиляции генерируется эталон, а второй раз генерируется реальный обход ветвей при тестах. Грубо говоря, я просто хочу залогировать таким образом покрытие.

FDS>>Я хочу, чтобы это было указать прямо в вызове макроса someMacro(getCoverageLogFunc("coverageInfo.txt", format), customCoverage)

FDS>>При этом customCoverage может возвращать, например, кортеж list[someType -> bool] * list[someType2 -> bool * list[someType3 -> someType4]

VD>Указывай. Кто тебе мешает?


И вызвать это на этапе компиляции

VD>Погляди как реализован стандартный набор макросов log. В нем есть макроатрибут LogFunction, который позволяет задать имя функции используемой для логирования. Думаю, что именно это тебе и нужно. А всю фигню вроде имени файла можно задать уже в ней. Но можно, конечно, передавать и "пирог с капустой". Это проблемы тебя и твоей фантазии. Формат, кстати, стандартные макросы так же позволяют задать, но через макроатрибут LogFormat.


Насколько я понимаю, это всё делается во время исполнения. Там очень много квазицитирования и, вроде бы, оно всё вставляется в исполняемый код, если я правильно понял (условия логирования и print_expr).

FDS>>А может быть ещё интереснее: пусть, есть стандартная функция getStandardCoverage, в которой уже определены первый и третий члены кортежа, а программисту предлагается вызвать уже скомпилированную функцию getStandardCoverage с параметром, в который передать вложенную нескомпилированную лямбду, возвращающую list[someType2 -> bool]


FDS>>Что-то такое можно сделать?


VD>Да, все возможно. Ты просто пока не можешь поднять свое сознание на мета-уровень .


Пока не могу.

VD>Похоже, что ты просто не верно понимаешь саму суть макросов. Макрос генерирует код. Ему плевать, скомпилированно, что-то или нет. Ему даже плевать есть ли функция вызов которой он пытается сгенерировать. Макрос опирирует выражениями. Они не имеют семантики. Только конечный, сгенерированный код что-то значит.


Мне нужно, чтобы макрос не просто генерировал код, но ещё и генерировал на этапе компиляции некий файл-эталон покрытия в зависимости от того, какие параметры ему заданы.
Re[2]: Как передать в макрос параметр времени компиляции
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 17:45
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, FDSC, Вы писали:


VD>Мой скайп VladD2. Ломись... поговорим интерактивно. А то понимание как-то не приходит.


У меня инет не такой хороший, так что не получится. Если только ICQ
Re[5]: Как передать в макрос параметр времени компиляции
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 17:53
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>В идеале мне нужно вызвать функцию, описанную в целевой программе, но на этапе компиляции.

FDS>Т.е. то же, но без квазицитирования.

Зачем, на этапе компиляции то?

Еще раз. Логирование то где ведется?

FDS>>>Грубо говоря, мне нужно, чтобы произошёл лог в файл "coverageInfo.txt", при этом запись форматировалась бы некоторой функцией format, а сама запись обрабатывалась бы функцией customCoverage, определяющей некий кустомный код, который может быть как уже скомпилирован, так и, в идеале, ещё нет (лямбда прямо при вызове макроса).


FDS>Нет, логирование идёт дважды: один раз на этапе компиляции генерируется эталон, а второй раз генерируется реальный обход ветвей при тестах.


Что за эталон?

FDS>Грубо говоря, я просто хочу залогировать таким образом покрытие.


Опять таки, покрытие вызовов в целевой программе?
Если да, то зачем что-то вызывать во время компиляции? Нужно сгенерировать код который это самое покрытие вычислит во время тестирования. (хотя это, вроде как, не плохо делают внешние утилиты).

FDS>>>Я хочу, чтобы это было указать прямо в вызове макроса someMacro(getCoverageLogFunc("coverageInfo.txt", format), customCoverage)

FDS>>>При этом customCoverage может возвращать, например, кортеж list[someType -> bool] * list[someType2 -> bool * list[someType3 -> someType4]

VD>>Указывай. Кто тебе мешает?


FDS>И вызвать это на этапе компиляции


Можно конечно и так извернуться, но это явно неверный дизайн и много не нужного траха.

VD>>Погляди как реализован стандартный набор макросов log. В нем есть макроатрибут LogFunction, который позволяет задать имя функции используемой для логирования. Думаю, что именно это тебе и нужно. А всю фигню вроде имени файла можно задать уже в ней. Но можно, конечно, передавать и "пирог с капустой". Это проблемы тебя и твоей фантазии. Формат, кстати, стандартные макросы так же позволяют задать, но через макроатрибут LogFormat.


FDS>Насколько я понимаю, это всё делается во время исполнения. Там очень много квазицитирования и, вроде бы, оно всё вставляется в исполняемый код, если я правильно понял (условия логирования и print_expr).


Естественно.

FDS>Мне нужно, чтобы макрос не просто генерировал код, но ещё и генерировал на этапе компиляции некий файл-эталон покрытия в зависимости от того, какие параметры ему заданы.


Я все же не могу понять, что ты понимаешь под этим эталоном покрытия. Приведи код который ты хочешь увидеть так если бы ты был вынужден написать его без макросов (за макросы). Ну, и за одно приведи этот самый эталонный файл (опять таки, написанный вручную).

Кроме того опиши задачу что ты пытаешься решить без привязки к методам решения и прочим подразумеваемым условиям. Просто цель и исходные данные.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Как передать в макрос параметр времени компиляции
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 17:54
Оценка:
Здравствуйте, FDSC, Вы писали:

VD>>Мой скайп VladD2. Ломись... поговорим интерактивно. А то понимание как-то не приходит.


FDS>У меня инет не такой хороший, так что не получится. Если только ICQ


Я на даче иной раз через ЖПРС выхожу. И скайп работает нормально. По нему можно и в режиме чата общаться. Скажу больше, в основном в этом режиме и общаемся.

А ICQ я давно не держу. Скайп его (и все остальное) с успехом заменяет.

Как вариант, есть еще гугльтолк.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: Как передать в макрос параметр времени компиляции
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 18:08
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, FDSC, Вы писали:


FDS>>В идеале мне нужно вызвать функцию, описанную в целевой программе, но на этапе компиляции.

FDS>>Т.е. то же, но без квазицитирования.

VD>Зачем, на этапе компиляции то?


VD>Еще раз. Логирование то где ведется?


Логирование ведётся два раза: эталонное на этапе компиляции и реальное на этапе выполнения. Проще говоря, на этапе компиляции макрос генерирует все возможные варианты меток логирования (все возможные трассы), а затем, после выполнения программы в runtime, эти метки могут быть сравнены и указано, что какая-то очерёдность меток не достигнута.
Грубо говоря

для кода
if (условие1)
{
  ...
  when (условие2)
    ...
}
else
  ....;

when (условие3)
  ...


на этапе компиляции макрос сгенерирует следующие трассы (возможные потоки исполнения)
условие1

условие1
условие2

условие1
условие3

условие1
условие2
условие3

не условие1
условие3

Если на этапе выполнения тест не сгенерирует какую-то из этих трасс, то мы сможем увидеть, что какой-то трассы не хватает (покрытия тестами нет).
Т.е. логом, в некотором смысле, это называть некорректно: это информация об эталонном покрытии, которое мы ожидаем встретить в логах (грубо говоря, часть лога).

FDS>>Грубо говоря, я просто хочу залогировать таким образом покрытие.


VD>Опять таки, покрытие вызовов в целевой программе?

Да

VD>Если да, то зачем что-то вызывать во время компиляции? Нужно сгенерировать код который это самое покрытие вычислит во время тестирования. (хотя это, вроде как, не плохо делают внешние утилиты).


Да, но как я узнаю, что нужно покрыть? Кто сгенерирует для меня варианты покрытия, которые я должен покрыть?

FDS>>Мне нужно, чтобы макрос не просто генерировал код, но ещё и генерировал на этапе компиляции некий файл-эталон покрытия в зависимости от того, какие параметры ему заданы.


VD>Я все же не могу понять, что ты понимаешь под этим эталоном покрытия. Приведи код который ты хочешь увидеть так если бы ты был вынужден написать его без макросов (за макросы). Ну, и за одно приведи этот самый эталонный файл (опять таки, написанный вручную).


без макросов нужно делать синтаксический анализатор, который как я указал выше выведет в файл покрытия все возможные или все интересные нам сочетания меток в зависимости от того, какой критерий покрытия указал программист (например, программист может указать, что выражение условие2&&условие3 истинно).

VD>Кроме того опиши задачу что ты пытаешься решить без привязки к методам решения и прочим подразумеваемым условиям. Просто цель и исходные данные.


Хочу на немерле повторить основную идею вот этого http://www.unitesk.ru/ http://ru.wikipedia.org/wiki/%D2%E5%F5%ED%EE%EB%EE%E3%E8%FF_UniTESK
unitesk
Re[7]: Мелкая ошибка
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 18:20
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>(например, программист может указать, что выражение условие2&&условие3 истинно).


условие2||условие3 — истинно. Тогда макрос не должен сгенерировать путь "условие1"
Re: Как передать в макрос параметр времени компиляции
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 18:30
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>Для чего это может быть нужно?

FDS>Например, у меня может быть макрос, который должен инструментировать код в таком ключе:
FDS>1. Выделить все условия
FDS>2. Вставить в код исполняемой функции автологирование факта захода в ветку
FDS>3. Запомнить в некотором файле ветки, в которые всё это вставлено

Попробую заняться телепатией и сам додумать задачу.

Итак у тебя есть желание сделать нечто вроде системы проверки покрытия кода тестами.

Для реализации этого ты хочешь макросам инструментировать код методов целевой программы так чтобы каждая его инструкция (или блоки инструкций гранулированные по некоторому признаку) была обернута в некий код который запоминает куда-то информацию о том, что этот участок кода был вызван. Например, если у нас есть следующая функция в целевой программе:
module X
{
  Foo(arg : int) : void
  {
    if (arg > 0)
      Foo(arg - 1);
    else
     Bar();
  }
}

и ты хочешь чтобы программа была переписана следующим образом:
static _visitedFlags : uint = 0;
static _locations : array[Nemerle.Compiler.Location] = array[
    Location("Foo.n", 5, 5, 5, 17), // местоположение "if (arg > 0)"
    Location("Foo.n", 6, 7, 6, 20), // местоположение "Foo(arg - 1);"
    ...
  ];

module X
{
  Foo(arg : int) : void
  {
    { _visitedFlags |= 0x01; 
    if ({ _visitedFlags |= 0x02; arg > 0})
      { _visitedFlags |= 0x04; Foo(arg - 1); }
    else
      { _visitedFlags |= 0x08; Bar(); }
    }
  }
}

DisplayUnvisitedFor_Foo() : void
{
  WriteLine("Not covered code locations of method 'Foo'");

  foreach (locIndex in BitsToIndexes(_visitedFlags))
    WriteLine(_locations[locIndex]);

  WriteLine();
}


Теперь, чтобы понять есть ли места в Foo не покрытые тестами нужно всего лишь прогнать тесты и вызвать метод DisplayUnvisitedFor_Foo(), который выведет список участков кода которые не были покрыты тестами.

Естественно, что в реальной реализации все должно быть несколько сложнее, так как:
1. В некоторых функциях кода будет так много, что одного поля uint или даже ulong не хватит и придется создавать составные поля.
2. Не имеет смысла выводить список непокрытых подвыражений, если выражение в которое они вложены так же непокрыто. Тут достаточно вывести только самый верхний непокрытый узел. Стало быть кроме флагов непокрытия еще нужно иметь информацию о вложенности веток. Тогда можно будет убрать все лишние ветки оставив только верхние.

Но это уже детали реализации — рутина. Принципиально это уже ничего не меняет.

Так вот, я не вижу зачем в этой задачи нужно что-то вызывать динамически, во время компиляции макроса. И зачем создавать какие-то внешние файлы.

Все что нужно сделать — это добавить (сгенерировать) код который будет вычислять покрытие и выводить о нем информацию. Код этот можно добавлять прямо в исходные типы. Так как он статический, он не будет сильно мешать. Кроме того, так как он нужен только в тестах, то и генерировать его можно на базе некоторых условий (например, когда задан некий символ препроцессора). Так что в релизе код мешаться не будет.

Что касается гибкости вывода информации о покрытии, то этого можно добиться дав возможность задать имя функции используемой для выводи информации о непокрытых частях кода (в моем примере используется WriteLine). Впрочем, тут много вариантов которые ограниченны только твоей фантазией.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: Как передать в макрос параметр времени компиляции
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 18:59
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>Логирование ведётся два раза: эталонное на этапе компиляции и реальное на этапе выполнения. Проще говоря, на этапе компиляции макрос генерирует все возможные варианты меток логирования (все возможные трассы), а затем, после выполнения программы в runtime, эти метки могут быть сравнены и указано, что какая-то очерёдность меток не достигнута.


Я не вижу смысла заниматься каким-то там логированием во время компиляции. Твоя задач собрать информацию о том какие из участков кода не были посещены? Ну, так и решай эту задачу, а не какую-то другую.

Здесь
Автор: VladD2
Дата: 22.08.11
я описал вариант решения твой задачи. Он прост, эффективен и не требует какой-либо алхимии.

FDS>Если на этапе выполнения тест не сгенерирует какую-то из этих трасс, то мы сможем увидеть, что какой-то трассы не хватает (покрытия тестами нет).


А далее ты хочешь сравнивать текст логов? А как потом определять чему в коде соответствует несовпадение лога?

FDS>Т.е. логом, в некотором смысле, это называть некорректно: это информация об эталонном покрытии, которое мы ожидаем встретить в логах (грубо говоря, часть лога).


Вот именно! Логом — это назвать нельзя. Это информация о покрытии. Ну так на фиг ее куда-то там пихать, и потом еще к тому же с чем-то сравнивать. Решай основную задачу — собирай информацию о покрытии.

Логи же могут быть тупо разными в зависимости от параметров. И как ты при этом будешь их сравнивать со своим эталоном?

Твоя главная проблема сделать сбор и накопление информации о покрытии максимально эффективным (с точки зрения памяти и процессорного времени), чтобы твои тесты не начали жутко тормозить. Это наталкивает на мысль, что формат хранения информации о покрытии должен быть компактным и не требовать динамического заема памяти.

Что же нам нужно запоминать? А всего лишь информацию о том, что некоторый блок был посещен). Блоки можно тупо пронумеровать в пределах метода, класса или даже всей программы. Лучше ограничиться методом, так как иначе объем структуры для хранения информации может быть слишком велик и начнутся промахи мимо кэша.

Тут подойдут два варианта:
1. Использование статического массива.
2. Использование битовых полей.

Битовые поля эффективнее, с массивами несколько проще работать.

FDS>Да, но как я узнаю, что нужно покрыть? Кто сгенерирует для меня варианты покрытия, которые я должен покрыть?


Ты же сам сказал "инструментировать код". Ну, дык и делай это!

Просто пробеги по коду метода и оберни все блоки приводящие к ветвлению в некий код собирающий оинформацию о их посещении. Каждый такой блок будет иметь порядковый номер (в рамках метода). Далее останется только прогнать тесты, и вывести список блоков не посещенных в них.

Информацию о блоках можно запоминать в виде структуры хранящей местоположение блока. У компиляторе немерла уже есть такая структура. Она называется Location и находится в пространстве имен Nemerle.Compiler. Можно или сделать ее аналог и поместить его в целевое приложение, или подключить к проекту Nemerle.Compiler.dll и использовать Nemerle.Compiler.Location.

Далее остается только отобразить порядковый номер на местоположение блока. Это легко сделать, если во время инструментирования генерировать Location-ов. Тода индекс в этом массиве будет соответствовать номеру блока в функции.

Ну, а сделать функцию которая выводит список Location-ов для не посещенных блоков — это уже совсем ерунда.

FDS>без макросов нужно делать синтаксический анализатор, который как я указал выше выведет в файл покрытия все возможные или все интересные нам сочетания меток в зависимости от того, какой критерий покрытия указал программист (например, программист может указать, что выражение условие2&&условие3 истинно).


У тебя исходно не верная стратегия. Ты зачем-то прилепляешь к задаче логирование. Хотя логирование здесь вообще не причем.

А от тебя я просил чего-то вроде примеров что я привел здесь
Автор: VladD2
Дата: 22.08.11
.

Вообще, разработку любого не тривиального макроса лучше начинать с написания частного случая вручную. Его сразу можно будет попробовать. Это убережет от неверного подхода.


VD>>Кроме того опиши задачу что ты пытаешься решить без привязки к методам решения и прочим подразумеваемым условиям. Просто цель и исходные данные.


FDS>Хочу на немерле повторить основную идею вот этого http://www.unitesk.ru/ http://ru.wikipedia.org/wiki/%D2%E5%F5%ED%EE%EB%EE%E3%E8%FF_UniTESK


Ну, это что-то очень абстрактное. Что ни понимаю под формальной моделью я вообще не понимаю. В общем, случае это какая-то фигня. Модель можно придумать только для конкретной задачи и далеко не для всех случаев.

В общем, это опять не то. Ты же собрался решать конкретную задачу? Вот ее и надо было описать. Покрытие, так покрытие.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Как передать в макрос параметр времени компиляции
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 19:05
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Итак у тебя есть желание сделать нечто вроде системы проверки покрытия кода тестами.

верно

VD>Для реализации этого ты хочешь макросам инструментировать код методов целевой программы так чтобы каждая его инструкция (или блоки инструкций гранулированные по некоторому признаку) была обернута в некий код который запоминает куда-то информацию о том, что этот участок кода был вызван.

Верно

VD>Например, если у нас есть следующая функция в целевой программе:

VD>
VD>module X
VD>{
VD>  Foo(arg : int) : void
VD>  {
VD>    if (arg > 0)
VD>      Foo(arg - 1);
VD>    else
VD>     Bar();
VD>  }
VD>}
VD>

VD>и ты хочешь чтобы программа была переписана следующим образом:
VD>
VD>static _visitedFlags : uint = 0;
VD>static _locations : array[Nemerle.Compiler.Location] = array[
VD>    Location("Foo.n", 5, 5, 5, 17), // местоположение "if (arg > 0)"
VD>    Location("Foo.n", 6, 7, 6, 20), // местоположение "Foo(arg - 1);"
VD>    ...
VD>  ];

VD>module X
VD>{
VD>  Foo(arg : int) : void
VD>  {
VD>    { _visitedFlags |= 0x01; 
VD>    if ({ _visitedFlags |= 0x02; arg > 0})
VD>      { _visitedFlags |= 0x04; Foo(arg - 1); }
VD>    else
VD>      { _visitedFlags |= 0x08; Bar(); }
VD>    }
VD>  }
VD>}

VD>DisplayUnvisitedFor_Foo() : void
VD>{
VD>  WriteLine("Not covered code locations of method 'Foo'");

VD>  foreach (locIndex in BitsToIndexes(_visitedFlags))
VD>    WriteLine(_locations[locIndex]);

VD>  WriteLine();
VD>}
VD>


VD>Теперь, чтобы понять есть ли места в Foo не покрытые тестами нужно всего лишь прогнать тесты и вызвать метод DisplayUnvisitedFor_Foo(), который выведет список участков кода которые не были покрыты тестами.


Не совсем верно, хотя близко.
Если наш тест упал, то мне хотелось бы иметь информацию, в каком именно условии он упал, а для этого мне нужно логировать трассу прямо при входе в условие (если упадёт — уже нечему будет логировать потом).
DisplayUnvisitedFor — функция, очевидно, внешняя, должна анализировать лог. Сам лог сложнее, т.к. покрытие мы используем не по операторам, а по возможным путям потока управления.

Очевидно, что для того, чтобы DisplayUnvisitedFor сработала, она должна иметь список всех возможных путей потока управления, чтобы проанализировать, все ли они были достигнуты ( об этом я писал в http://rsdn.ru/forum/nemerle/4389580.1.aspx
Автор: FDSC
Дата: 22.08.11
, видимо, ты не успел прочитать ).

VD>Так вот, я не вижу зачем в этой задачи нужно что-то вызывать динамически, во время компиляции макроса. И зачем создавать какие-то внешние файлы.

VD>Все что нужно сделать — это добавить (сгенерировать) код который будет вычислять покрытие и выводить о нем информацию.

1. Файл нужен, чтобы функция DisplayUnvisitedFor могла провести вычисления, т.к. у нас есть более сложный набор комбинаций веток и функции нужно знать, что должно было быть покрыто (некоторые условия могут быть взаимосвязаны).
2. Функции-параметры нужны чтобы
1) Определить имя файла-эталона и, если нужно, его формат (если кто-то захочет его ещё и прочитать, а не только вывести для парсинга)
2) Для индивидуализации критерия покрытия. Я могу передавать соотв. PExpr из макроса в эту функцию, а она сама будет определять, что именно логировать и что именно требовать для покрытия. Грубо говоря, исходный макрос может ничего не знать о переменных типа int и обработать выражение x > 10 как одно условие. Функция индивидуализации преобразует его, скажем, в условия обхода для любого x в виде: <0, 0, >0, >10; т.е. добавит ещё условия проверки при нуле, и учёт знаков, чтобы в функцию были переданы отрицательные значения (возможно, программист забыл, что числа могут быть меньше нуля, а не только меньше десяти, но больше нуля)
3) Для указания способа вспомогательного логирования величин. Например, у меня есть некоторый объект someType, как его залогировать в лог, чтобы программист в случае, если код покрыт, но работает неверно, смог понять, собственно, что было передано в метод?

VD>2. Код этот можно добавлять прямо в исходные типы. Так как он статический, он не будет сильно мешать.


VD>Что касается гибкости вывода информации о покрытии, то этого можно добиться дав возможность задать имя функции используемой для выводи информации о непокрытых частях кода (в моем примере используется WriteLine).


И что с этим именем дальше делать при получении эталона?
У тебя эталон прост: 0001111
А в реале эталон — это перебор флагов: в это вошли, в это нет, в это не вошли, в это и не должны были и т.п.
Re[7]: Как передать в макрос параметр времени компиляции
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 19:07
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>на этапе компиляции макрос сгенерирует следующие трассы (возможные потоки исполнения)

FDS>Если на этапе выполнения тест не сгенерирует какую-то из этих трасс, то мы сможем увидеть, что какой-то трассы не хватает (покрытия тестами нет).

Невнимательно прочитал в прошлый раз...

Сейчас перечитал и понял, что это какая-то ерунда. Зачем собирать информацию о этих трассах, если можно просто собирать информацию о том какие из блоков кода не были посещены?

Просто, если тесты покроют 100% кода метода, то все эти трассы однозначно будут покрыты. Если же часть кода не будет покрыта, то и часть этих трасс не будет.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[8]: Отступление в Unitesk
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 19:18
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Я не вижу смысла заниматься каким-то там логированием во время компиляции. Твоя задач собрать информацию о том какие из участков кода не были посещены? Ну, так и решай эту задачу, а не какую-то другую.


VD>Здесь
Автор: VladD2
Дата: 22.08.11
я описал вариант решения твой задачи. Он прост, эффективен и не требует какой-либо алхимии.


Ответил там (есть разные критерии покрытия — твой не годится).

VD>А от тебя я просил чего-то вроде примеров что я привел здесь
Автор: VladD2
Дата: 22.08.11
.


VD>Ну, это что-то очень абстрактное. Что ни понимаю под формальной моделью я вообще не понимаю. В общем, случае это какая-то фигня. Модель можно придумать только для конкретной задачи и далеко не для всех случаев.


VD>В общем, это опять не то. Ты же собрался решать конкретную задачу? Вот ее и надо было описать. Покрытие, так покрытие.


У них это решается в конкретном виде, извини, не посмотрел внимательно по ссылкам.

Дело в том, что мы берём код нашего метода, и, грубо говоря, по нему делаем формальную модель просто исключая всё, кроме условий:

if (условие1)
{
  ...
  when (условие2)
    ...
}
else
  ....;

// это спец. ключевое слово, описывающее нечто в коде модели, что не может быть из неё извлечено
tautology условие2 ^ условие3;

when (условие3)
  ...


Очень грубо, формальная модель в Unitesk — это код, который описывает такие условия, а затем инструмент тестирования говорит мне, какие участки кода мне нужно покрыть с учётом указанной тавтологии и комбинаторного покрытия кода.

Т.е. он мне скажет, что я должен пройти тестами по следующим трассам

условие1

условие1
условие2

условие1
условие3

условие1
условие2
условие3

не условие1
условие3

не условие1

Но, с учётом тавтологии, если я зашёл в условие2, то условия условие3 уже быть не может и наоборот, но при этом одно из этих условий должно быть, поэтому он мне скажет, что я должен покрыть следующие трассы

условие1

условие1
условие2

условие1
условие3

условие1
условие2
условие3

не условие1
условие3

не условие1

Вот это он должен описать в некотором файле, чтобы затем я мог по логам покрытия тестами, сравнить, покрыты все эти условия или нет.
Re[9]: Исправление ошибки
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 19:22
Оценка:
FDS>не условие1
Это тоже должно быть покрыто
Re[8]: Как передать в макрос параметр времени компиляции
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 19:23
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Просто, если тесты покроют 100% кода метода, то все эти трассы однозначно будут покрыты. Если же часть кода не будет покрыта, то и часть этих трасс не будет.


Это неверно. Если тесты покроют 100% кода метода, то это значит, что будут покрыты по крайней мере по одной трассе, содержащей соотв. условия. Но вот все или не все — неизвестно.

Пример
if (условие1)
{
  ...
  when (условие2)
    ...
}
else
  ....;

// это спец. ключевое слово, описывающее нечто в коде модели, что не может быть из неё извлечено
tautology условие2 ^ условие3;

when (условие3)
  ...


Трассы к покрытию:
условие1
условие2

условие1
условие3

не условие1
условие3

не условие1

Допустим, у нас в одном тесте верны условия условие1 и условие2, а в другом — не условие1 и условие3

Получаем, что в первом тесте мы покрыли код условия1 и условия2, во втором код не условия1 и условия3, т.е. весь код. В то же время мы не покрыли следующие трассы
условие1
условие3

не условие1
Re[3]: Как передать в макрос параметр времени компиляции
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 20:02
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>Не совсем верно, хотя близко.

FDS>Если наш тест упал, то мне хотелось бы иметь информацию, в каком именно условии он упал, а для этого мне нужно логировать трассу прямо при входе в условие (если упадёт — уже нечему будет логировать потом).

Сдается мне, что ты пытаешся совместить две разных задачи (вычисление покрытия тестами и логирование) в одну. Не уверен, что это хорошая идея. В прочем, тебе решать.

FDS>DisplayUnvisitedFor — функция, очевидно, внешняя, должна анализировать лог.


В моем (упрощенном) случае она внутренняя и анализировать ей особо нечего.

FDS>Сам лог сложнее, т.к. покрытие мы используем не по операторам, а по возможным путям потока управления.


То есть, ты хочешь сначала проанализировать поток исполнения программы, записать его в некоторой форме, а потом записать реальные пути выполнения и сравнить их с возможными? Так?

В любом случае идея писать в логи какой-то текст для этого мне кажется не верной. Это приведет к серьезному замедлению тестов.

По сути ведь тебе не нужно хранить дублирующиеся пути. Тебе досточно информации о том, что путь был пройден хотя бы один раз. Так?

FDS>Очевидно, что для того, чтобы DisplayUnvisitedFor сработала, она должна иметь список всех возможных путей потока управления, чтобы проанализировать, все ли они были достигнуты ( об этом я писал в http://rsdn.ru/forum/nemerle/4389580.1.aspx
Автор: FDSC
Дата: 22.08.11
, видимо, ты не успел прочитать ).


Ну, в принципе достаточно иметь эталонное дерево ветвления, а не набор отдельных путей.
Тогда задача сводится к раскраске дерева в цвета (пройдено/не пройдено), а задача выявления не посещенного пути в задачу вывода пути к не раскрашенным подветкам.

Такое дерево будет весьма компактным и его смело можно разместить в памяти. Это конечно не битовая карта, но тоже компактная структура (не больше чем веток АСТ в коде).

FDS>1. Файл нужен, чтобы функция DisplayUnvisitedFor могла провести вычисления, т.к. у нас есть более сложный набор комбинаций веток и функции нужно знать, что должно было быть покрыто (некоторые условия могут быть взаимосвязаны).


Пока не вижу нужды в файле. В прочем, создать файл из макроса не проблема. Получаешь путь к файлу (из свойства method.Body.Locatio.File) в котором лежит код, убираешь расширения, добавляешь имя типа и метода и некоторое другое расширение. Далее пиши в него что хочешь.

FDS>2. Функции-параметры нужны чтобы

FDS> 1) Определить имя файла-эталона и, если нужно, его формат (если кто-то захочет его ещё и прочитать, а не только вывести для парсинга)

Явный овердизайн. В прочем, это следствие смешивания логирования со средствами вычисления покрытия/путей.

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

FDS> 2) Для индивидуализации критерия покрытия. Я могу передавать соотв. PExpr из макроса в эту функцию, а она сама будет определять, что именно логировать и что именно требовать для покрытия.


Какие-то плагины к еще не написанному макросу. Явный овердизайн.


FDS>Грубо говоря, исходный макрос может ничего не знать о переменных типа int и обработать выражение x > 10 как одно условие. Функция индивидуализации преобразует его, скажем, в условия обхода для любого x в виде: <0, 0, >0, >10; т.е. добавит ещё условия проверки при нуле, и учёт знаков, чтобы в функцию были переданы отрицательные значения (возможно, программист забыл, что числа могут быть меньше нуля, а не только меньше десяти, но больше нуля)


Ну, забыл и забыл. Ты не сможешь формально вычислить что забыл программист. Забыл, значит тест на некоторых данных упадет и ты увидишь свое непокрытие.

Потом, опят же у тебя какие-то абстрактные рассуждения. Приведи пример того что ты имеешь в виду.
Ну, там вот тебе вход "тут исходный код", вот параметры макросу "...", а вот конечный код.

FDS> 3) Для указания способа вспомогательного логирования величин. Например, у меня есть некоторый объект someType, как его залогировать в лог, чтобы программист в случае, если код покрыт, но работает неверно, смог понять, собственно, что было передано в метод?


На мой взгляд, прежде всего, не стоит мешать эти две задач. Логирование, логированием, а покрытие покрытием.

Если программист поймет, что его код работает не верно, то у него по идее должен упасть тест. Если это не так, то программист должен написать тест который будет падать. По крайней мере, когда я берусь устранять баг, то вначале я его воспроизвожу (и добиваюсь четкого постоянного воспроизведения). Далее, я выбираю средство с помощью которого я смогу понять что приводит к багу. В первую очередь я использую отладчик! А вот к логированию и чтению логов я прибегаю только в самом крайнем случае. Кроме того. Я не буду логировать все подряд. Я вставлю логирование только туда куда надо (и возможно поменяю это место раз 10). Так что подобная помощью от системы выявляющей непокрытие я расценю как вредительство!

VD>>2. Код этот можно добавлять прямо в исходные типы. Так как он статический, он не будет сильно мешать.


VD>>Что касается гибкости вывода информации о покрытии, то этого можно добиться дав возможность задать имя функции используемой для выводи информации о непокрытых частях кода (в моем примере используется WriteLine).


FDS>И что с этим именем дальше делать при получении эталона?


ОК, я понял, что ты хочешь выявлять пути. Тут идея с флагами конечно непригодна. Но идя логирования и последующего анализа логов просто не взлетит на мало-мальски объемной системе.

FDS>У тебя эталон прост: 0001111

FDS>А в реале эталон — это перебор флагов: в это вошли, в это нет, в это не вошли, в это и не должны были и т.п.

В общем, то что ты хочешь конечно сделать можно. Но лично я вижу тут дичайшй овердизайн (т.е. попытку спроектировать излишне универсалную систему).

То что ты хочешь в плане передачи функции через параметр — это так же не очень хорошая идея, хотя и несомненно реализуемая, так как никто не мешает делать макросы расширяемыми. Это приведет к замедлению конечной системы, так как код перед выполнением все равно придется скомпилировать или интерпретировать. В первом случае время будет уходить за компиляцию, во втором вычисления замедлятся в несколько раз (обычно около 10). Кончено, можно кэширвоать резуьтаты компиляции, но это еще более усложнит код. Ну, и собственно, на компиляцию или интепретацию придется потратить много сил.

Из более менее разумных вариантов динамической загрузки кода можно было бы порекомендовать следующее. В решение поместить отдельный проект в котором поместить эти пользовательские функции (в виде статических методов модулей). Далее, подключить этот проект к проекту где планируется использовать макросы (целевому проекту). Макросам в качестве параметров передавать полное имя этих функций. Ну, а из макроса вызывать их динамически, через загрузку сборки и рефлексию. Тут можно использовать макрос late для упрощения динамического вызова.

Однако, еще раз повторюсь, что все это пахнет овердизайном.

На твоем месте я бы:
1. Не смешивал функции подсистемы логирования и подсистемы выявления не пройденных путей.
2. Не вводил бы возможность подстановки пользовательских функций, а просто сделал бы гибкую систему настроек.
3. Вообще отказался бы от использования файлов для выявления покрытия, а остановился бы на статическом дереве путей для каждого метода и инструментировании методов кодом раскраски этих деревьев.

Сам по себе анализ потока управления не сама простая задача. Лучше пока что сосредоточиться на ней. Кстати, я бы порекомендовал делать такой анализ не на базе PExpr, а на базе TExpr, так как в коде могут оказаться пользовательские макросы которые приведут к созданию новых путей вычисления, которые твой макрос не сможет покрыть. Вот здесь я приводил пример макроса производящего анализ TExpr. Это будет не плохой отправной точкой.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: Отступление в Unitesk
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 20:07
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>Вот это он должен описать в некотором файле, чтобы затем я мог по логам покрытия тестами, сравнить, покрыты все эти условия или нет.


ОК, пиши в файл, если тебе оно надо.

Но зачем тут вообще файл? Тут само собой напрашивается дерево.

И не ясно зачем как-то хитро задавать имя файла с помощью динамически вычисляемого кода.
Тут надо или формировать имя файлов автоматически (по единому алгоритму), или позволить пользователю задавать его для отдельных методов. Ну, или и то, и другое сразу.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: Как передать в макрос параметр времени компиляции
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 20:13
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>Это неверно. Если тесты покроют 100% кода метода, то это значит, что будут покрыты по крайней мере по одной трассе, содержащей соотв. условия. Но вот все или не все — неизвестно.


FDS>...Получаем, что в первом тесте мы покрыли код условия1 и условия2, во втором код не условия1 и условия3, т.е. весь код. В то же время мы не покрыли следующие трассы

FDS>условие1
FDS>условие3
FDS>не условие1

ОК, убедил... хот смахивает на паранойю.

Тогда остается один вопрос. Зачем для этого файлы? Почему бы не описать возможны пути в одном дереве и не поместить это дерево в статическую переменную? Тогда бы твоя задача свелась к задаче раскраски этого дерева и последующего вычисления непройденных в нем путей. Другими словами к стандартным для работы с графами/деревьям алгоритмам. Что не мало важно, к быстрым алгоритмам!
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Как передать в макрос параметр времени компиляции
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 20:42
Оценка:
Здравствуйте, VladD2, Вы писали:

FDS>>Сам лог сложнее, т.к. покрытие мы используем не по операторам, а по возможным путям потока управления.


VD>То есть, ты хочешь сначала проанализировать поток исполнения программы, записать его в некоторой форме, а потом записать реальные пути выполнения и сравнить их с возможными? Так?


Конечно.

VD>В любом случае идея писать в логи какой-то текст для этого мне кажется не верной. Это приведет к серьезному замедлению тестов.


Это не страшно. Поверь, всё это уже давно и успешно работает, я лишь пытаюсь перенести общую концепцию UniTesk на Nemerle с помощью макросов.

VD>По сути ведь тебе не нужно хранить дублирующиеся пути. Тебе досточно информации о том, что путь был пройден хотя бы один раз. Так?


Вообще говоря, в UniTesk в отчётах выводятся количество покрытий и лог переменных (лог делает сам пользователь) и иногда это бывает крайне полезно.

FDS>>Очевидно, что для того, чтобы DisplayUnvisitedFor сработала, она должна иметь список всех возможных путей потока управления, чтобы проанализировать, все ли они были достигнуты ( об этом я писал в http://rsdn.ru/forum/nemerle/4389580.1.aspx
Автор: FDSC
Дата: 22.08.11
, видимо, ты не успел прочитать ).


VD>Ну, в принципе достаточно иметь эталонное дерево ветвления, а не набор отдельных путей.


Лучше отдельные пути, т.к. тогда гораздо легче настраивать, что мы хотим покрыть.

VD>Тогда задача сводится к раскраске дерева в цвета (пройдено/не пройдено), а задача выявления не посещенного пути в задачу вывода пути к не раскрашенным подветкам.


Нет. Ещё раз повторюсь, у нас задача организовать комбинаторное покрытие условий, а не покрытие операторов.

VD>Такое дерево будет весьма компактным и его смело можно разместить в памяти. Это конечно не битовая карта, но тоже компактная структура (не больше чем веток АСТ в коде).


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

FDS>>1. Файл нужен, чтобы функция DisplayUnvisitedFor могла провести вычисления, т.к. у нас есть более сложный набор комбинаций веток и функции нужно знать, что должно было быть покрыто (некоторые условия могут быть взаимосвязаны).


VD>Пока не вижу нужды в файле. В прочем, создать файл из макроса не проблема. Получаешь путь к файлу (из свойства method.Body.Locatio.File) в котором лежит код, убираешь расширения, добавляешь имя типа и метода и некоторое другое расширение. Далее пиши в него что хочешь.


Неудобно. Потом его фиг найдёшь, ведь он будет писаться чёрт-знает куда (в рабочую директорию, которая может быть вообще закрыта для записи)

FDS>>2. Функции-параметры нужны чтобы

FDS>> 1) Определить имя файла-эталона и, если нужно, его формат (если кто-то захочет его ещё и прочитать, а не только вывести для парсинга)

VD>Явный овердизайн. В прочем, это следствие смешивания логирования со средствами вычисления покрытия/путей.


Нет, потому что я по логу вычисляю затем, покрыл я или нет что-либо. Но, в то же время, лог используется не только для этого.
А чтобы покрытие вычислить, да, нужен файл. Но и то, и другое здесь невозможно исключить.

VD>Имя файла отлично может вычисляться из имени файла в котором находится код и других имен (см.выше).


Неподходит.

VD>В прочем, можешь, если это надо, задавать это имя навешивая куда надо макро-атрибут. В его параметре и задашь нужную информацию.


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

FDS>> 2) Для индивидуализации критерия покрытия. Я могу передавать соотв. PExpr из макроса в эту функцию, а она сама будет определять, что именно логировать и что именно требовать для покрытия.


VD>Какие-то плагины к еще не написанному макросу. Явный овердизайн.


Зачем мне писать макрос, если я не знаю, сможет ли он потом работать так, как я хочу? Сначала мне нужно убедиться, что его возможно написать.

FDS>>Грубо говоря, исходный макрос может ничего не знать о переменных типа int и обработать выражение x > 10 как одно условие. Функция индивидуализации преобразует его, скажем, в условия обхода для любого x в виде: <0, 0, >0, >10; т.е. добавит ещё условия проверки при нуле, и учёт знаков, чтобы в функцию были переданы отрицательные значения (возможно, программист забыл, что числа могут быть меньше нуля, а не только меньше десяти, но больше нуля)


VD>Ну, забыл и забыл. Ты не сможешь формально вычислить что забыл программист. Забыл, значит тест на некоторых данных упадет и ты увидишь свое непокрытие.


А кто тебе сказал, что "тест на некоторых данных упадёт"? Мне и нужно убедиться, что тест подал не только положительные значения, но и нуль, и отрицательные, что модуль не упал не потому, что его не протестировали, а потому, что правильно работает. Но прямо в исходный макрос это всё писать — убийство малолетних.

VD>Потом, опят же у тебя какие-то абстрактные рассуждения. Приведи пример того что ты имеешь в виду.

VD>Ну, там вот тебе вход "тут исходный код", вот параметры макросу "...", а вот конечный код.

[testCoverageMacro("имя:\файла", someFunc)]
 // someFunc - некая заранее написанная функция, которая вычисляет критерий покрытия
testFunc()
{
if (условие1)
{
  ...
  when (условие2)
    ...
}
else
  ....;

// это спец. ключевое слово, описывающее нечто в коде модели, что не может быть из неё извлечено
tautology условие2 ^ условие3;

when (условие3)
  ...
}


VD>На мой взгляд, прежде всего, не стоит мешать эти две задач. Логирование, логированием, а покрытие покрытием.


Лог используется для анализа покрытия. Всё равно его нужно делать.

VD>А вот к логированию и чтению логов я прибегаю только в самом крайнем случае. Кроме того. Я не буду логировать все подряд. Я вставлю логирование только туда куда надо (и возможно поменяю это место раз 10). Так что подобная помощью от системы выявляющей непокрытие я расценю как вредительство!


Значит эта система не для тебя.
В некоторых случаях отладчик использовать невозможно вообще.


VD>ОК, я понял, что ты хочешь выявлять пути. Тут идея с флагами конечно непригодна. Но идя логирования и последующего анализа логов просто не взлетит на мало-мальски объемной системе.


Влад, я ничего нового не придумал. Ты просто не видел как это работает.

VD>В общем, то что ты хочешь конечно сделать можно. Но лично я вижу тут дичайшй овердизайн (т.е. попытку спроектировать излишне универсалную систему).


На самом деле то, что я хочу повторить чувствительно сложнее.

VD>То что ты хочешь в плане передачи функции через параметр — это так же не очень хорошая идея, хотя и несомненно реализуемая, так как никто не мешает делать макросы расширяемыми. Это приведет к замедлению конечной системы, так как код перед выполнением все равно придется скомпилировать или интерпретировать. В первом случае время будет уходить за компиляцию, во втором вычисления замедлятся в несколько раз (обычно около 10). Кончено, можно кэширвоать резуьтаты компиляции, но это еще более усложнит код. Ну, и собственно, на компиляцию или интепретацию придется потратить много сил.


Ничего не понял.

VD>Ну, а из макроса вызывать их динамически, через загрузку сборки и рефлексию. Тут можно использовать макрос late для упрощения динамического вызова.


А параметры я как через рефлексию передам? Очень неудобно для пользователя получается, хотя, в принципе, это реализуемо.

VD>На твоем месте я бы:

VD>1. Не смешивал функции подсистемы логирования и подсистемы выявления не пройденных путей.
Невозможно
VD>2. Не вводил бы возможность подстановки пользовательских функций, а просто сделал бы гибкую систему настроек.
Невозможно
VD>3. Вообще отказался бы от использования файлов для выявления покрытия, а остановился бы на статическом дереве путей для каждого метода и инструментировании методов кодом раскраски этих деревьев.
Невозможно
Re[10]: Отступление в Unitesk
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 21:00
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, FDSC, Вы писали:


FDS>>Вот это он должен описать в некотором файле, чтобы затем я мог по логам покрытия тестами, сравнить, покрыты все эти условия или нет.


VD>ОК, пиши в файл, если тебе оно надо.


VD>Но зачем тут вообще файл? Тут само собой напрашивается дерево.


Откуда я возьму это дерево для анализа покрытия? Получается, мне его нужно как-то в программу запихать, грубо говоря, статически объявить — а это лишняя работа.

VD>И не ясно зачем как-то хитро задавать имя файла с помощью динамически вычисляемого кода.

VD>Тут надо или формировать имя файлов автоматически (по единому алгоритму), или позволить пользователю задавать его для отдельных методов. Ну, или и то, и другое сразу.

Как автоматически я вычислю нужную для пользователя директорию?
Re[11]: Отступление в Unitesk
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 21:18
Оценка:
Здравствуйте, FDSC, Вы писали:

VD>>Но зачем тут вообще файл? Тут само собой напрашивается дерево.


FDS>Откуда я возьму это дерево для анализа покрытия?


Вычислишь во время анализа потока управления (в макросе).

FDS>Получается, мне его нужно как-то в программу запихать, грубо говоря, статически объявить — а это лишняя работа.


Не понял, ты в любом случае это сделаешь. Разница только в том, запихаешь ли ты это в файл и потом будешь с ним умчаться, или поместишь прямо рядом с функцией в виде структуры данных, которую легко будет обрабатывать коду сгенерированному макросом.

Работы здесь будет даже меньше. У тебя это дерево будет нужно внутри инструментированного тела метода. Плюс его же можно использовать для формирования списка непройденных путей и путей что нужно покрыть.

VD>>И не ясно зачем как-то хитро задавать имя файла с помощью динамически вычисляемого кода.

VD>>Тут надо или формировать имя файлов автоматически (по единому алгоритму), или позволить пользователю задавать его для отдельных методов. Ну, или и то, и другое сразу.

FDS>Как автоматически я вычислю нужную для пользователя директорию?


Вариант 1: Класть файлы рядом с файлом исходника.
Вариант 2: Класть файлы рядом с файлом исходника, но в подкаталог с именем исходника.
Вариант 3: Завести в папке проекта (путь к которой можно узнать через Manaret) папку в которую складывать все эти файлы, повторяя при этом иерархию исходников.
Вариант 4: Придумать что-то еще.

Хотя еще раз повторюсь, что на мой взгляд файл тут просто не нужен.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[12]: Отступление в Unitesk
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 21:36
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, FDSC, Вы писали:


VD>>>Но зачем тут вообще файл? Тут само собой напрашивается дерево.


FDS>>Откуда я возьму это дерево для анализа покрытия?


VD>Вычислишь во время анализа потока управления (в макросе).


А дальше? После компиляции оно пропадёт. А мне нужно-то оно будет после запуска тестов уже.

FDS>>Получается, мне его нужно как-то в программу запихать, грубо говоря, статически объявить — а это лишняя работа.


VD>Не понял, ты в любом случае это сделаешь. Разница только в том, запихаешь ли ты это в файл и потом будешь с ним умчаться, или поместишь прямо рядом с функцией в виде структуры данных, которую легко будет обрабатывать коду сгенерированному макросом.


Наоборот, мне всё равно придётся эту структуру в файл запихивать: отчёт по тестированию делать придётся. Да и файл тогда сможет использовать любая другая программа анализа, в т.ч. специально написанная пользователем.

VD>Работы здесь будет даже меньше. У тебя это дерево будет нужно внутри инструментированного тела метода.

Как раз там мне это дерево совершенно ни к чему

FDS>>Как автоматически я вычислю нужную для пользователя директорию?


VD>Вариант 1: Класть файлы рядом с файлом исходника.

Неподходит.

VD>Вариант 2: Класть файлы рядом с файлом исходника, но в подкаталог с именем исходника.

Неподходит

VD>Вариант 3: Завести в папке проекта (путь к которой можно узнать через Manaret) папку в которую складывать все эти файлы, повторяя при этом иерархию исходников.

Неподходит Директория должна быть произвольной, к проекту не относящейся. Например, когда я работал в ИСП РАН у нас она была под отдельной системой контроля версий и даже хранилась на другом сервере.

VD>Вариант 4: Придумать что-то еще.

Ну вот я и придумал.

VD>Хотя еще раз повторюсь, что на мой взгляд файл тут просто не нужен.


Опять же, кроме файла есть ещё другие пользовательские функции.
Re[5]: Как передать в макрос параметр времени компиляции
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 22:09
Оценка:
Здравствуйте, FDSC, Вы писали:

VD>>В любом случае идея писать в логи какой-то текст для этого мне кажется не верной. Это приведет к серьезному замедлению тестов.


FDS>Это не страшно. Поверь, всё это уже давно и успешно работает, я лишь пытаюсь перенести общую концепцию UniTesk на Nemerle с помощью макросов.


Работает, и работает на реальных, больших приложениях — это разные вещи. Если тесты будут идти часами — никто этим пользоваться не будет. А судя по услышанному тестов будет много, чтобы перекрыть все ветви исполнения.

FDS>Вообще говоря, в UniTesk в отчётах выводятся количество покрытий и лог переменных (лог делает сам пользователь) и иногда это бывает крайне полезно.


И что из них можно узнать? Скажем у меня цикл на 100500 итераций. Ты мне что все его счетчики выведешь?

Так что пускай лог и делает сам пользователь. Ему куда виднее что делать и как. Ты же реши эффективно ту задачу, что нужно.

VD>>Ну, в принципе достаточно иметь эталонное дерево ветвления, а не набор отдельных путей.


FDS>Лучше отдельные пути, т.к. тогда гораздо легче настраивать, что мы хотим покрыть.


Все равно они будут пересекаться. Так зачем хранить лишнюю информацию? В прочем, опять же твои проблемы. Просто количество данных увеличится в несколько раз. А тебе бы их нужно сократить до минимума, чтобы все это работало приемлемо на реальных проектах.

VD>>Тогда задача сводится к раскраске дерева в цвета (пройдено/не пройдено), а задача выявления не посещенного пути в задачу вывода пути к не раскрашенным подветкам.


FDS>Нет. Ещё раз повторюсь, у нас задача организовать комбинаторное покрытие условий, а не покрытие операторов.


У тебя и будет "комбинаторное покрытие условий". А дерево тебе по любому будет нужно, так как раскрашивать придется параллельно несколько ветвей и лишь в момент когда они разойдутся можно будет отбросить их часть. Это и есть дерево, где часть путей объединена, а ближе к листьям они расходятся.

VD>>Такое дерево будет весьма компактным и его смело можно разместить в памяти. Это конечно не битовая карта, но тоже компактная структура (не больше чем веток АСТ в коде).


FDS>Всё равно откуда-то его нужно брать перед тем, как размещать в памяти.


Естественно. Оно будет формироваться при анализе потоков управления в макросе.

FDS>А у нас оно есть в памяти только на этапе компиляции, следовательно, надо его выгружать в файл


Ничего не естественно. Можно сгенерировать код создающий это дерево при старте приложения или отложено (при первом к нему обращении).

FDS>и как-то это выгрузку настраивать (в идеале, ставим атрибут на сборку, который задаёт путь к файлам, описывающим требуемое покрытие, и функцию, вычисляющую требуемое покрытие, а потом из других макросов его считываем).


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

Потом, ты эти убьешь сразу двух зайцев. И модель получишь сразу в память, и структуру данных которую можно будет раскрашивать инструментированным кодом метода. А это уже выгода существенная.

Твой вариант подразумевает запись многотонных логов, ведь все повторные проходы одного и того же пути будут писаться в логи, а потом чтение все этой бурды в память и сравнение с эталонными путями. Причем придется эти логи еще перобразовывать все к тому же дереву, так как в исходном виде они не пригодны для анализа.

Получается бессмысленный расход процессорного времени, огромные файлы на дисках (цыклы и рекурсивные вызовы никто не отменял), куча работы по сравнению логов с эталонами, и в итоге тот же результат.

Как-то совсем это не здорово.

VD>>Пока не вижу нужды в файле. В прочем, создать файл из макроса не проблема. Получаешь путь к файлу (из свойства method.Body.Locatio.File) в котором лежит код, убираешь расширения, добавляешь имя типа и метода и некоторое другое расширение. Далее пиши в него что хочешь.


FDS>Неудобно. Потом его фиг найдёшь, ведь он будет писаться чёрт-знает куда (в рабочую директорию, которая может быть вообще закрыта для записи)


Это ты его не найдешь, если он будет черт знает где валяться, а путь к нему будет описан вручную. Придется вспоминать что ты там понпихал в исходниках и т.п. А файлов ведь будет море! На каждую функцию по файлу (или хотя бы на тип).

Ну, а не хочешь рядом с исхдниками, воссоздай структуру каталогов в отдельной папке. Это не сложно. Макросы даже могут специально навесить GoToInfo на некоторые ветки АСТ, так что можно будет переход делать автоматом (в IDE).

VD>>Явный овердизайн. В прочем, это следствие смешивания логирования со средствами вычисления покрытия/путей.


FDS>Нет, потому что я по логу вычисляю затем, покрыл я или нет что-либо. Но, в то же время, лог используется не только для этого.

FDS>А чтобы покрытие вычислить, да, нужен файл. Но и то, и другое здесь невозможно исключить.

Более чем возможно и я тебе описал как. То же что описываешь ты — это не работоспособное решение. На реальных проектах оно уйдет в даун.

Описывать же имена файлов для отдельных функций — это вообще — застрелиться.

FDS>Ну файл — задал: это просто строка (хотя ведь могу сделать и функцию, которая возвращает имя файла по какому-то соглашению).

FDS>А функцию вычисления покрытия я как задам? Я хочу, чтобы её пользователь мог расширять, поэтому в код макроса её включить принципиально не могу никак. Нужно задавать как параметр.

Думаю, ты о расширяемости думаешь очень рано. Тебе эту все сначала заставить взлететь в нерсширяемом виде. А уж потом о расширяемости думать. А то ведь, если оно дует тормозным и неудобным, то кроме тебя это никто использовать не будет. И как следствие, проблема расширяемости просто не возникнет .

А в принципе, она конечно решаема.

VD>>Какие-то плагины к еще не написанному макросу. Явный овердизайн.


FDS>Зачем мне писать макрос, если я не знаю, сможет ли он потом работать так, как я хочу? Сначала мне нужно убедиться, что его возможно написать.


Сможет. Но вероятность, что он не взлетит по другим причинам, по моим прикидкам, стремится к 70%. Так что просто забей пока на это. Будет продукт — сделать его расширяемым не составит труда, так как дотнет штука компонентная.

VD>>Ну, забыл и забыл. Ты не сможешь формально вычислить что забыл программист. Забыл, значит тест на некоторых данных упадет и ты увидишь свое непокрытие.


FDS>А кто тебе сказал, что "тест на некоторых данных упадёт"?


В общем, случае это физически невозможно NP-полная задача. Но твое покрытие и ряд эвристик смогут увеличить эту вероятность до приемлемых значений.

FDS>Мне и нужно убедиться, что тест подал не только положительные значения, но и нуль, и отрицательные, что модуль не упал не потому, что его не протестировали, а потому, что правильно работает. Но прямо в исходный макрос это всё писать — убийство малолетних.


Как я уже сказал выше, в общем случае это все равно невозможно. Ну, да. Вынести эвристики в плагины наверно будет не плохим решением. Хотя нужно это будет только если библиотеку примет значимое число людей. Иначе не грех будет и в макрос новую функцию добавить.

Короче, мой тебе совет. Оставь фичу расширяемости на вторую версию.

VD>>Потом, опят же у тебя какие-то абстрактные рассуждения. Приведи пример того что ты имеешь в виду.

VD>>Ну, там вот тебе вход "тут исходный код", вот параметры макросу "...", а вот конечный код.

FDS>
FDS>[testCoverageMacro("имя:\файла", someFunc)]
FDS> // someFunc - некая заранее написанная функция, которая вычисляет критерий покрытия
FDS>


Принципиально сделать это можно. Если someFunc при этом будет импортироваться из внешней сборки, то не так уж сложно даже.

Что касается имени файла, то это тоже сделать не сложно. Но это же застрелиться писать к каждой функции бессмысленное имя файла. Это надо автоматом задавать. Максим, что нужно настраивать — это каталог куда все это класть. Если же писать же это в один файл, то этот файл потом будет открываться годами, если вообще откроется.

VD>>На мой взгляд, прежде всего, не стоит мешать эти две задач. Логирование, логированием, а покрытие покрытием.


FDS>Лог используется для анализа покрытия. Всё равно его нужно делать.


Об этом мы уже говрили. Для покрытия можно использовать куда боеле компактную структуру данных и более эффективные алгоритмы.

Уверен, что в реально используемых "промышленных" реализациях так и сделано (если такие вообще есть). Иначе это просто не взлетит. Объем данных будет нереальным.

VD>>А вот к логированию и чтению логов я прибегаю только в самом крайнем случае. Кроме того. Я не буду логировать все подряд. Я вставлю логирование только туда куда надо (и возможно поменяю это место раз 10). Так что подобная помощью от системы выявляющей непокрытие я расценю как вредительство!


FDS>Значит эта система не для тебя.


А для кого тогда? Я как раз средний программист.

FDS>В некоторых случаях отладчик использовать невозможно вообще.


И что? Из этого не следует, что логирование нужно применять везде. Или что это хороший способ найти ошибку. А когда без него невозможно, то и говорить не о чем.

VD>>ОК, я понял, что ты хочешь выявлять пути. Тут идея с флагами конечно непригодна. Но идя логирования и последующего анализа логов просто не взлетит на мало-мальски объемной системе.


FDS>Влад, я ничего нового не придумал. Ты просто не видел как это работает.


Мне не надо на это смотреть. Я понял что ты хочешь и прикидываю это к тому же компилятору немерла. На нем твое решение не взлетит.

Меж тем очевидно, что решение можно сделать с вполне себе приемлемым оверхэдом.

FDS>На самом деле то, что я хочу повторить чувствительно сложнее.


О оно где-то на практике используется?

FDS>Ничего не понял.


Короче, сделать можно, но потрахаться придется . Так что чтобы не трахаться понапрсну, сделай первую версию с заранее захардкженым списком функций, а там уже, если надо будет доработаешь.

VD>>Ну, а из макроса вызывать их динамически, через загрузку сборки и рефлексию. Тут можно использовать макрос late для упрощения динамического вызова.


FDS>А параметры я как через рефлексию передам?


Легко и непринужденно. В прочем, для вызова даже рефлексия не нужна. Достаточно через нее создать делегат в который засунуть ссылку на нужную функцию. Делегат будет создаваться один раз, а далее его вызов будет идти со скорость примерно равной скости вызова метода интерфейса.

FDS>Очень неудобно для пользователя получается, хотя, в принципе, это реализуемо.


Пользователь то тут причем? Все что ему нужно будет сделать — это создать сборку, реализовать в ней нужные функции и описать импорт сборки в проекте где он будет использовать эти функции. Это куда лучше чем изврат с динамической компиляцией, и как следствие, отсутствие нормальной отладки и интеллисенса.

VD>>На твоем месте я бы:

VD>>1. Не смешивал функции подсистемы логирования и подсистемы выявления не пройденных путей.
FDS>Невозможно
VD>>2. Не вводил бы возможность подстановки пользовательских функций, а просто сделал бы гибкую систему настроек.
FDS>Невозможно
VD>>3. Вообще отказался бы от использования файлов для выявления покрытия, а остановился бы на статическом дереве путей для каждого метода и инструментировании методов кодом раскраски этих деревьев.
FDS>Невозможно

Ну, это твое дело. Мое мнение ты услышал.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[13]: Отступление в Unitesk
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.08.11 22:28
Оценка:
Здравствуйте, FDSC, Вы писали:

VD>>Вычислишь во время анализа потока управления (в макросе).


FDS>А дальше? После компиляции оно пропадёт. А мне нужно-то оно будет после запуска тестов уже.


Я же несколько раз сказал — добавляешь (в тип где объявлен метод) статическое поле, а в него код формирования дерева описывающего пути.

FDS>Наоборот, мне всё равно придётся эту структуру в файл запихивать: отчёт по тестированию делать придётся.


В файл лучше писать сам отчет в виде текста или ХТМЛ-я.

FDS>Да и файл тогда сможет использовать любая другая программа анализа, в т.ч. специально написанная пользователем.


Ему тоже куда луче будет не черт знает какой формат парсить, а работать с деревом которое стандартно и легко анализируется.

VD>>Работы здесь будет даже меньше. У тебя это дерево будет нужно внутри инструментированного тела метода.

FDS>Как раз там мне это дерево совершенно ни к чему

Пойми. У теяб только два пути:
1. Писать в логи информацию о вхождении в каждый блок программы. Не надо быть предсказамусом, чтобы понять, что в серьезных приложениях логи будут гигабайтными (а то и терабайтными), все это будет тормозить и т.п.

2. Заранее построить в памяти дерево содержащее возможные пути, а в коде внедренном инструментированием просто раскрашивать это дерево. Тогда в конце тестов все что останется сделать — этот пробежаться по таким деревьям и на их основании сгенерировать отчет о непроцденных путях. Скорость такого решения будет очень высокая , ведь ничего никуда писать, вовремя выполнения, не надо. Гигабайтных логов не будет, так как на диск будет писаться только отчеты. Писать сложной логики анализа логов так же не нужно будет.

То есть выбор, между реально рабочим решением, и расширенной функциональность решения которое с огромной вероятностью, вообще не взлетит.

VD>>Вариант 3: Завести в папке проекта (путь к которой можно узнать через Manaret) папку в которую складывать все эти файлы, повторяя при этом иерархию исходников.

FDS>Неподходит Директория должна быть произвольной, к проекту не относящейся. Например, когда я работал в ИСП РАН у нас она была под отдельной системой контроля версий и даже хранилась на другом сервере.

Логи в системе контроля версий?
Вы маньяки!

Ну, а назначить другой каталог — это не проблема. Сделай еще один макрос который позволит это сделать. Только для не каждого метода, а для проекта в целом. Ну, или хотя бы для папки в проекте. А то это ж застрелиться — указывать пути у каждой функции.

VD>>Вариант 4: Придумать что-то еще.

FDS>Ну вот я и придумал.

То что ты придумал не утомятся делать только в ИСП РАН.
То есть, гибкость — это хорошо, но только если это дополнительная возможность, а не когда это "почетная" обязанность.

VD>>Хотя еще раз повторюсь, что на мой взгляд файл тут просто не нужен.


FDS>Опять же, кроме файла есть ещё другие пользовательские функции.


Ну, их ты сможешь подгружать из сборок.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: Пути инструментации для анализа покрытия
От: FDSC Россия consp11.github.io блог
Дата: 22.08.11 23:49
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, FDSC, Вы писали:


VD>>>В любом случае идея писать в логи какой-то текст для этого мне кажется не верной. Это приведет к серьезному замедлению тестов.


FDS>>Это не страшно. Поверь, всё это уже давно и успешно работает, я лишь пытаюсь перенести общую концепцию UniTesk на Nemerle с помощью макросов.


VD>Работает, и работает на реальных, больших приложениях — это разные вещи. Если тесты будут идти часами — никто этим пользоваться не будет. А судя по услышанному тестов будет много, чтобы перекрыть все ветви исполнения.


Работает на реальных больших приложениях (одно из — работает на 22-х процессорном сервере в компании, имя которой вы точно знаете). Тесты, кстати, идут часами. Заказчик доволен и заказчик не один.

FDS>>Вообще говоря, в UniTesk в отчётах выводятся количество покрытий и лог переменных (лог делает сам пользователь) и иногда это бывает крайне полезно.


VD>И что из них можно узнать? Скажем у меня цикл на 100500 итераций. Ты мне что все его счетчики выведешь?


Да. В некоторых ситуациях это самый удобный способ узнать, что случилось и повторить (точно могу сказать про 3000х800=2,4 миллиона итераций).

VD>Так что пускай лог и делает сам пользователь. Ему куда виднее что делать и как. Ты же реши эффективно ту задачу, что нужно.


Дык я и хочу, чтобы он лог мог настроить прямо из макроса передав туда, если нужно, и функцию логирования (пока не подумал, правда, как это делать именно в Nemerle; проблема в том, что сам лог должен вставляться автоматически, пользователь лишь будет определять что и как, а когда — должен решить макрос).

FDS>>Лучше отдельные пути, т.к. тогда гораздо легче настраивать, что мы хотим покрыть.


VD>Все равно они будут пересекаться. Так зачем хранить лишнюю информацию? В прочем, опять же твои проблемы. Просто количество данных увеличится в несколько раз. А тебе бы их нужно сократить до минимума, чтобы все это работало приемлемо на реальных проектах.


Практика показывает, что ничего сокращать не нужно, а вот настроить пути покрытия как раз очень нужно.

VD>>>Тогда задача сводится к раскраске дерева в цвета (пройдено/не пройдено), а задача выявления не посещенного пути в задачу вывода пути к не раскрашенным подветкам.


FDS>>Нет. Ещё раз повторюсь, у нас задача организовать комбинаторное покрытие условий, а не покрытие операторов.


VD>У тебя и будет "комбинаторное покрытие условий". А дерево тебе по любому будет нужно, так как раскрашивать придется параллельно несколько ветвей и лишь в момент когда они разойдутся можно будет отбросить их часть. Это и есть дерево, где часть путей объединена, а ближе к листьям они расходятся.


Ничего не понял.

Есть код

if (условие1)
{
  ...
  when (условие2)
    ...
}
else
  ....;

when (условие3)
  ...

Чтобы по нему трассы сгенерировать, мне нужно сгенерировать рекурсивно подтрассы для каждого из условий, потом их объединить во всех возможных сочетаниях. Как это представить деревом, чтобы описать комбинаторное покрытие, честно говоря, ума не приложу.


VD>>>Такое дерево будет весьма компактным и его смело можно разместить в памяти. Это конечно не битовая карта, но тоже компактная структура (не больше чем веток АСТ в коде).


FDS>>Всё равно откуда-то его нужно брать перед тем, как размещать в памяти.


VD>Естественно. Оно будет формироваться при анализе потоков управления в макросе.


И дальше теряться. Откуда я его возьму на этапе анализа покрытия тестами?

FDS>>А у нас оно есть в памяти только на этапе компиляции, следовательно, надо его выгружать в файл


VD>Ничего не естественно. Можно сгенерировать код создающий это дерево при старте приложения или отложено (при первом к нему обращении).


А потом выгружать в файл, чтобы провести анализ покрытия. Тестируемое приложение лишь логирует, не нужно смешивать лог и анализ. Анализ должен быть дальше по логу, даже если приложение не завершилось.

VD>Вот за одно и проблема сохранения будет решена (отброшена за ненадобностью). Все равно инструментированную версию применять для работы будет не здорово, так как оверхэд от инструментации будет. Так зачем что-то в файлы пихать и потом из этих файлов все читать обратно, когда все тоже самое можно сразу в память поднять. Макросы это сделают без напряга.


Хотя бы для того, чтобы не смешивать логирование и анализ, который обязательно должен быть после отработки приложения, в т.ч. после его аварийного завершения. Это возможно только в отдельном приложении.

VD>Потом, ты эти убьешь сразу двух зайцев. И модель получишь сразу в память, и структуру данных которую можно будет раскрашивать инструментированным кодом метода. А это уже выгода существенная.


Которая моментально убивается при падении приложения и потере всего, что было в памяти.

VD>Твой вариант подразумевает запись многотонных логов, ведь все повторные проходы одного и того же пути будут писаться в логи, а потом чтение все этой бурды в память и сравнение с эталонными путями. Причем придется эти логи еще перобразовывать все к тому же дереву, так как в исходном виде они не пригодны для анализа.


Да, именно.

VD>Получается бессмысленный расход процессорного времени, огромные файлы на дисках (цыклы и рекурсивные вызовы никто не отменял), куча работы по сравнению логов с эталонами, и в итоге тот же результат.


Но уже с логами в файлах и результатом, даже при аварийной работе ПО. К сожалению, падают программы часто и просто так от этого избавится невозможно (бывают и бесконечные рекурсии, и памяти нехватает — тут только вовремя записанные в файл данные помогут, да и не всё обернёшь в try-catch).

VD>>>Пока не вижу нужды в файле. В прочем, создать файл из макроса не проблема. Получаешь путь к файлу (из свойства method.Body.Locatio.File) в котором лежит код, убираешь расширения, добавляешь имя типа и метода и некоторое другое расширение. Далее пиши в него что хочешь.


FDS>>Неудобно. Потом его фиг найдёшь, ведь он будет писаться чёрт-знает куда (в рабочую директорию, которая может быть вообще закрыта для записи)


VD>Это ты его не найдешь, если он будет черт знает где валяться, а путь к нему будет описан вручную. Придется вспоминать что ты там понпихал в исходниках и т.п. А файлов ведь будет море! На каждую функцию по файлу (или хотя бы на тип).


Файл — один на сборку, т.е. на проект. Вспоминать ничего не придётся, т.к. этот инструмент предназначен для программиста-тестировщика, каждый день записывающего в логи гигабайты информации и прекрасно знающего, где эти логи лежат.

VD>Ну, а не хочешь рядом с исхдниками, воссоздай структуру каталогов в отдельной папке. Это не сложно. Макросы даже могут специально навесить GoToInfo на некоторые ветки АСТ, так что можно будет переход делать автоматом (в IDE).


Не понял, что такое GoToInfo, можно ссылку?
Проблема в том, в какой именно папке их воссоздавать. Вообще, повторюсь, главное — это индивидуализация уже написанного макроса другим разработчиком. Иначе придётся всю работу проделывать самостоятельно, что долго и ограничивает функциональность (уже приходилось)

VD>>>Явный овердизайн. В прочем, это следствие смешивания логирования со средствами вычисления покрытия/путей.


VD>Более чем возможно и я тебе описал как. То же что описываешь ты — это не работоспособное решение. На реальных проектах оно уйдет в даун.


На реальных проектах — не уходит. В любом случае, запись в памяти не подходит, я уже говорил почему (аварийное завершение приложения).

VD>Описывать же имена файлов для отдельных функций — это вообще — застрелиться.


Согласен.

FDS>>Ну файл — задал: это просто строка (хотя ведь могу сделать и функцию, которая возвращает имя файла по какому-то соглашению).

FDS>>А функцию вычисления покрытия я как задам? Я хочу, чтобы её пользователь мог расширять, поэтому в код макроса её включить принципиально не могу никак. Нужно задавать как параметр.

VD>Думаю, ты о расширяемости думаешь очень рано. Тебе эту все сначала заставить взлететь в нерсширяемом виде. А уж потом о расширяемости думать. А то ведь, если оно дует тормозным и неудобным, то кроме тебя это никто использовать не будет. И как следствие, проблема расширяемости просто не возникнет .


Я сначала думаю о расширяемости, а потом обо всём остальном, иначе когда проблема расширяемости возникнет, будет уже слишком поздно.
В любом случае, такая проблема вполне реально может возникнуть, а вот как её решить на Nemerle я не понял. Честно говоря, даже не очень понял, почему вообще макросы это не предусматривают

VD>А в принципе, она конечно решаема.


VD>Сможет. Но вероятность, что он не взлетит по другим причинам, по моим прикидкам, стремится к 70%. Так что просто забей пока на это. Будет продукт — сделать его расширяемым не составит труда, так как дотнет штука компонентная.


Java — тоже штука компонентная. Ничего расширить я там в своё время не смог. А вот если взлетит, то вероятность того, что мне понадобится это расширение стремится к 100% процентам (так как на Java уже требовалось) и это один из самых больших рисков. Мне совершенно не хочется тратить время на то, что потом нельзя будет расширить. К тому же мне просто интересно, как это сделать и я хочу это сделать сразу, потому что к проекту я могу и охладеть, а вот эту штуку было бы сделать интересно просто чтобы понять ограничения макросов. К сож., их, судя по всему, неимоверно много.


VD>>>Ну, забыл и забыл. Ты не сможешь формально вычислить что забыл программист. Забыл, значит тест на некоторых данных упадет и ты увидишь свое непокрытие.


FDS>>А кто тебе сказал, что "тест на некоторых данных упадёт"?


VD>В общем, случае это физически невозможно NP-полная задача. Но твое покрытие и ряд эвристик смогут увеличить эту вероятность до приемлемых значений.


Вот как раз такую эвристику, помогающую тестировщику об этом не забыть я и пытаюсь дать возможность встроить в систему.

FDS>>Мне и нужно убедиться, что тест подал не только положительные значения, но и нуль, и отрицательные, что модуль не упал не потому, что его не протестировали, а потому, что правильно работает. Но прямо в исходный макрос это всё писать — убийство малолетних.


VD>Как я уже сказал выше, в общем случае это все равно невозможно.


NP-полная задача хорошо решается перебором .

VD>Ну, да. Вынести эвристики в плагины наверно будет не плохим решением. Хотя нужно это будет только если библиотеку примет значимое число людей. Иначе не грех будет и в макрос новую функцию добавить.


Считай, что я поспорил с одним из разработчиков Unitesk, что я смогу на Nemerle сделать круче, чем они на Java и C#. Задача это продвинуть в использование — это для меня дело десятое. Если получится, я буду рад.

Что касается "не грех будет и в макрос новую функцию добавить" — практика показывает, что то, что может сам пользователь добавить, то используется. А вот то, что разработчик когда-то добавит, используется очень редко. Зачастую разработчик об этом и не знает. В своё время для того, чтобы добавить одну небольшую функцию в JavaTesk я ждал 7 месяцев только того, чтобы до разработчиков дошло, что то, что я предлагаю нужно на реальном проекте и не сейчас, а год назад. Что уж говорить про то, что реализации я не дождался (при мне был заявлен фичреквест)

VD>>>Потом, опят же у тебя какие-то абстрактные рассуждения. Приведи пример того что ты имеешь в виду.

VD>>>Ну, там вот тебе вход "тут исходный код", вот параметры макросу "...", а вот конечный код.

FDS>>
FDS>>[testCoverageMacro("имя:\файла", someFunc)]
FDS>> // someFunc - некая заранее написанная функция, которая вычисляет критерий покрытия
FDS>>


VD>Принципиально сделать это можно. Если someFunc при этом будет импортироваться из внешней сборки, то не так уж сложно даже.


Стоп. Без рефлексии именно? Можно ещё раз, как?

VD>Что касается имени файла, то это тоже сделать не сложно. Но это же застрелиться писать к каждой функции бессмысленное имя файла. Это надо автоматом задавать. Максим, что нужно настраивать — это каталог куда все это класть. Если же писать же это в один файл, то этот файл потом будет открываться годами, если вообще откроется.


Да нет. Покрытие вычисляется для метода, в методе обычно условий очень не много и всё работает неплохо. Логируются обычно только методы верхнего уровня или на один ниже. Вообще говоря, эти методы специально пишутся программистом-тестировщиком, т.е. ставить такой макрос на реальный метод может быть бессмысленно.

VD>>>На мой взгляд, прежде всего, не стоит мешать эти две задач. Логирование, логированием, а покрытие покрытием.


FDS>>Лог используется для анализа покрытия. Всё равно его нужно делать.


VD>Об этом мы уже говрили. Для покрытия можно использовать куда боеле компактную структуру данных и более эффективные алгоритмы.


VD>Уверен, что в реально используемых "промышленных" реализациях так и сделано (если такие вообще есть). Иначе это просто не взлетит. Объем данных будет нереальным.


Это не так. В реально используемой промышленно реализации пишется лог, а затем этот лог анализируется. Это я знаю точно (там даже инструменты разные группы разработчиков делают: одни, что лог пишет и анализ требуемого покрытия проводит, вторые — что лог анализирует и строит отчёты)

VD>>>А вот к логированию и чтению логов я прибегаю только в самом крайнем случае. Кроме того. Я не буду логировать все подряд. Я вставлю логирование только туда куда надо (и возможно поменяю это место раз 10). Так что подобная помощью от системы выявляющей непокрытие я расценю как вредительство!


VD>А для кого тогда? Я как раз средний программист.


На моей памяти, система используется для независимого тестирования. А ты к тестированию отношения как такового не имеешь. Не стоит принимать эту систему за NUnit — она и должна быть значительно более тяжёлой.

FDS>>В некоторых случаях отладчик использовать невозможно вообще.


VD>И что? Из этого не следует, что логирование нужно применять везде. Или что это хороший способ найти ошибку.


А кто сказал, что оно будет применяться везьде?

VD>А когда без него невозможно, то и говорить не о чем.

Ну да, тут только лог и спасёт.

FDS>>Влад, я ничего нового не придумал. Ты просто не видел как это работает.


VD>Мне не надо на это смотреть. Я понял что ты хочешь и прикидываю это к тому же компилятору немерла. На нем твое решение не взлетит.


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

VD>Меж тем очевидно, что решение можно сделать с вполне себе приемлемым оверхэдом.


Не понял.


FDS>>На самом деле то, что я хочу повторить чувствительно сложнее.


VD>О оно где-то на практике используется?


Я же говорю, я копирую чужое решение, уже раз десять повторил.

FDS>>Ничего не понял.


VD>Короче, сделать можно, но потрахаться придется . Так что чтобы не трахаться понапрсну, сделай первую версию с заранее захардкженым списком функций, а там уже, если надо будет доработаешь.


Хочу потрахаться. Признаться, мне это более интересно, чем делать остальной анализ (подобное, хотя всё же более простое, я уже делал) и мотива нет, если это не будет правильно расширяться. Нечем будет похвастаться, понимаешь?

VD>>>Ну, а из макроса вызывать их динамически, через загрузку сборки и рефлексию. Тут можно использовать макрос late для упрощения динамического вызова.


FDS>>А параметры я как через рефлексию передам?


VD>Легко и непринужденно. В прочем, для вызова даже рефлексия не нужна. Достаточно через нее создать делегат в который засунуть ссылку на нужную функцию. Делегат будет создаваться один раз, а далее его вызов будет идти со скорость примерно равной скости вызова метода интерфейса.


Вот представь себе, что у меня есть некоторая функция, которая бьёт на классы эквивалентности целые числа. Но делает это настраиваемо, т.е. она не сама бьёт, возвращает мне функцию без параметров, которая всё что надо умеет делать и немного различается в зависимости от параметров генерирующей функции (генерация здесь — простое лексическое замыкание).

Т.е. в идеале программист должен смочь написать следующее
[testCoverageMacro("имя:\файла", getSomeFunc(параметр1, параметр2, ["имя переменной для логирования", "имя переменной для логирования"], SomeEnum.типКлассаЭквивалентности))]

а макрос должен иметь возможность это вызвать и получить результат работы getSomeFunc. По сути, у меня такое впечатление, что чтобы сделать такое через рефлексию, я должен обработать второй параметр вручную по TExpr. Нет?

FDS>>Очень неудобно для пользователя получается, хотя, в принципе, это реализуемо.


VD>Пользователь то тут причем? Все что ему нужно будет сделать — это создать сборку, реализовать в ней нужные функции и описать импорт сборки в проекте где он будет использовать эти функции. Это куда лучше чем изврат с динамической компиляцией, и как следствие, отсутствие нормальной отладки и интеллисенса.


Вот я вижу ограничение и чувствую сильный дискомфорт. Тут даже не важно, может быть рефлексия действительно самый лучший вариант и ты прав. Но как с этим ограничением бороться, чтобы пользователь мог ввести что-нибудь, что я немного выше написал
[testCoverageMacro("имя:\файла", getSomeFunc(параметр1, параметр2, ["имя переменной для логирования", "имя переменной для логирования"], SomeEnum.типКлассаЭквивалентности))]

я не понимаю и меня это мучает, порождает неуверенность. Крайне неприятные чувства при программировании на языке, который позиционируется как самый свободный из всех языков.

P.S. Я так и не понял, возможно ли вообще, в принципе, каким-либо образом передать в макрос функцию так, чтобы пользователь мог работать с параметрами макроса как с обычными переменными?

P.P.S. По методу Late посмотрел, но пока тупо не разобрался. Но насколько я понял, он рефлексию делает в runtime, как мне показалось по этой строке

def call_expr(args_expr)
        <[ { $(expr_name : name) = $expr; $(expr_name : name).GetType().InvokeMember($name_expr, $flags_expr, null, $(expr_name : name), $args_expr, $pms_expr, null, $names_expr) } ]>



Или я чего-то недосмотрел?
Re[14]: Отступление в Unitesk
От: FDSC Россия consp11.github.io блог
Дата: 23.08.11 00:28
Оценка:
Здравствуйте, VladD2, Вы писали:


FDS>>Наоборот, мне всё равно придётся эту структуру в файл запихивать: отчёт по тестированию делать придётся.


VD>В файл лучше писать сам отчет в виде текста или ХТМЛ-я.


Не лучше.

FDS>>Да и файл тогда сможет использовать любая другая программа анализа, в т.ч. специально написанная пользователем.


VD>Ему тоже куда луче будет не черт знает какой формат парсить, а работать с деревом которое стандартно и легко анализируется.


Кто нам мешает использовать стандартный формат?

VD>Пойми. У теяб только два пути:

VD>1. Писать в логи информацию о вхождении в каждый блок программы. Не надо быть предсказамусом, чтобы понять, что в серьезных приложениях логи будут гигабайтными (а то и терабайтными), все это будет тормозить и т.п.

Да, логи гигабайтные, но по причине дампа многих объектов: покрытие там лишь малая часть. Покрытие записывается при вхождении только в специально выбранные методы. Когда нужно протестировать, интерфейсные методы так логируются. Только интерфейсные или даже специально написанные.

VD>2. Заранее построить в памяти дерево содержащее возможные пути, а в коде внедренном инструментированием просто раскрашивать это дерево. Тогда в конце тестов все что останется сделать — этот пробежаться по таким деревьям и на их основании сгенерировать отчет о непроцденных путях. Скорость такого решения будет очень высокая , ведь ничего никуда писать, вовремя выполнения, не надо. Гигабайтных логов не будет, так как на диск будет писаться только отчеты. Писать сложной логики анализа логов так же не нужно будет.


Ещё раз повторюсь — всё хорошо, кроме аварийного завершения тестируемой программы и пропадания всех результатов. Это — принципиально.
И ещё раз — я ничего нового не выдумываю, всё это уже работает и работает хорошо и за деньги. Я хочу повторить это на Nemerle, потому что интересно, насколько Nemerle делает это легче и хочу повторить это не в простейшем варианте, т.к. в нём ничего интересного нет — я не увижу ограничений языка.

VD>То есть выбор, между реально рабочим решением, и расширенной функциональность решения которое с огромной вероятностью, вообще не взлетит.


см. выше. Интереса делать простое, но реально работающее у меня просто нет: это уже сделали до меня, причём для нескольких языков. Я хочу лучше понять, как работает Nemerle и оценить его ограничения. А если заработает и будет полезно — я вообще буду прыгать до потолка.


VD>>>Вариант 3: Завести в папке проекта (путь к которой можно узнать через Manaret) папку в которую складывать все эти файлы, повторяя при этом иерархию исходников.

FDS>>Неподходит Директория должна быть произвольной, к проекту не относящейся. Например, когда я работал в ИСП РАН у нас она была под отдельной системой контроля версий и даже хранилась на другом сервере.

VD>Логи в системе контроля версий?

VD>Вы маньяки!

Ага. Причём где-то за пол-года точно. И они использовались для того, чтобы выяснить, в каком релизе перестала работать некая функциональность — помогали именно пространные логи (т.к. функциональность в явном виде не проверялась).

VD>Ну, а назначить другой каталог — это не проблема. Сделай еще один макрос который позволит это сделать. Только для не каждого метода, а для проекта в целом. Ну, или хотя бы для папки в проекте. А то это ж застрелиться — указывать пути у каждой функции.


Насколько я понимаю, насчёт макроса примерно так надо делать
1. Создаю макроатрибут уровня сборки
2. Выдаю туда string с именем папки
3. Записываю внутри макроса в некую глобальную переменную (например, статическую переменную типа) эту строку, т.к. string можно передавать в макрос как типизированный параметр, в отличие от функции или других сложных типов
4. Считываю из других макросов это значение

Верно?

VD>То что ты придумал не утомятся делать только в ИСП РАН.

VD>То есть, гибкость — это хорошо, но только если это дополнительная возможность, а не когда это "почетная" обязанность.

К сож., это очень необходимая возможность по-моему мнению. В ИСП РАН, кстати, нормально не реализованная, но очень нужная.

VD>>>Хотя еще раз повторюсь, что на мой взгляд файл тут просто не нужен.


FDS>>Опять же, кроме файла есть ещё другие пользовательские функции.


VD>Ну, их ты сможешь подгружать из сборок.


А если их будет 30 штук, и все однотипные, которые на самом деле можно сгенерировать одной функцией с лексическим замыканием? Быр. Хотя, в принципе, ты прав. Но хотелось бы чего-то более элегантного и, главное, более близко расположенного к реальному коду,
чтобы текст этой функции потом не пришлось долго искать.

А вообще, решение работающее, наверное.
Re[7]: Пути инструментации для анализа покрытия
От: VladD2 Российская Империя www.nemerle.org
Дата: 23.08.11 01:54
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>Работает на реальных больших приложениях (одно из — работает на 22-х процессорном сервере в компании, имя которой вы точно знаете). Тесты, кстати, идут часами. Заказчик доволен и заказчик не один.


Может тесты от того и идут часами?

VD>>И что из них можно узнать? Скажем у меня цикл на 100500 итераций. Ты мне что все его счетчики выведешь?


FDS>Да. В некоторых ситуациях это самый удобный способ узнать, что случилось и повторить (точно могу сказать про 3000х800=2,4 миллиона итераций).


Извини, окончания компиляции компилятора немерла ты не дождешся. Так что информация будет просто бесполена. Это тебе не 22 процессора не бизнес-задачки. Тут работает только один проц, а вычислений больше чем известная фирма делает за год.

FDS>Дык я и хочу, чтобы он лог мог настроить прямо из макроса передав туда, если нужно, и функцию логирования


Дык для этого отдельная макра есть. Зачем мешать то все одну кучу?

FDS>Практика показывает, что ничего сокращать не нужно, а вот настроить пути покрытия как раз очень нужно.


Просто у тебя однобокая практика. Поверь на слова. На вычислительных задачах это не работает.

Тут как-то мне товарищ объяснял, что можно вообще рестроспективно запоминать все изменения в программе и сделать отладчик обратного хода. Я ему так и не смог объяснить, что это работает только при небольших объемах вычислений.

В общем, переубеждать тебя не буду. Это бесполезно. Просто когда будешь проводить тесты, то в качестве одного из тестов возьми компилятор немерла и его же тесты. Погляди как твое решение будет на нем работать.

VD>>У тебя и будет "комбинаторное покрытие условий". А дерево тебе по любому будет нужно, так как раскрашивать придется параллельно несколько ветвей и лишь в момент когда они разойдутся можно будет отбросить их часть. Это и есть дерево, где часть путей объединена, а ближе к листьям они расходятся.


FDS>Ничего не понял.


Плохо.

FDS>Есть код


FDS>
FDS>if (условие1)
FDS>{
FDS>  ...
FDS>  when (условие2)
FDS>    ...
FDS>}
FDS>else
FDS>  ....;

FDS>when (условие3)
FDS>  ...
FDS>

FDS>Чтобы по нему трассы сгенерировать, мне нужно сгенерировать рекурсивно подтрассы для каждого из условий, потом их объединить во всех возможных сочетаниях. Как это представить деревом, чтобы описать комбинаторное покрытие, честно говоря, ума не приложу.

Вход у тебя один? Значит корень у нас уже есть. Далее, как ты говоришь — "нужно сгенерировать рекурсивно подтрассы для каждого из условий, потом их объединить во всех возможных сочетаниях". В итоге получаем развесистое дерево с один корнем. Что тут не понятного?

Для твоего примера это что-то вроде:
Корень
  условие1
    1...
    условие1:true
      2...
    условие1:false
      3...
  условие3
    4...
  5...

Из него можно вывести все возможные пути. Например:
Корень->условие1->1...->условие1:true->2...->5...
Корень->условие1->1...->условие1:true->2...->условие3->4...->5...


VD>>Естественно. Оно будет формироваться при анализе потоков управления в макросе.


FDS>И дальше теряться. Откуда я его возьму на этапе анализа покрытия тестами?


Десятый раз повторяю — запоминаться в статическом поле. Твой макрос сформирует код строящий это дерево (из конструкторов).

VD>>Ничего не естественно. Можно сгенерировать код создающий это дерево при старте приложения или отложено (при первом к нему обращении).


FDS>А потом выгружать в файл, чтобы провести анализ покрытия.


Зачем в файл? Уфф. Если у тебя есть это дерево и код инструментирован, то при прогоне кода, он будет автоматом это дерево раскрашивать. Если управление прошло по некоторому пути, то оно будет раскрашено как пройденное.

FDS>Тестируемое приложение лишь логирует, не нужно смешивать лог и анализ. Анализ должен быть дальше по логу, даже если приложение не завершилось.


Можно и так. Только это будет сильно тормозить.

FDS>Которая моментально убивается при падении приложения и потере всего, что было в памяти.


В дотнете можно обрабатывать событие "необработанное исключение" и на него разбираться. Хотя есть ряд исключений которые не обрабатываются. Но их мало.

FDS>Но уже с логами в файлах и результатом, даже при аварийной работе ПО. К сожалению, падают программы часто и просто так от этого избавится невозможно (бывают и бесконечные рекурсии, и памяти нехватает — тут только вовремя записанные в файл данные помогут, да и не всё обернёшь в try-catch).


Ага. Но есть ода проблема. С такими всеобемлющими логами приложение клиенту не отдашь. Стало быть гонять с ними ты будешь только автоматические тесты. А с ними и так ясно все при падении.

FDS>Файл — один на сборку, т.е. на проект.


Ты его размер представляешь?

Потом, если файл один. То на кой его имя указывать на каждом методе? Нужно ввести макроатрибут уровня сборки который позволит задать имя файла. И все! А уж в файле твой макро сам всему имена задаст. У него же вся информация.

Сколько будет длиться анализ этого лога для вычислительных задач вроде компиляции компилятора немерла?

VD>>Ну, а не хочешь рядом с исхдниками, воссоздай структуру каталогов в отдельной папке. Это не сложно. Макросы даже могут специально навесить GoToInfo на некоторые ветки АСТ, так что можно будет переход делать автоматом (в IDE).


FDS>Не понял, что такое GoToInfo, можно ссылку?


Это возможность макросу подсказать IDE как делать навигацию по для некоторого символа. Например, это дело используется для реализации навигации по грамматике Nemerle.Peg. Там при переходе к по имени подправила происходит переход к его определению, а при переходе с его имени на обработчик и обратно с обработчика на имя правила. Таким образом, можно "гулять" по грамматике.

FDS>Я сначала думаю о расширяемости, а потом обо всём остальном, иначе когда проблема расширяемости возникнет, будет уже слишком поздно.

FDS>В любом случае, такая проблема вполне реально может возникнуть, а вот как её решить на Nemerle я не понял. Честно говоря, даже не очень понял, почему вообще макросы это не предусматривают

Давольно странное требование расширять расширение. Прикинь если бы ты потребовал от палагина к студии возможности расширять его плагинам. Наверно есть такие плагины, ну, РеШарпер, например, но это все же исключение из правил. И проблема расширения расширений — это проблема писателя этих расширений .

VD>>Сможет. Но вероятность, что он не взлетит по другим причинам, по моим прикидкам, стремится к 70%. Так что просто забей пока на это. Будет продукт — сделать его расширяемым не составит труда, так как дотнет штука компонентная.


FDS>Java — тоже штука компонентная. Ничего расширить я там в своё время не смог.


Ну, мы тут щи лаптем не хлебаем. Если сказали тебе можно, значит можно. Не бись!
На Яве у тебя нас не было. А тут мы есть. И, если что, поможем.

FDS>А вот если взлетит, то вероятность того, что мне понадобится это расширение стремится к 100% процентам


Ну, вот тогда еще раз тему поднимешь и мы тебе поможем. Не берись сразу за 10 дел.

FDS>(так как на Java уже требовалось) и это один из самых больших рисков. Мне совершенно не хочется тратить время на то, что потом нельзя будет расширить. К тому же мне просто интересно, как это сделать и я хочу это сделать сразу, потому что к проекту я могу и охладеть,


Если тебе интересно, то можешь сделать это отдельно, в порядке эксеперементов.

FDS>а вот эту штуку было бы сделать интересно просто чтобы понять ограничения макросов. К сож., их, судя по всему, неимоверно много.


Макрос — это программа. Как и любую программу ее можно расширять. Готового средства нет, так как оно на фиг ни кому не было нужно, до тебя. Но дотнет среда компонентная. И делать в ней расширения не сложно.

Самый простой способ тебе описали. Компилируй свои расширения в отдельную сборку, грузи сборку через рефлексию и отображай имена функций в делегаты с нужной сигнатурой. Потом вызывай делегаты и они будут выполнять нужную тебе работу. Другой вариант — использование заранее определенных интерфейсов. Он почти ничем не отличается, кроме того что позволяет использовать не отдельные функции, а объекты с этим самым интерфейсом. Смысл же тот же самый. Грузишь сборку через рефлексию, создаешь тип по имени (через Activator), получаешь ссылку на интерфейс и дергаешь его как-будто имеешь дело с объектом скомпилированным в твоем же приложении.

FDS>NP-полная задача хорошо решается перебором .


Если время не ограничено, то конечно .

FDS>Считай, что я поспорил с одним из разработчиков Unitesk, что я смогу на Nemerle сделать круче, чем они на Java и C#. Задача это продвинуть в использование — это для меня дело десятое. Если получится, я буду рад.


Ну, раз поспорил, то давай. Производительность в этом тесте, как я понимаю, не важна?

FDS>Что касается "не грех будет и в макрос новую функцию добавить" — практика показывает, что то, что может сам пользователь добавить, то используется. А вот то, что разработчик когда-то добавит, используется очень редко. Зачастую разработчик об этом и не знает. В своё время для того, чтобы добавить одну небольшую функцию в JavaTesk я ждал 7 месяцев только того, чтобы до разработчиков дошло, что то, что я предлагаю нужно на реальном проекте и не сейчас, а год назад. Что уж говорить про то, что реализации я не дождался (при мне был заявлен фичреквест)


Практика показывает другое. Она показывает, что когда люди начинают лепить плагины к еще не написанному софту, то очень часто они этот софт так и не доводят до рабочего состояния.

Как я тебе уже сказал, чтобы опять что это возможно достаточно просто провести эксперимент. Он не сложный. Часа за два справишься. Создай макрос которому передается имя сборки и имя функции. Пусть эта фукнция вовращает PExpr. Далее создай пару длл-ей с парой функций в каждой, ну, и используй это дело в тестовом приложении. Сборки грузятся через Assebmbly.LoadFrom, далее получаешь список типов, в них список статических публичных методов и преобразуешь это дело в делегаты. Код будет примерно такой:
def checkSignature(m)
{
  true // здесь должен быт код проверки сингатуры
}
def funcMap = Assembly.LoadFrom("имя сборки") // загрузили сборку
  // получили все типы
  .GetTypes() 
  // запросили все подходящие статические метоы
  .SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public).Where(checkSignature)) 
  // преобразовали список методов (т.е. объектов MethodInfo) в словарь "имя метода"/"делегат"
  .ToDictionary(m => m.Name, m => Delegate.CreateDelegate(typeof(Func[string, int]), m) :> Func[string, int]);

_ = funcMap["имя_функции"]("значение");

В этом примере параметры функций string -> int. Ты же подставишь те что тебе нужны.

Как видишь, не так много кода, если знаешь что писать.

FDS>Стоп. Без рефлексии именно? Можно ещё раз, как?


Без рефлексии динамика не делается. А как я тебе в общих чертах набросал выше. Скорость вызова будет очень выской, так как он будет не через рефлексию, а через делегат делаться. Из динамической загрузки ничего быстрее не сделаешь.

Сами мкросборки примерно так же подгружаются.

VD>>Мне не надо на это смотреть. Я понял что ты хочешь и прикидываю это к тому же компилятору немерла. На нем твое решение не взлетит.


FDS>В данном эпизоде ты сильно ошибаешься, если имеешь в виду производительность.


ОК, рад буду ошибиться. Если сделаешь этого монстра, протестируй его на компиляторе. Тогда и увидим, кто был прав.

FDS>Хочу потрахаться. Признаться, мне это более интересно, чем делать остальной анализ (подобное, хотя всё же более простое, я уже делал) и мотива нет, если это не будет правильно расширяться. Нечем будет похвастаться, понимаешь?


Понимаю . Но хвастаться тут особо не чем. Это примитивщина. В общем, план действий я тебе орисовал. Далее сам. Если что подскажем. Можно конечно и до динамической компиляции с кэшированием докатиться, но это по-моему уже точно перебор.

FDS>Вот представь себе, что у меня есть некоторая функция, которая бьёт на классы эквивалентности целые числа. Но делает это настраиваемо, т.е. она не сама бьёт, возвращает мне функцию без параметров, которая всё что надо умеет делать и немного различается в зависимости от параметров генерирующей функции (генерация здесь — простое лексическое замыкание).


Это уже как тебе нравится. Можно замыкание, можно черта лысого.

FDS>Т.е. в идеале программист должен смочь написать следующее

FDS>[testCoverageMacro("имя:\файла", getSomeFunc(параметр1, параметр2, ["имя переменной для логирования", "имя переменной для логирования"], SomeEnum.типКлассаЭквивалентности))]

FDS>а макрос должен иметь возможность это вызвать и получить результат работы getSomeFunc. По сути, у меня такое впечатление, что чтобы сделать такое через рефлексию, я должен обработать второй параметр вручную по TExpr. Нет?


Ты вообще что-то странное описываешь. Что такое параметр1 и "имя переменной для логирования"?

Представь, что у тебя есть внешняя библиотека из которой ты можешь вызвать функцию. Ты можешь передать этой функции все что угодно, и она может возвратить тоже что угодно (в том числе и функцию без параметров). Если в этих условиях твоя задача решается, то нет проблем.

Заранее даю подсказку. С помощью функций можно почти любую проблему решить .

VD>>Пользователь то тут причем? Все что ему нужно будет сделать — это создать сборку, реализовать в ней нужные функции и описать импорт сборки в проекте где он будет использовать эти функции. Это куда лучше чем изврат с динамической компиляцией, и как следствие, отсутствие нормальной отладки и интеллисенса.


FDS>Вот я вижу ограничение и чувствую сильный дискомфорт.


В чем ограничение то?

FDS>Тут даже не важно, может быть рефлексия действительно самый лучший вариант и ты прав. Но как с этим ограничением бороться, чтобы пользователь мог ввести что-нибудь, что я немного выше написал

FDS>[testCoverageMacro("имя:\файла", getSomeFunc(параметр1, параметр2, ["имя переменной для логирования", "имя переменной для логирования"], SomeEnum.типКлассаЭквивалентности))]

FDS>я не понимаю и меня это мучает, порождает неуверенность. Крайне неприятные чувства при программировании на языке, который позиционируется как самый свободный из всех языков.


Ты вкладываешь в свои примеры какую-то семантику. При этом никто кроме тебя ее не понимает. А помочь можно только поняв, что другому нужно. По сему мне трудно ответить на вой вопрос.

Для начала тебе не плохо было бы научиться доходчиво описывать свои идеи. А то половина предпосылок остается у тебя в голове. А из меня фиговый телепат.

FDS>P.S. Я так и не понял, возможно ли вообще, в принципе, каким-либо образом передать в макрос функцию так, чтобы пользователь мог работать с параметрами макроса как с обычными переменными?


Для меня это набор слов. Я не могу на него ответить. В нем не определены понятия:
1. "пользователь мог работать с параметрами макроса".
2. "обычными переменными".

Я уверен, что твои задачи решаются с помощью макров. Но не уверен, что ты правильно представляешь как нужно их для этого использовать.

Опиши свою задачу более детально, чтобы мне не приходилось догадываться. А я попробую помочь.

FDS>P.P.S. По методу Late посмотрел, но пока тупо не разобрался. Но насколько я понял, он рефлексию делает в runtime, как мне показалось по этой строке


FDS>
FDS>def call_expr(args_expr)
FDS>        <[ { $(expr_name : name) = $expr; $(expr_name : name).GetType().InvokeMember($name_expr, $flags_expr, null, $(expr_name : name), $args_expr, $pms_expr, null, $names_expr) } ]>
FDS>


FDS>Или я чего-то недосмотрел?


Да. Но у меня смутное сомнение, что late тебе вообще не нужен.

В общем, для начала четко сформулируй свою задачу. А там и решение само собой найдется.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[15]: Отступление в Unitesk
От: VladD2 Российская Империя www.nemerle.org
Дата: 23.08.11 02:47
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>Насколько я понимаю, насчёт макроса примерно так надо делать

FDS>1. Создаю макроатрибут уровня сборки

+1

FDS>2. Выдаю туда string с именем папки


+1

FDS>3. Записываю внутри макроса в некую глобальную переменную (например, статическую переменную типа) эту строку, т.к. string можно передавать в макрос как типизированный параметр, в отличие от функции или других сложных типов


Записываешь в
Manager.UserData["Некое уникальное имя"] = значение;

Это как глобальная переменная, но уникальная для каждого проекта и сбрасываемая при перепарсивании проекта.

FDS>4. Считываю из других макросов это значение


+1

FDS>Верно?


Ага. С точностью до глобальных (они же статические) переменных.

VD>>Ну, их ты сможешь подгружать из сборок.


FDS>А если их будет 30 штук, и все однотипные, которые на самом деле можно сгенерировать одной функцией с лексическим замыканием?


Значит их тела будут почти пустыми, возвращающими то самое замыкание. Ты ведь можешь просить от пользователя функцию принимающух Х параметров, и возвращающую функцию без параметров.

FDS>Быр. Хотя, в принципе, ты прав. Но хотелось бы чего-то более элегантного и, главное, [b]более близко расположенного к реальному коду,


Если тебе не жалко времени, и плевать на то что у пользователей не будет интеллисенса, то можешь компилять функции на лету.

FDS>чтобы текст этой функции потом не пришлось долго искать.


Давай им внятные имена .

FDS>А вообще, решение работающее, наверное.


100%. Как и компиляция на лету. Вопрос только в целесообразности.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[16]: Компиляция на лету
От: FDSC Россия consp11.github.io блог
Дата: 23.08.11 11:39
Оценка:
Здравствуйте, VladD2, Вы писали:

FDS>>А если их будет 30 штук, и все однотипные, которые на самом деле можно сгенерировать одной функцией с лексическим замыканием?


VD>Значит их тела будут почти пустыми, возвращающими то самое замыкание. Ты ведь можешь просить от пользователя функцию принимающух Х параметров, и возвращающую функцию без параметров.


Ну да, но неудобно десятки функций делать отдельно и потом даже с внятными именами не очень будет. Задолбаться, если честно.

VD>Если тебе не жалко времени, и плевать на то что у пользователей не будет интеллисенса, то можешь компилять функции на лету.


А как компиляцию на лету выполнять?
Re[8]: Использование сложных типов во время компиляции в пар
От: FDSC Россия consp11.github.io блог
Дата: 23.08.11 12:52
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, FDSC, Вы писали:


FDS>>Работает на реальных больших приложениях (одно из — работает на 22-х процессорном сервере в компании, имя которой вы точно знаете). Тесты, кстати, идут часами. Заказчик доволен и заказчик не один.


VD>Может тесты от того и идут часами?


От чего? От того, что селект на тестовой базе 25 секунд выполняется, а когда его написали, то и все 15 минут?

VD>Извини, окончания компиляции компилятора немерла ты не дождешся. Так что информация будет просто бесполена. Это тебе не 22 процессора не бизнес-задачки. Тут работает только один проц, а вычислений больше чем известная фирма делает за год.


Почему, все вычисления на этапе компиляции пройдут за миллисекунды.

FDS>>Дык я и хочу, чтобы он лог мог настроить прямо из макроса передав туда, если нужно, и функцию логирования


VD>Дык для этого отдельная макра есть. Зачем мешать то все одну кучу?


Это не в одну кучу, мне и нужна макра, которая логирует так, как мне нужно и то, что мне нужно. Тут никакой мешанины нет.

FDS>>Практика показывает, что ничего сокращать не нужно, а вот настроить пути покрытия как раз очень нужно.


VD>Просто у тебя однобокая практика. Поверь на слова. На вычислительных задачах это не работает.


А кто говорит, что я хочу сделать эту штуку универсальной? Я хочу повторить то, что уже работает и работает хорошо.

VD>Тут как-то мне товарищ объяснял, что можно вообще рестроспективно запоминать все изменения в программе и сделать отладчик обратного хода. Я ему так и не смог объяснить, что это работает только при небольших объемах вычислений.


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

VD>В общем, переубеждать тебя не буду. Это бесполезно. Просто когда будешь проводить тесты, то в качестве одного из тестов возьми компилятор немерла и его же тесты. Погляди как твое решение будет на нем работать.


Это бесполезно. Решение для этого не предназначено.

VD>>>У тебя и будет "комбинаторное покрытие условий". А дерево тебе по любому будет нужно, так как раскрашивать придется параллельно несколько ветвей и лишь в момент когда они разойдутся можно будет отбросить их часть. Это и есть дерево, где часть путей объединена, а ближе к листьям они расходятся.


FDS>>Ничего не понял.


VD>Плохо.


FDS>>Есть код


FDS>>
FDS>>if (условие1)
FDS>>{
FDS>>  ...
FDS>>  when (условие2)
FDS>>    ...
FDS>>}
FDS>>else
FDS>>  ....;

FDS>>when (условие3)
FDS>>  ...
FDS>>

FDS>>Чтобы по нему трассы сгенерировать, мне нужно сгенерировать рекурсивно подтрассы для каждого из условий, потом их объединить во всех возможных сочетаниях. Как это представить деревом, чтобы описать комбинаторное покрытие, честно говоря, ума не приложу.

VD>Вход у тебя один? Значит корень у нас уже есть. Далее, как ты говоришь — "нужно сгенерировать рекурсивно подтрассы для каждого из условий, потом их объединить во всех возможных сочетаниях". В итоге получаем развесистое дерево с один корнем. Что тут не понятного?


VD>Для твоего примера это что-то вроде:

VD>
VD>Корень
VD>  условие1
VD>    1...
VD>    условие1:true
VD>      2...
VD>    условие1:false
VD>      3...
VD>  условие3
VD>    4...
VD>  5...
VD>

VD>Из него можно вывести все возможные пути. Например:
VD>
VD>Корень->условие1->1...->условие1:true->2...->5...
VD>Корень->условие1->1...->условие1:true->2...->условие3->4...->5...
VD>


Это дерево, эквивалентное для нашей задачи по структуре дереву AST: конечно, AST мне понадобится и будет как-то так и упрощено, ты прав, без него никуда.

Раскрашивать-то ты как его собрался? Не получится с раскраской комбинаторное покрытие или я не понимаю, что ты имеешь в виду под раскраской.

VD>>>Ничего не естественно. Можно сгенерировать код создающий это дерево при старте приложения или отложено (при первом к нему обращении).


FDS>>А потом выгружать в файл, чтобы провести анализ покрытия.


VD>Зачем в файл? Уфф. Если у тебя есть это дерево и код инструментирован, то при прогоне кода, он будет автоматом это дерево раскрашивать. Если управление прошло по некоторому пути, то оно будет раскрашено как пройденное.


Ещё раз повторю: как только тест падает, у нас всё слетает. Значит нужно выводить в файл.

FDS>>Которая моментально убивается при падении приложения и потере всего, что было в памяти.


VD>В дотнете можно обрабатывать событие "необработанное исключение" и на него разбираться. Хотя есть ряд исключений которые не обрабатываются. Но их мало.


Их достаточно для того, чтобы не делать такой обработки. Да и обработка эта сама, насколько я помню, не позволяет многих вещей: в частности, в ней самой если исключение бросить — приложение завершиться даже при оборачивании его в try ... catch, если я, конечно, помню верно. В любом случае, это не самый удобный способ вывода.

VD>Ага. Но есть ода проблема. С такими всеобемлющими логами приложение клиенту не отдашь. Стало быть гонять с ними ты будешь только автоматические тесты.


Верно

VD> А с ними и так ясно все при падении.


Как раз очень неясно. Представь себе 3000 базовых значений, на каждом из которых один тест выполняет около тысячи манипуляций. На какой из этих манипуляций тест, собственно, упал? Это "с ними всё понятно" спокойно на несколько дней затягивалось даже с дополнительными специально введёнными по этому случаю логами.


FDS>>Файл — один на сборку, т.е. на проект.


VD>Ты его размер представляешь?


Представляю. Менее 100 кб, если всё верно применить.

VD>Сколько будет длиться анализ этого лога для вычислительных задач вроде компиляции компилятора немерла?


Вообще, в ИСП РАН тестировали функцию какой-то несложной оптимизации для интеловского компилятора C++: нормально получилось. Правда для таких задач, по-моему, Unitesk не очень.

FDS>>Я сначала думаю о расширяемости, а потом обо всём остальном, иначе когда проблема расширяемости возникнет, будет уже слишком поздно.

FDS>>В любом случае, такая проблема вполне реально может возникнуть, а вот как её решить на Nemerle я не понял. Честно говоря, даже не очень понял, почему вообще макросы это не предусматривают

VD>Давольно странное требование расширять расширение. Прикинь если бы ты потребовал от палагина к студии возможности расширять его плагинам. Наверно есть такие плагины, ну, РеШарпер, например, но это все же исключение из правил. И проблема расширения расширений — это проблема писателя этих расширений .


Для тебя — так, для меня принципиально по-другому, т.к. я сам был "писателем этих расширений" и очень не хочу лишних проблем.

VD>Ну, мы тут щи лаптем не хлебаем. Если сказали тебе можно, значит можно. Не бись!

VD>На Яве у тебя нас не было. А тут мы есть. И, если что, поможем.

Ну пока как-то не доходит. Только и говоришь, что это нефига не нужно и никогда не пригодится. Да и на Яве у меня разработчики JavaTesk были в той же комнате, что и я и в соседней, так что я к ним иногда обращался.

FDS>>А вот если взлетит, то вероятность того, что мне понадобится это расширение стремится к 100% процентам


VD>Ну, вот тогда еще раз тему поднимешь и мы тебе поможем. Не берись сразу за 10 дел.


У меня стоит задача понять, как и что расширить, а потом я возьмусь за сам код: будет другая задача. Сначала я выполняю проектирование, грубо говоря, а затем уже реализацию. Без проектирования, то бишь без понимания, что и как у меня будет, реализацию начинать смысла нет.

FDS>>(так как на Java уже требовалось) и это один из самых больших рисков. Мне совершенно не хочется тратить время на то, что потом нельзя будет расширить. К тому же мне просто интересно, как это сделать и я хочу это сделать сразу, потому что к проекту я могу и охладеть,


VD>Если тебе интересно, то можешь сделать это отдельно, в порядке эксеперементов.


Я это отдельно и делаю. Посмотри в название темы, я спросил, как вызвать функцию в макросе, а пример привёл, чтобы было понятно, зачем примерно это может понадобится, откуда задача взялась.

VD>Самый простой способ тебе описали. Компилируй свои расширения в отдельную сборку, грузи сборку через рефлексию и отображай имена функций в делегаты с нужной сигнатурой. Потом вызывай делегаты и они будут выполнять нужную тебе работу. Другой вариант — использование заранее определенных интерфейсов. Он почти ничем не отличается, кроме того что позволяет использовать не отдельные функции, а объекты с этим самым интерфейсом. Смысл же тот же самый. Грузишь сборку через рефлексию, создаешь тип по имени (через Activator), получаешь ссылку на интерфейс и дергаешь его как-будто имеешь дело с объектом скомпилированным в твоем же приложении.


С рефлексией я уже всё понял, ты мне объясни, можно ли без рефлексии, чтобы программист лишним не заморачивался.

FDS>>Считай, что я поспорил с одним из разработчиков Unitesk, что я смогу на Nemerle сделать круче, чем они на Java и C#. Задача это продвинуть в использование — это для меня дело десятое. Если получится, я буду рад.


VD>Ну, раз поспорил, то давай. Производительность в этом тесте, как я понимаю, не важна?


Производительность там вполне нормальная будет. Поверь, ты споришь не со мной, а с самой концепцией Unitesk, которая показала свою жизнеспособность как минимум на 4-х языках программирования у кучи заказчиков.

VD>Практика показывает другое. Она показывает, что когда люди начинают лепить плагины к еще не написанному софту, то очень часто они этот софт так и не доводят до рабочего состояния.


Ну если им говорить, что ничего не заработает, вместо того, чтобы объяснить, как влепить плагин, то они за дело даже не возьмутся .

VD>Как я тебе уже сказал, чтобы опять что это возможно достаточно просто провести эксперимент. Он не сложный. Часа за два справишься. Создай макрос которому передается имя сборки и имя функции. Пусть эта фукнция вовращает PExpr. Далее создай пару длл-ей с парой функций в каждой, ну, и используй это дело в тестовом приложении. Сборки грузятся через Assebmbly.LoadFrom, далее получаешь список типов, в них список статических публичных методов и преобразуешь это дело в делегаты. Код будет примерно такой: ...


VD>Как видишь, не так много кода, если знаешь что писать.


Зачем мне проводить этот эксперимент? Я и так понял, что через рефлексию это возможно, а подгрузку через рефлексию слава богу делал не раз. Но это не очень удобно для конечного пользователя. Ты предлагаешь решение, которое следует из ограниченных возможностей технологии Nemerle, а я хочу видеть то решение, которое я хочу видеть: рефлексия здесь работает, но это не совсем то.

FDS>>Стоп. Без рефлексии именно? Можно ещё раз, как?


VD>Без рефлексии динамика не делается. А как я тебе в общих чертах набросал выше. Скорость вызова будет очень выской, так как он будет не через рефлексию, а через делегат делаться. Из динамической загрузки ничего быстрее не сделаешь.


Противоречие. Раньше ты говорил, что написать макрос someMacro("", getSomeFunc(ля-ля-тополя)) вполне возможно. А сейчас оказывается, что придётся писать макрос someMacro("", "getSomeFunc_getTopolM") — это немного разные вещи. Возможно ли сделать вызов макроса как написано в первом способе или нет?

FDS>>В данном эпизоде ты сильно ошибаешься, если имеешь в виду производительность.


VD>ОК, рад буду ошибиться. Если сделаешь этого монстра, протестируй его на компиляторе. Тогда и увидим, кто был прав.


Он не предназначен для тестирования компилятора. Хотя мысль интересная: тогда посмотрим, что я был прав .

VD>Понимаю . Но хвастаться тут особо не чем. Это примитивщина. В общем, план действий я тебе орисовал. Далее сам. Если что подскажем. Можно конечно и до динамической компиляции с кэшированием докатиться, но это по-моему уже точно перебор.


Пока это не совсем то, что надо. Потому что есть someMacro("", "getSomeFunc_getTopolM"), а хочется someMacro("", getSomeFunc(ля-ля-тополя))

Что касается "похвастаться" — в том и дело, что я хочу на Nemerle сделать в одиночку то, что коллектив делал чёрт знает сколько и именно потому, что на Nemerle это может быть просто на порядок проще, если не на два. В принципе, рефлексия меня устраивает, но мне интересно, как это сделать так, чтобы выглядело someMacro("", getSomeFunc(ля-ля-тополя)). Жаль, если язык этого не позволяет

FDS>>Т.е. в идеале программист должен смочь написать следующее

FDS>>[testCoverageMacro("имя:\файла", getSomeFunc(параметр1, параметр2, ["имя переменной для логирования", "имя переменной для логирования"], SomeEnum.типКлассаЭквивалентности))]

FDS>>а макрос должен иметь возможность это вызвать и получить результат работы getSomeFunc. По сути, у меня такое впечатление, что чтобы сделать такое через рефлексию, я должен обработать второй параметр вручную по TExpr. Нет?


VD>Ты вообще что-то странное описываешь. Что такое параметр1 и "имя переменной для логирования"?


Некоторые параметры функции, ну т.е. они просто есть. А потом getSomeFunc из них даёт нам некую функцию прямо в макрос. Безо всяких лишних сборок и создания вручную аналогичных функций.

VD>Представь, что у тебя есть внешняя библиотека из которой ты можешь вызвать функцию. Ты можешь передать этой функции все что угодно, и она может возвратить тоже что угодно (в том числе и функцию без параметров). Если в этих условиях твоя задача решается, то нет проблем.


Пока я могу вызвать функцию по имени и ничего ей не передать. Т.к. чтобы ей что-то передать, мне это надо как-то загнать в параметры макроса, а загнать это не получится, исключая элементарные типы.

Меня как раз интересует, как ей передать параметры так или иначе, но чтобы это было удобно записывать в макросе.

VD>>>Пользователь то тут причем? Все что ему нужно будет сделать — это создать сборку, реализовать в ней нужные функции и описать импорт сборки в проекте где он будет использовать эти функции. Это куда лучше чем изврат с динамической компиляцией, и как следствие, отсутствие нормальной отладки и интеллисенса.


FDS>>Вот я вижу ограничение и чувствую сильный дискомфорт.


VD>В чем ограничение то?


В том, что я не могу передать в макрос ничего, кроме строки, инта и була: ни функцию не передашь, ни объект.

FDS>>Тут даже не важно, может быть рефлексия действительно самый лучший вариант и ты прав. Но как с этим ограничением бороться, чтобы пользователь мог ввести что-нибудь, что я немного выше написал

FDS>>[testCoverageMacro("имя:\файла", getSomeFunc(параметр1, параметр2, ["имя переменной для логирования", "имя переменной для логирования"], SomeEnum.типКлассаЭквивалентности))]

FDS>>я не понимаю и меня это мучает, порождает неуверенность. Крайне неприятные чувства при программировании на языке, который позиционируется как самый свободный из всех языков.


VD>Ты вкладываешь в свои примеры какую-то семантику. При этом никто кроме тебя ее не понимает. А помочь можно только поняв, что другому нужно. По сему мне трудно ответить на вой вопрос.


Да нет там никакой семантики. Просто вызывается некоторая getSomeFunc, которая возвращает нечто. Понять нужно то, что сейчас ты, по сути, утверждаешь, что так записать макрос, как я записал, невозможно (имя в виду вызов getSomeFunc на момент компиляции, чтобы она не делала).

VD>Для начала тебе не плохо было бы научиться доходчиво описывать свои идеи. А то половина предпосылок остается у тебя в голове. А из меня фиговый телепат.


Да уж, тебе бы подучиться телепатии.

FDS>>P.S. Я так и не понял, возможно ли вообще, в принципе, каким-либо образом передать в макрос функцию так, чтобы пользователь мог работать с параметрами макроса как с обычными переменными?


VD>Для меня это набор слов. Я не могу на него ответить. В нем не определены понятия:

VD>1. "пользователь мог работать с параметрами макроса".

По-моему довольно ясно писал. Ты предлагаешь пользователю сделать вызов макроса так: someMacro("имя_функции_в_другой_сборке_без_параметров"), и объявнить в другой сборке функцию имя_функции_в_другой_сборке_без_параметров, которая, скажем, просто вызывает getSomeFunc(некоторыеПараметры) и возвращает её результат.

Сейчас пользователь фактически не может работать с параметрами макроса, т.к. он может туда передать совсем не то, что ему нужно.
А нужно ему передать туда someMacro(getSomeFunc(некоторыеПараметры)) и это большая разница.

VD>2. "обычными переменными".


То же самое, не очень приятное ограничение, что я не могу сделать так someMacro(["хочу-эту-строку", "эту-тоже-хочу"]) и использовать затем этот список там, где мне нужно без разбора PExpr или TExpr, просто как список параметров работы макроса, а не как некий код, с которым макрос работает. Например, компилятор передаёт в макроатрибуты параметры сложных типов, например, TypeBuilder, а я этого сделать не могу. Грубо говоря, нужен некий макрос, с которым можно будет написать executeCompilertime(def a = 1; "a = " + (a + 8)) и он вернёт во время компиляции строку "a = 9".

VD>Я уверен, что твои задачи решаются с помощью макров. Но не уверен, что ты правильно представляешь как нужно их для этого использовать.


Решаются, это я уже понял, только вот неудобно.

VD>Опиши свою задачу более детально, чтобы мне не приходилось догадываться. А я попробую помочь.


Честно говоря, я не очень понимаю, что здесь непонятного. Хочу иметь возможность передавать макросам параметры как в виде PExpr, так и в виде сложных типов, в т.ч. функций, с которыми я буду работать на этапе компиляции. Пусть даже внутри не смогут использоваться те типы, которые ещё не скомпилировались.

VD>Да. Но у меня смутное сомнение, что late тебе вообще не нужен.


Ты просто писал об этом макросе.

VD>В общем, для начала четко сформулируй свою задачу. А там и решение само собой найдется.


То, что немного выше — это чётко?
Re[17]: Компиляция на лету
От: para  
Дата: 23.08.11 15:40
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>А как компиляцию на лету выполнять?


полу-вариант в лоб:
скопировать нужные функции в отдельный файл и отдельно его скомпилировать через командную строку
это конечно фиговый вариант но работать будет... не во всех случаях)
— сложная обработка ошибок, — плохо с Интели сенносом

тут главная проблема найти все зависимые функции и определить, что они не зависят от текущего макроса иначе сам понимаешь...

если хочешь действительно ощутить круть Немерли, можешь даже попробовать сделать свой ДСЛ (На пег-парсере) для анализа целевой программы...

Вариант 2: компилировать в два прохода
1) исключить твой макрос и скомпилировать основу
2) скопировать скомпиленную длл в obj подключить её, связать и скомпилить по настоящему

компилятор как сервис — тут я не помогу
Re[17]: Компиляция на лету
От: Ziaw Россия  
Дата: 23.08.11 17:23
Оценка:
Здравствуйте, FDSC, Вы писали:

FDS>А как компиляцию на лету выполнять?


Можно вызвать немерловый компилятор дав ему исходники в файлах: https://github.com/Ziaw/nor/blob/master/NRails.Console/MigrationCompiler.n

А вообще давно есть фичреквест на компиляцию PExpr, только никому пока не нужно особо было.
Re[18]: Компиляция на лету
От: VladD2 Российская Империя www.nemerle.org
Дата: 23.08.11 18:03
Оценка:
Здравствуйте, Ziaw, Вы писали:

FDS>>А как компиляцию на лету выполнять?


Z>Можно вызвать немерловый компилятор дав ему исходники в файлах: https://github.com/Ziaw/nor/blob/master/NRails.Console/MigrationCompiler.n


Z>А вообще давно есть фичреквест на компиляцию PExpr, только никому пока не нужно особо было.


Ну, так занялись бы. Это и полезно, и интересно.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.