Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > Тестовая система аля topcoder


Автор: Platon 23.10.2007, 20:01
Здравствуйте, уважаемые.

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

Всем наверно уже известно, что я делаю робкие попытки сделать тестовую систему.
Встали пара вопросов.
1. Использовать в своей системе JUnit, который есть не только на Java, но и на других языках. 1 вопрос, хочется проконсультироваться может ли JUnit манипулировать временем и объемом выделяемой памяти процессу. Или нет смысла? Лучше делать по программе на тест.

2. Конвертируемость тестовых данных. Т.е. думается сделать удобную ГАИшку, в которой тыкая можно выбирать число входных параметров, их тип, а затем набор данных по ним и эталонный результат, нуу, может + еще погрешность. В результате получается xml на основе которого получаем 4 тестирующих программы (на Java, C++, C#, VB) Проблема встала с массивами, как в xml описать массив, чтоб по записи можно было понять что это массив такого-то типа такой-то размерности и с такими-то данными.

Автор: powerOn 23.10.2007, 22:02
Цитата(Platon @  23.10.2007,  21:01 Найти цитируемый пост)
Проблема встала с массивами, как в xml описать массив, чтоб по записи можно было понять что это массив такого-то типа такой-то размерности и с такими-то данными. 


Код

<root>
    <myarray type="sometype">
        <element>1</element>
        <element>2</element>
        <element>3</element>
        <element>4</element>
        <element>5</element>
    </myarray>
</root>



Автор: nornad 24.10.2007, 02:52
Также атрибутом можно указать и то, что тэг содержит массив. В примере powerOn я бы сделал так (если надо именно указать, что "это - массив"):
Код

<myarray vartype="array" elemtype="number">

Автор: Platon 26.10.2007, 12:13
А размерность массива как делать? что-то типа

Код

<argument vartype="array" elemtype="number">
    <element>
        <element>11</element>
        <element>12</element>
        <element>13</element>
    </element>
    <element>
        <element>21</element>
        <element>22</element>
        <element>23</element>
    </element>
</argument>

?

Автор: alex_nevolin 26.10.2007, 12:39
Цитата(Platon @  23.10.2007,  20:01 Найти цитируемый пост)
1 вопрос, хочется проконсультироваться может ли JUnit манипулировать временем и объемом выделяемой памяти процессу. 
 Что имеется ввиду?
JUnit тесты - это те-же java классы просто подчиняющиеся определеным правилам. Т.е. если есть возможность в принципе манипулировать временем и объемом выделяемой памяти процессу, то это не составит труда реализовать в тесте. 

Автор: Platon 12.12.2007, 13:56
Так, вернемся к нашим баранам ^_^
Запустил я эту тему, так и не начав...

В общем, у меня разработана кое какая модель по генерации шаблонных тестеров. Возник вопрос по velocity... я видимо слабоват в этом направлении.
У меня получилось следующее:
Описание классов и тестовых решений в xml
Код

<class-descriptor>
    <className>Platon</className>
    <methodName>getArgumentus</methodName>
    <arg num="1" type="array">
        <param type="string" depth="1" />
    </arg>
    <arg num="2" type="string" />
    <arg num="3" type="int" />
    <arg num="4" type="long" />
</class-descriptor>

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

<test-case>
    <case-num>1</case-num>
    <arg num="1">{"Data", "coolMan!!!"}</arg>
    <answer>"+Data+coolMan!!!+"</answer>
</test-case>

case-num - номер тестового семпла
num - номер аргумента
answer - ответ

Теперь по коду (обращайте внимание в основном на velocity и на обслуживающие классы (ClassData, MethodArgument, ArrayMethodArgument)):
Код шаблона:
Код

#macro (type $t)
#if ($t.equals("string"))
String
#elseif ($t.equals("int"))
int
#elseif ($t.equals("long"))
long
#elseif ($t.equals("double"))
double
#elseif ($t.equals("float"))
float
#end
#end

#macro (createType $t)
#if ($t.type.equals("array"))
    #type($t.param.type)
    #foreach($i in $t.type)
        []
    #end
#else
    #type($t.type)
#end
#end

#macro (generate $i)
#if ($classData.args.get($i).type.equals("string"))
c.get($i).getText()
#elseif ($classData.args.get($i).type.equals("int"))
Integer.parseInt(c.get($i).getText())
#elseif ($classData.args.get($i).type.equals("long"))
Long.parseLong(c.get($i).getText())
#elseif ($classData.args.get($i).type.equals("double"))
Double.parseDouble(c.get($i).getText())
#elseif ($classData.args.get($i).type.equals("float"))
Float.parseFloat(c.get($i).getText())
#end
#end

#macro (generateValue $i)
#if ($classData.args.get($i).type.equals("array"))
new #createType($classData.args.get($i))
() c.get($i).getText();
#end
#end
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import java.util.List;
import java.io.IOException;

public class Tester {

    public static final int OK = 0;
    public static final int TEST_ERROR_BASE = 1000;

    public static void main(String[] args) throws Exception {
        testCase(Integer.parseInt(args[0]), loadCase(Integer.parseInt(args[0])));
    }

    private static void testCase(int i, Test test) {
        System.setSecurityManager(null);
        if (!asseertEquals(new $className().$methodName(
            #foreach ($d in [1 .. $classData.args.size()])
                #if ($d != 1)
                    ,
                #end
                test.arg$d
            #end
        )))
            System.exit(TEST_ERROR_BASE + i);
        System.exit(OK);
    }

    private static Test loadCase(int testCase) throws IOException, JDOMException {
        Test t = new Test();
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(getTestcasePath(testCase));
        List<Element> c = doc.getRootElement().getChildren("arg");
        #foreach($d in [0..$classData.args.size()])
            #set($am = $d + 1)
            t.arg$am = #generateValue($d)
        #end
        return t;
    }

    private boolean assertEquals(int a, int b) {
        return a == b;
    }

    private static class Test {
    #foreach($d in [0..$agrsAmount])
        #set ($am = $d + 1)
        public #createType($classData.args.get($d)) arg$am;
    #end
    }
}

Обслуживающие классы:
Код

public class ClassData {
    public String className;
    public String methodName;
    public Vector<MethodArgument> args;
}

public class MethodArgument {
    public String type;

    public MethodArgument(String type) {
        this.type = type;
    }

    public MethodArgument() {
    }
}


public class TesterGenerator {

    public String generateTemplate(String path) throws Exception {
        VelocityEngine velocity = new VelocityEngine();
        velocity.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, System.getProperty("user.dir"));
        velocity.init();
        VelocityContext c = new VelocityContext();
        ClassData data = loadClassData(path);
        c.put("classData", data);
        c.put("className", data.className);
        c.put("methodName", data.methodName);

        StringWriter writer = new StringWriter(2500);
        Template t = velocity.getTemplate("Tester.vm");
        t.merge(c, writer);
        return writer.toString();
    }

    private ClassData loadClassData(String path) throws IOException, JDOMException {
        SAXBuilder b = new SAXBuilder();
        Element d = b.build(new FileInputStream(path)).getRootElement();
        ClassData res = new ClassData();
        res.className = d.getChild("className").getText();
        res.methodName = d.getChild("methodName").getText();
        List<Element> elements =  d.getChildren("arg");
        res.args = new Vector<MethodArgument>();
        res.args.ensureCapacity(elements.size());
        for (Element e : elements)
            res.args.add(e.getAttribute("num").getIntValue() - 1, buildNode(e));
        return res;
    }

    private MethodArgument buildNode(Element e) throws DataConversionException {
        if (e.getAttribute("type").getValue().equals("array")) {
            Element s = e.getChild("param");
            return new ArrayMethodArgument("array", s.getAttribute("type").getValue(), s.getAttribute("depth").getIntValue());
        }
        return new MethodArgument(e.getAttribute("type").getValue());
    }

    public static void main(String[] args) throws Exception {
        String s = new TesterGenerator().generateTemplate("class-descriptor.xml");
        System.out.println(s);
    }
}


