На настоящий момент несколько конкретизировалось понимание работы системы.
Исходный код программы, подготовленный студентом, является входными данными для проверяющей системы. Сама проверка состоит из двух этапов: подготовительного и основного. На первом этапе выполняется подготовка программы к проверке. В самом простом случае этот этап представляет собой компиляцию решения в исполняемый файл, или просто сохранение исходного текста в виде файла на жестком диске (для интерпретируемых языков). В более сложных вариантах на этом этапе возможно построение различных моделей проверяемой программы. Эти модели могут быть использованы системой для определения разнообразных характеристик проверяемой программы. Главным результатом этапа подготовки является возможность тестировать решение студента.
Далее осуществляется валидация решения на одном или нескольких тестах. Под валидацией здесь понимается применение к подготовленному на предыдущем этапе решению студента некоторой формальной вычисли-тельной процедуры с булевым результатом V(Input,Output), где Input – на-бор параметров для генерации тестовых исходных данных, на которых будет осуществляться валидация решения, Output – набор параметров для проверки корректности результата, выданного решением студента.
В простейшем случае входные параметры Input могут быть без из-менения поданы на вход студенческой программы, а результат ее работы сверен на полное совпадение с выходными параметрами Output.
В более сложных вариантах реализации валидаторы могут в качестве результата выводить пояснения по результатам проверки. Вообще гово-ря валидаторы могут выполнять ряд задач, на первый взгляд, им не свойст-венных. Например, осуществлять поиск плагиата, выполнять сложную оценку внутреннего и внешнего качества решений. на основе вычисления каких-нибудь метрических показателей.
Архитектура предлагаемого решения. Физическая архитектура системы состоит из четырех узлов. За основу была взята классическая трехзвенная архитектура: тонкий клиент, web-сервер и СУБД. Дополни-тельным узлом в системе является сервер проверки. Непосредственно пользователь (преподаватель или студент) работает с системой через web-браузер. Все программы студентов, отосланные на проверку, сохраняются в БД. Сервер проверки решений периодически опрашивает web-сервер с целью получения новых программ, которые необходимо проверить, после чего осуществляет подготовку и валидацию решения.
В настоящее время, прототип системы реализован с использованием средств и инструментов платформы Microsoft.NET. Для реализации ви-зуальной web-части использовалась технология ASP.NET MVC, для реализации web-сервисов технология Windows Communication Foundation. В ка-честве СУБД использовалась Microsoft SQL Server 2008.
Интерес для рассмотрения представляет логическая архитектура для построения валидаторов программ. Валидатор в прототипе системы представлен в виде класса, реализующего интерфейс IValidator. Класс должен реализовать два метода: BeforeRun() и Validate(). Метод BeforeRun() должен осуществлять генерацию эталонных результатов, а метод Validate() осуществляет сравнение эталонных и полученных после за-пуска программы результатов. Запуск этих методов осуществляет система.
Для запуска программы пользователя определен интерфейс IRunner, единственным методом в котором является метод Run(). В классе-валидаторе должно быть определено свойство типа IRunner. Сам запуск осуществляет система независимо от валидатора. Дополнительно в системе определен интерфейс IPreparator с единственным методом Prepare. На-значение этого метода — осуществление дополнительной подготовки пе-ред запуском валидатора (например, именно этот метод может строить упомянутые выше модели).
В качестве примера можно привести реализацию простейшего ва-лидатора, осуществляющего проверку задач на тему сортировки.
Код:
public class IntegerSortValidator : IValidator
{ public IRunner Runner { get; set; }
private int[] ResultArray { get; set; } // -- эталонный результат
public void BeforeRun(TestInfo testInfo)
{ // -- Получение параметров автогенерации тестового файла
var parameters = testInfo.InputParams.Split(' ');
int N = Int32.Parse(parameters[0]); int seed = Int32.Parse(parameters[1]);
int maxValue = Int32.Parse(parameters[2]);
// -- Генерация массива случайных чисел
var rnd = new Random(seed); var inputarray = new int[N];
for (int i = 0; i < N; i++) inputarray[i] = rnd.Next(maxValue);
// -- Создание входного тестового файла
var infile = new StreamWriter("input.txt", false);
infile.WriteLine(N);
foreach (var integer in inputarray) infile.Write("{0} ", integer);
infile.WriteLine(); infile.Close();
// -- Сортировка сгенерированного массива и сохранение для проверки
Array.Sort(inputarray); ResultArray = inputarray;
}
public bool Validate(out string comment)
{ var outfile = new StreamReader("output.txt");
var resultStrings = outfile.ReadToEnd().Split(' '); outfile.Close();
int counter = 0;
foreach (var resultString in resultStrings)
{ if (resultString != String.Empty)
{ if (ResultArray[counter] != Int32.Parse(resultString))
{ comment = String.Format("Ошибка на позиции {0}: ожидалось {1},
а на самом деле {2}", counter, ResultArray[counter], resultString);
return false;
}
counter++;
}
}
if (counter != ResultArray.Length)
{ comment = String.Format("Количество чисел в отсортированном массиве
неправильное: ожидалось {0}, а на самом деле {1}", ResultArray.Length, counter);
return false;
}
comment = "Все Ok!";
return true;
} // -- конец валидатора
} // -- конец класса
Метод BeforeRun() запускается перед запуском проверяемой про-граммы и генерирует массив из N случайных чисел, стартовым значением является seed, максимальным — maxValue. Массив записывается в файл input.txt и является входным для проверяемой программы сортировки. Кроме того, массив сортируется и сохраняется в поле класса-валидатора ResultArray.
Метод Validate() запускается после выполнения проверяемой про-граммы и сравнивает полученный при выполнении программы сортиро-ванный файл output.txt с сортированным массивом ResultArray. По результатам проверки пользователю выдается простейшее сообщение.
В настоящее время мне почти ясны почти все детали реализации в рамках ББ, кроме взаимодействия клиент-сервер. В этом япока не только в ББ, но и вообще чайник.
О макросах я не зря пытал. Поскольку, как вы видите, тут все - call-backи. Для трансляции полученной от студента программы, нужно, чтобы она была оформлена специальным образом: получала данные из отдельного текста с определеннным именем, и результат писала либо в лог, либо в отдельный текст с заданным именем. Кроме того, вызываемый модуль и вызываемая процедура должны называться специальным именем, например Student.Do (как вы понимаете в додиезе имя определено прямо в языке - main).
Система должна поместить полученный модуль в строку (? - посмотрю пример Ермакова) и вызвать компилятор для трансляции.
Интерфейсы делаем с помощью абстрактных записей в одном модуле.
Проверяющая система должна методы означенных интерфейсов.
В методе Run() должен быть прописан вызов пользовательской процедуры.
Вот.