Информер пишет:
Цитата

SEVERE: Right side of range operator [n..m] has null value. Operation not possible. Tester.vm [line 65, column 41]

проверил на вывод $classData - работает, $classData.args - не работает
Подумал нужны getter/setter методы, сделал, но пишет исключение:
Цитата

SEVERE: Method get threw exception for reference $classData in template Tester.vm at  [1,6]
Exception in thread "main" org.apache.velocity.exception.MethodInvocationException: Invocation of method 'get' in  class java.util.Vector threw exception java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 4 @ Tester.vm[1,22]

Черт ногу сломит, у меня в [1,22] только объявления макрокоманд, в чем может быть подвох?

Автор: Platon 12.12.2007, 14:12
И еще доп вопрос, может я глупо сделал и решения не нашел правильного, сделал свой вариант синхронного вывода. Как не странно это звучит ^_^ люди мучаются с асинхронным, а я с синхронным, в общем я решил, что нече клиенту в разнобой слать запросы пусть шлет их последовательно в основном потоке (специфика такая)
Фича очень похожая на RMI

Примерчик:
Код

public interface AuthBridge {
    public AuthResponse authenticate(String login, String password);
}

Клиентская реализация
Код

public class AuthHandler implements AuthBridge, MessageHandler<AuthMessage> {
    private final IoSession connection;
    private ResponseWaitor<AuthResponse> authResp = new ResponseWaitor<AuthResponse>();
    public AuthHandler(IoSession connection) {
        this.connection = connection;
    }

    public AuthResponse authenticate(String login, String password) {
        connection.write(new AuthRequest(login, password));
        return authResp.getObject();
    }


    public void messageReceived(IoSession ioSession, AuthMessage authMessage) throws Exception {
        if (authMessage instanceof AuthResponse) {
            authResp.setObject((AuthResponse)authMessage);
        }
    }
}

Сервернная реализация:
Код

public class AuthHandler implements MessageHandler<AuthMessage> {

    public void messageReceived(IoSession ioSession, AuthMessage authMessage) throws Exception {
        if (authMessage instanceof AuthRequest) {
            final AuthRequest ar = (AuthRequest)authMessage;
            final AuthImpl impl = new AuthImpl(ioSession);
            ioSession.write(impl.authenticate(ar.login, ar.password));
        }
    }

    private static class AuthImpl implements AuthBridge {

        IoSession session;

        public AuthImpl(IoSession session) {
            this.session = session;
        }

        public AuthResponse authenticate(String login, String password) {
            return null;
        }
    }
}

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

А, да, самое главное, вот класс, который ожидает приема объекта, и сразу будет поток
Код

public class ResponseWaitor<E> {
    private E resp;

    public synchronized void setObject(E resp) {
        this.resp = resp;
        notify();
    }

    public boolean isDone() {
        return resp != null;
    }

    public E getObject() {
        while(!isDone()) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return resp;
    }
}


Оцените, и покритикуйте.

Автор: Kangaroo 12.12.2007, 14:13
Platon, 
нужны геттер/сеттеры, потому что Velocity их использует. Верни их обратно smile

Цитата(Platon @  12.12.2007,  12:56 Найти цитируемый пост)
Подумал нужны getter/setter методы, сделал, но пишет исключение:

Цитата    

SEVERE: Method get threw exception for reference $classData in template Tester.vm at  [1,6]
Exception in thread "main" org.apache.velocity.exception.MethodInvocationException: Invocation of method 'get' in  class java.util.Vector threw exception java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 4 @ Tester.vm[1,22]
    
Черт ногу сломит, у меня в [1,22] только объявления макрокоманд, в чем может быть подвох?

Это ж обычный ArrayIndexOutOfBoundsException. Вот в этом куске кода:
Код

#foreach ($d in [1 .. $classData.args.size()])    
                #if ($d != 1)    
                    ,    
                #end    
                test.arg$d    
            #end

У тебя $d от 1 до размера вектора, а нужно от 0 до РАЗМЕР_ВЕКТОРА-1

Автор: Platon 12.12.2007, 14:17
Поясню некоторые моменты:
мы выполняем простую процедуру авторицации/аутентификации (точно не уверен,  что тут правильно)
Клиент пишет в сокет сервера запрос, и ставит себя в ожидание
Сервер принимает этот запрос и направляет его в своем реализованном интерфейсе AuthBridge, и сразу же отсылает ответ.
Клиентская реализация получает ответ, пробуждает поток и продолжает победное шествие.


Автор: Platon 12.12.2007, 16:09
Kangaroo, ууух, не успел запостить следующее сообщение, уже ответил! А я сижу жду ответа.

Цитата(Kangaroo @  12.12.2007,  15:13 Найти цитируемый пост)
Черт ногу сломит, у меня в [1,22] только объявления макрокоманд, в чем может быть подвох?

А как насчет того что не в ту строку указывает?

Автор: Kangaroo 12.12.2007, 16:19
Цитата(Platon @  12.12.2007,  15:09 Найти цитируемый пост)
А как насчет того что не в ту строку указывает?

Это фишка Velocity - ошибки не там показывать ))..  у меня пока не доходили руки, чтобы разобраться. Сам сталкиваюсь с этим.

Автор: Platon 12.12.2007, 17:15
Да ^_^ мне реально помогло , но мне не понравилось, что выход такой некозистый получился.
Пришлось написать всё в 1 строчку... чтоб было так как я хочу.
Цитата

new $className().$methodName(#foreach ($d in [1 .. $classData.args.size()])#if ($d != 1),#end test.arg$d#end))

читать такое тяжеловато.

Автор: Platon 12.12.2007, 21:09
Еще бы хотел спросить по поводу внутреннего устройства системы.
Я вижу внутренности системы так, распределенная на несколько компов.
1. Сервер, с ним идет основное общение клиентов.
2. Тестер, их несколько, коннектятся к серверу и ждут, когда он скажет им проверить ту или иную задачу.
3. Файловый сервер условий задач, тут хранятся все файлы задач.
4. Файловый сервер решенных пользователями задач.
5. База данных

насчет 3-4 я думаю, может объеденить?
А также возникают вопросы как лкчше всего эти пункты наладить между собой.
Я прикинул так, что сервер, при запросе условия задачи,  может перенаправить пользователя на файловый сервер и скачать задачу прямо с него, тогда возникает проблема тоог, что файловый сервер должен быть не просто ФС, но также минимально фильтровать тех, кто к нему коннектиться, нельзя выдавать задания раньше, чем назначено соревнование.
Сервер при взаимодействии с клиентом, формирует на Файл сервере решенных задач отправленные пользователем задачи.
При запросе от сервера к тестеру проверить определенную задачу, тестер скачивает с этого файл сервера необходимую задачу.

В итоге, получается хоть локальное взаимодействие, но внутренний трафик мыслится гигантским.
Тут очередной каскад вопросов связанных с кешированием данных. Целесообразно ли на тестере кешировать к примеру 10 последних решенных задач, если очень высока вероятность их повторного (2-кратного, 3х, 4х, 50х) запроса. Или это не так существенно и только нагрузит систему?

Что тут может не понравиться? Жду критики.

Добавлено через 1 минуту
Насчет http://forum.vingrad.ru/index.php?showtopic=178554&view=findpost&p=1346121 кто, что может сказать?

Автор: Platon 14.12.2007, 21:35
Пришел к тому, что это лишняя морока разделять основной сервер от набора задач и набора решенных заданий, получается все эти 3 компонента будут в 1-й серверной программе.

Причина по которой я не смог разделить эти компоненты: 
Рассмотрим предполагаемый алгоритм, если клиент выбирает задачу, конектясь к серверу списка задач а не к главному серверу:
-- коннект к серверу списка задач(ССЗ), запрос конкретной задачи
  -- ССЗ коннектится к главному серверу (ГС), чтобы спросить можно ли выдавать задачу клиенту. (вот тут то и загвоздка)

Если сервер интегрирован, т.е. все 3 компонента в 1-й серверной программе, то проверку доступности организовать не составит труда, иначе получается, что каждый раз придется дергать БД, чтоб установить подключаемую личность, это видится мне как накладный расход.

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

Слишком много вопросов и мало ответов :(

Автор: Platon 14.12.2007, 23:01
Может мне посмотреть в сторону кластеров (группы серверов)?
По вики, выглядит очень заманчиво smile необходимо только реализовать или использовать готовый кластерный менеджер.
Что можете посоветовать по этому поводу? Есть готовые менеджеры? Может это уже удел J2EE? хотя хотелось бы без него обойтись smile

Автор: Platon 15.12.2007, 09:31
На самом деле кластеризация это то, что надо.
Получается, теперь можно строить такую схему

Цитата

К-+КС-+--------+     +------Т
  +КМ |-ССЗ    |-ТС--|
К-+КС-+--------+     +------Т

                    
К-клиент
КМ - кластерный менеджер, к конотому первоначально коннектятся клиенты
КС-Кластерный сервер
ССЗ - сервер списка задач
ТС - получается новый компонент, тестовый диспетчер.
Т -тестовый работник, коннектится к ТС, с целью получения работы (запуска задачи пользователя)

Автор: ivg 15.12.2007, 09:44
Не просёк, это система предназначенная для тестирования людей или ПО. Если ПО, к чему относится аля topcoder, если людей то при чём тут JUnit.
Видимо всё таки речь идёт о ПО? Просто уточнить.
Поскольку тема топика, в основном касается архитектуры будущей системы, давайте начнем с описания use cases, а то из-за деревьев леса не видно  smile 

Автор: Platon 16.12.2007, 15:45
JUnit я просто подумал в качестве тестера, но вскоре понял что он тут не при чем...

ivg, на самом деле было бы клёво, если бы вы помогли мне научиться писать Use Case, Sequence и Classes Diagrams.

Кстати, я совсем туплю, система кодеров вот она: http://topcoder.com/tc если кому интересно зайдите и посмотрите, что я имею ввиду под системой тестирования. На самом деле это соревнования по спортивному программированию.

Знаете, безвылазно 2-й день парюсь над писанием кода. попробовал сначала закодить сходу, не получилось, потом 2-й раз попробовал, подумал о кластеризации, переделываю 3-й раз.

Да, в этом деле без проектировки ну никак не обойтись ^_^

В каком формате эти диаграммы писать будем? Картинками или с помощью единой программы?

Автор: Platon 16.12.2007, 16:21
Да при подходе к кластерной задаче выяснилось, что кластерным серверам так или иначе придется общаться между собой(?) если к примеру это чат. Опять усложнение системы :(

Автор: Platon 16.12.2007, 17:20
Я тут накидал фигульку, не знаю правильно-неправильно, но по сути, она просто с круглешками и с подписью ролей.
http://ipicture.ru/Gallery/Viewfull/190368.html

Автор: ivg 16.12.2007, 21:55
Кажется я понял, что вы хотите сделать. Попробую сформулировать, для тех кто может, как и я не понял с начала:
Система для оценки (тестирование, по моему тут неоднозначный термин) кода (программ, модулей, единиц компиляции и т. п.), написанного её пользователями, с целью определения оптимального решения из множества решений конкретного задания.
Поправьте если не так.
Цитата(Platon @  16.12.2007,  15:45 Найти цитируемый пост)
если бы вы помогли мне научиться писать Use Case, Sequence и Classes Diagrams

Я могу помочь, только в качестве "второй головы" (ну это там где две головы лучше). Я не архитектор, и подобного опыта у меня нет. Вот если бы, кто нибудь из здешних архитекторов преподал на этом примере мастер-класс, это 
Цитата(Platon @  16.12.2007,  15:45 Найти цитируемый пост)
на самом деле было бы клёво

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

Автор: AlexeyVorotnikov 17.12.2007, 11:28
Я не понимаю, зачем нужен кластер? На систему планируется мега-нагрузка, с которой не справиться один сервер?

Автор: Platon 17.12.2007, 20:36
AlexeyVorotnikov, на самом деле планируется. Дело в том, что это онлайн тестирование в определенный временной промежуток, т.е. к примеру около 2500 челов одновременно тыкаются по клиенту, отсылая туеву кучу запросов серверам. Я не знаю как это называется научно, ну что то типа часов пик. 
В общем, это не сайт, где нагрузка распределяется равномерно.
+ На самом деле хотелось бы получить опыт программирования подобной системы.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)