Giter Site home page Giter Site logo

dodona-edu / universal-judge Goto Github PK

View Code? Open in Web Editor NEW
9.0 8.0 4.0 91.29 MB

Universal judge for educational software testing

Home Page: https://docs.dodona.be/en/tested

License: MIT License

Python 80.58% Shell 0.11% Java 2.50% JavaScript 1.12% Haskell 9.44% C 1.63% Kotlin 1.94% C# 1.81% Smalltalk 0.01% Nix 0.53% Jinja 0.34%
dodona educational-software judge

universal-judge's Introduction

TESTed: universal judge for educational software testing

TESTed is a software test framework to evaluate submissions for programming exercises across multiple programming languages, using a single test suite per exercise.

TESTed is developed by Team Dodona at Ghent University. If you use this software in research, please cite:

  • Strijbol, N., Van Petegem, C., Maertens, R., Sels, B., Scholliers, C., Dawyndt, P., & Mesuere, B. (2023). TESTed—An educational testing framework with language-agnostic test suites for programming exercises. SoftwareX, 22, 101404. doi:10.1016/j.softx.2023.101404

Important

The documentation below is intended for running TESTed as a standalone tool. If you are looking to create exercises for Dodona, we have more suitable documentation available.

Installing TESTed

TESTed is implemented in Python, but has various dependencies for its language-specific modules. We only use the Python language module in this README, but see dependencies.md for an overview of dependencies for each of the supported programming languages.

Install Python 3.11 or later (including pip). Next, clone the TESTed repository and open a command prompt in the cloned repository. TESTed uses pipenv to manage its Python dependencies. Now you can run the following commands to install them:

# Pipenv install
$ pip install pipenv --user
# Install dependencies (include --dev for tests/development)
$ pipenv sync --dev
# Activate the virtualenv
$ pipenv shell

Running TESTed

TESTed evaluates a submission for a programming exercise based on a test suite that specifies some test cases for the exercise. In what follows, we guide you through the configuration of a simple programming exercise and running TESTed to evaluate a submission using the test suite of the exercise. The directory ./exercise/ in the root directory of TESTed contains some more examples of programming exercises with test suites for TESTed.

1. Create an exercise

Let's configure a simple programming exercise that asks to implement a function echo. The function takes a single argument and returns its argument.

Start creating a directory for the configuration of the exercise. To keep things simple, we add the exercise to the exercise subdirectory in the root directory of TESTed.

mkdir exercise/simple-example

Note that you would normally not store your exercises in the TESTed repository. We recommend creating a new repository for your exercises.

2. Create a test suite

The next step is to design a test suite that will be used to evaluate submission for the exercise. Again, to keep things simple, we will only include a single test case in the test suite.

- tab: Echo
  testcases:
    - expression: "echo('input-1')"
      return: "input-1"

This test suite describes the following tests: we have one tab, which is named Echo. Inside this tab, there is one test case, in which we call the function echo with the string argument "input-1". The expected output is a return value (again a string) of "input-1". All other tests use the defaults: for example, no output is allowed on stderr, while stdout is ignored.

Put the file containing the test suite in the following location:

# Create the file
$ touch exercise/simple-example/suite.yaml
# Now you should put the content from above in the file.

3. Create some submissions

Now create two Python submissions for the programming exercise. The first one contains a correct solution, and the second one returns the wrong result.

$ cat exercise/simple-example/correct.py
def echo(argument):
  return argument
$ cat exercise/simple-example/wrong.py
def echo(argument):
  # Oops, this is wrong.
  return argument * 2

4. Evaluate the submissions

To evaluate a submission with TESTed, you need to provide a test suite and configuration information. This information can be piped to TESTed via stdin, but to make things easier, we will add the information to a configuration file in the directory of the exercise. In practice, this configuration file would be created by the learning environment in which TESTed is integrated.

$ cat exercise/simple-example/config.json
{
  "programming_language": "python",
  "natural_language": "en",
  "resources": "exercise/simple-example/",
  "source": "exercise/simple-example/correct.py",
  "judge": ".",
  "workdir": "workdir/",
  "test_suite": "suite.yaml",
  "memory_limit": 536870912,
  "time_limit": 60
}

These attributes are used by TESTed:

  • programming_language: programming language of the submission
  • resources: path of a directory with resources TESTed can use
  • source: path of the submission that must be evaluated
  • judge: path of the root directory of TESTEd
  • workdir: path of a temporary directory (see below)
  • test_suite: path of the test suite, relative to the resources directory (as defined above)

Before evaluating a submission, TESTed generates test code in the workdir. Create that directory:

$ mkdir workdir/

The content in this directory stays in place after TESTed finishes its evaluation, so you can inspect the generated test code. Before running TESTed again, you'll need to clear this directory.

With this command, TESTed will evaluate the submission and generate feedback on stdout.

$ python -m tested -c exercise/simple-example/config.json
{"command": "start-judgement"}
{"title": "Echo", "command": "start-tab"}
{"command": "start-context"}
{"description": {"description": "echo('input-1')", "format": "python"}, "command": "start-testcase"}
{"expected": "input-1", "channel": "return (String)", "command": "start-test"}
{"generated": "input-1", "status": {"enum": "correct"}, "command": "close-test"}
{"command": "close-testcase"}
{"command": "close-context"}
{"command": "close-tab"}
{"command": "close-judgement"}

By default, TESTed generates its feedback on stdout. The feedback is formatted in the JSON Lines text format, meaning that each line contains a JSON object. Here's how you get an overview of all options supported by TESTed:

$ python -m tested --help
usage: __main__.py [-h] [-c CONFIG] [-o OUTPUT] [-v]

The programming-language-agnostic educational test framework.

optional arguments:
  -h, --help            show this help message and exit
  -c CONFIG, --config CONFIG
                        Where to read the config from
  -o OUTPUT, --output OUTPUT
                        Where the judge output should be written to.
  -v, --verbose         Include verbose logs. It is recommended to also use -o in this case.

Adjust the configuration file if you want to evaluate the wrong submission.

For reference, the file tested/dsl/schema.json contains the JSON Schema of the test suite format.

Running TESTed locally

The python -m tested command is intended for production use. However, it is not always convenient to create a config.json file for each exercise to run.

TESTed supports two ways of running TESTed without a config file. The first way is:

# Run a hard-coded exercise with logs enabled, useful for debugging
$ python -m tested.manual

This command is useful when debugging TESTed itself or a particularly challenging exercise. It will execute a hardcoded config, which is set in tested/manual.py.

The second way is:

# Run an exercise with CLI paramaters
$ python -m tested.cli --help
usage: cli.py [-h] -e EXERCISE [-s SUBMISSION] [-t TESTSUITE] [-f] [-v] [-d] [-p PROGRAMMING_LANGUAGE]

Simple CLI for TESTed

options:
  -h, --help            show this help message and exit
  -e EXERCISE, --exercise EXERCISE
                        Path to a directory containing an exercise
  -s SUBMISSION, --submission SUBMISSION
                        Path to a submission to evaluate
  -t TESTSUITE, --testsuite TESTSUITE
                        Path to a test suite
  -f, --full            If the output should be shown in full (default: false)
  -v, --verbose         If the judge should be verbose in its output (default: false)
  -d, --debug           If the judge should be outputing the debug messages (default: false)
  -p PROGRAMMING_LANGUAGE, --programming_language PROGRAMMING_LANGUAGE
                        The programming language to use

additional information: The CLI only looks at a config.json file in the exercise directory. It does not look in folders above the exercise directory.

This is the "CLI mode": here you can pass various options as command line parameters. For example, for exercises following a standardized directory structure, the path to the exercise folder is often enough.

TESTed repository

The repository of TESTed is organized as follows:

  • tested: Python code of the actual judge (run by Dodona)
  • tests: unit tests for TESTed

You can run the basic unit tests with:

$ python -m pytest tests/test_functionality.py

universal-judge's People

Contributors

bsels avatar chvp avatar dependabot[bot] avatar jsteegmans avatar lgtm-migrator avatar niknetniko avatar pdawyndt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

universal-judge's Issues

Research PEML

Research how we can use PEML as DSL to write exercises

  • Research PEML
  • Integration with Tested

Escape double quotes in string literal

The reason I initially took the easy road here by adopting the Python single quote default for string literals, is that there's an extra step needed to escape double quotes inside a string literal enclosed in double quotes. I think this still needs to be done here, by replacing any double quote by an escaped double quote, unless it's already escaped.

Real world test beschrijven

Vergeet je ook niet ergens ruimte te voorzien om de real world test van je judge op Dodona te bespreken. Je hoeft dit nu nog niet per se uit te schrijven (we hebben een beoordeling op de GitHub issue), maar ergens een stub voorzien dat het niet vergeten wordt, kan geen kwaad.

Uitbreiden gegevenstypes

Het idee is om de gegevenstypes op te delen in twee categorieën:

  1. De basistypes, met de huidige types (integer, rational, text, boolean, sequence, set en map.
  2. Uitgebreide types. Deze gegevenstypes staan toe om meer details te gebruiken.
    Het grote verschil is dat deze uitgebreide types standaard vertaald worden naar een van de basistypes. Voor talen die bijvoorbeeld geen tuple uit Python ondersteunen, zal het vanzelf omgezet worden naar een list (maar bv. in Haskell kan het wel omgezet worden naar een tuple). Er is ook de mogelijk dat implementaties voor programmeertalen expliciet een bepaald type niet ondersteunen. Zo zal de Java-implementatie geen uint64 (een unsigned 64-bit integer) ondersteunen, omdat er geen equivalent bestaat in de taal. Een voorbeeld:
TESTed -> int8 uint8 int16 uint16 int32 uint32 int64 uint64
Python int int int int int int int int
Java byte short short int int long long -
C int8_t uint8_t int16_t uint16_t int32_t uint32_t int64_t uint64_t
Haskell Integer Integer Integer Integer Integer Integer Integer Integer

Het grote voordeel is dat er meer types kunnen, zoals bv. list en tuple in Python, maar ook array en List in Java.

Performantie geprogrammeerde evaluatie

Hier zijn twee ideeën voor:

  • Gebruikt jupyter kernels. Dit heb ik momenteel opgenomen als future work in de tekst.
  • Voorzie speciale ondersteuning voor Python door de custom evaluator rechtstreeks uit te voeren, zoals bv. de Python-judge dat doet.
    • Hier kan zelfs verder in gegaan worden: we zouden kunnen zeggen dat geprogrammeerde evaluatie enkel in Python mogelijk is. Dit heeft bijkomende voordelen:
      • De geprogrammeerde evaluatie moet niet in de programmeertaal geïmplementeerd worden (ik kijk er sowieso voor om dit optioneel te maken)
      • We zijn niet meer gebonden aan de serialisatie voor de communicatie custom evaluator <-> judge. Het meegeven van bv. de context uit het testplan aan de evaluator is dan perfect mogelijk.
      • Implementatie TESTed wordt eenvoudiger.

robuuste judge

De judge liefst met alle mogelijke manieren waarop het verkeerd kan gaan overweg kunnen. Doelen, in volgorde van belangrijkheid:

  1. De judge mag nooit foute output juist rekenen.
  2. Zoveel mogelijk nuttige uitvoer geven.

Waar is de judge op voorzien:

  • Vroegtijdige stop (bv. exit(150))
  • Niet voldoende uitvoer
  • Fout tijdens de uitvoering
  • Fout tijdens de compilatie
  • Foute uitvoer
  • Code vraagt te veel input
  • Te veel uitvoer in vergelijking met de testen

Moeilijker:

  • Tijdslimiet
  • Geheugenlimiet
  • Uitvoerlimiet (bv. echt heel veel blijven printen in oneindige lus)

Gemigreerd van https://github.com/niknetniko/test-judge/issues/2

Stub voor linter

De tekst bevat nu nog geen bespreking van de stub die voorzien is om een linter te configureren voor de programmeertaal.

Soorten oefeningen die ondersteund worden

In hoofdstuk drie worden voorbeelden gegeven van soorten oefeningen die ondersteund worden door de generieke judge. Daar is op dit moment sprake van drie soorten oefeningen die zullen besproken worden, maar misschien zijn er nog extra waarvan we een voorbeeld zouden willen geven:

  • batch uitvoer: enkel uitvoeren van ingediende oplossing, zonder bijkomende testgevallen
    • lezen van stdin en schrijven naar stdout: dit is het makkelijkste soort oefeningen om een generieke uitvoer voor te voorzien, maar we mogen zeker niet vergeten melden dat de generieke judge dit ook ondersteunt (eerste vier reeksen van Python oefeningen gebruiken dit)
    • opties en argumenten doorgeven: wordt gebruikt bij bash shell scripts in computergebruik
    • aanvullen van gegeven stuk command line: hierbij is het de bedoeling om een gegeven stuk commandolijn aan te vullen (wordt gebruikt om te leren werken op de commandolijn in computergebruik); bijvoorbeeld vul aan zodat omgekeerde string wordt teruggegeven echo 'SPAM' | ... (moet bv. MAPS uitschrijven op stdout; testgevallen bestaan telkens uit stuk commandolijn met lichte varianten, hier de string die uitgeschreven wordt op stdin, waar de ingediende oplossing dan moet ingevuld worden op de plaats van de drie puntjes)

Partiële evaluaties met orakel

Uit de e-mail:

Een speciaal geval is van geprogrammeerde evaluatie, die je een "orakel" zou kunnen noemen.
Voorbeeld: oefening waarbij de leeftijd van een persoon moet berekend worden (op vandaag), gegeven de geboortedatum van die persoon. Daarbij kan dus de verwachte waarde niet statisch in het testplan zitten, maar het kan wel berekend worden door een orakel. Dit orakel kan nu al vervat zitten in de geprogrammeerde evaluatie, maar het zou eventueel ook kunnen:

  • ​berekend worden in het testplan (dynamisch testplan), dus voorafgaand aan het genereren van de testcode
  • berekend worden door het orakel en geëvalueerd worden door TESTed als het resultaat een ondersteund datatype heeft (zoals in dit geval een integer); met andere woorden, de geprogrammeerde evaluatie bestaat er dan enkel in om op basis van de input (geboortedatum) de verwachte output (leeftijd) te genereren en die terug te geven aan TESTed, die dan op zijn beurt de gelijkheid van de verwachte waarde (gegenereerd door het orakel) en de gegenereerde waarde (door de ingediende oplossing) kan controleren

In die zin werkt het "orakel" eerder op dezelfde manier als de uitvoeringsomgeving, dan als de evaluatieomgeving, want ze moet enkel resultaten afleveren (hetzij voor integratie in het testplan of voor gebruik in de evaluatiestap van TESTed). Voor wie het testplan opstelt, zou dat betekenen dat een (deel van een) voorbeeldoplossing moet aangeleverd worden (in een programmeertaal die door TESTed ondersteund wordt.

serialisatieformaat opstellen

Zie #8 voor een lijst van ondersteunde datatypes. Om de implementatie eenvoudiger te houden, is het best om onmiddellijk voor alle types het type ook apart mee te geven, los van hoe we het in json implementeren (bv. ook bij strings al het type string meegeven).

performantie verbeteren

Enkele ideeën:

  • Contexten parallel uitvoeren
  • Bijkomende compilatiestap voor dingen die niet context-afhankelijk zijn, zoals de code van de student.

taalspecifieke invoer

Een stuk code dat uitgevoerd wordt als "functie-oproep". Eventueel, maar niet noodzakelijk, gekoppeld aan taalspecifieke evaluator (#3)

Te doen:

  • Welke combinaties zijn mogelijk? Bv. stdin en eigen code, generieke functie en eigen code, enz. In welke volgorde moeten dingen?

aantekeningen vergadering VIII

Voorbereiding

De voorbije week

Termen:

  • Aangepaste (custom) evaluator: eigen evaluatiecode, maar taalonafhankelijk
  • Specifieke (specific) evaluator: eigen evaluatiecode, taalafhankelijk

Gedaan deze week:

  • Implementeer eigen taalonafhankelijke evaluatiecode
  • Implementeer exception als eigen kanaal
    • Laat ook aangepaste en specifieke evaluators toe, die krijgen dan de exception
    • Geïmplementeerd in Java en Python: niet helemaal duidelijk of dat dat bestaat in Haskell
    • Werkt als het foutkanaal: indien het testplan none specificeert, d.w.z. er geen uitvoer op het exceptions-kanaal verwacht wordt, zal de uitvoer van het kanaal gebruikt worden als foutboodschap van de andere kanalen
  • Werk implementatie serialisatie af (voornamelijk in Haskell)
  • Kijk wat naar oefeningen van Veerle
    • Op zich redelijke kandidaat voor het tonen van wat we willen oplossen (zelfde oefening in andere talen)
    • Wel nog geen fantastische kandidaat gevonden voor aangepaste evaluator

Volgende week

  • Opstellen van een oefening met "willekeurigheid", kan de aangepaste evaluator tonen
  • Beginnen aan opstellen diavoorstelling
  • Implementeren polling voor bestandsgrootte
  • Implementeren taalspecifieke invoer
  • Nadenken en eventueel implementeren constructor calls

Message format bij evaluators

Momenteel is dat enkel tekst, maar het kan handig zijn om ook het formaat (en als we toch bezig zijn ook de zichtbaarheid) te kunnen opgeven: op dat moment kunnen we zelfs het formaat van Dodona gewoon gebruiken.

Optional context / testcase

An optional context or testcase that uses a construct not supported by the programming language of the submitted solution would be skipped, instead of flagging a no-language-support for the entire testplan.

Controle op onoplosbare oefeningen

Momenteel beperkt de controle op comptabiliteit met een programmeertaal tot dingen die TESTed nodig heeft (een testplan waarbij bv. sets gebruikt worden zal niet werken in talen zonder set).

Er is nog een ander aspect dat niet gecontroleerd wordt: is de oefening oplosbaar in de programmeertaal. Bij de ISBN-opgave bijvoorbeeld:

Schrijf een functie is_isbn waaraan een string c (str) moet doorgegeven worden. De functie moet een Booleaanse waarde (bool) teruggeven, die aangeeft of c een geldige ISBN-code is. De functie heeft ook nog een optionele tweede parameter isbn13 waaraan een Booleaanse waarde (bool) kan doorgegeven worden die aangeeft of het om een ISBN-10 code (False) of om een ISBN-13 code (True, standaardwaarde) moet gaan.

Deze opgave impliceert dat er een manier is om optionele parameters te hebben in de taal. In Java is dit opgevangen met overloading, maar dat is niet mogelijk in alle talen. In C zou er rond gewerkt kunnen worden met een macro (denk ik, nog niet gedaan), maar ik heb nog niet echt een goede oplossing gevonden voor Haskell.

Hier zijn meerdere pistes mogelijk:

  1. We doen niets. Het is dan aan de lesgever om aan te duiden welke programmeertalen mogelijk zijn.
  2. We leiden een soort van function signatures af uit de oproepen in het testplan, en controleren vervolgens of de taal dat ondersteunt (of we eisen dat deze signatures ook in het testplan zijn, maar dat gaat is minder eenvoudig)
  3. We doen ook geval 2, maar proberen dan de compiler of runtime errors te parsen en zo te bekijken of de functie juist is of niet (bv. in haskell zal dit een compiler error zijn). Dit moet dan wel zeer strikt zijn, zodat errors door slechte code van de studenten niet ook goedgekeurd wordt.

Een ander, gerelateerd aspect zijn dynamische types van argumenten. In Python geen probleem, Java ook nog doenbaar met overloading, maar Haskell en C worden weer moeilijk.

Time-outs implementeren

  • Per context
  • Standaardwaarde: 80% van totaal / aantal contexten
  • Standaardwaarde overschrijven op niveau van context
    • Per programmeertaal andere waarde mogelijk

Parallelle uitvoering verwijderen uit tekst

Ik heb de parallelle uitvoering van de contexten verwijderd voor het implementeren van de time-out.

  • De globale timer maakt het ingewikkeld om een juiste time-out toe te passen. Bovendien zullen de resultaten dan minder betrouwbaar zijn: bij oefeningen met een strakke tijdslimiet kan het zijn dat een beoordeling slaagt bij een uitvoering (omdat er parallel contexten uitgevoerd worden), maar faalt bij een andere, doordat de contexten sequentieel te traag zijn. Ik zie hier niet direct een oplossing voor.
  • De tijdswinst op Dodona zelf is niet zo groot (zoals al vermeld in de tekst).

Concreet zou ik overal de vermelding van parallelle uitvoering verwijderden, met uitzondering van de paragraaf over performantie, waar ik bovenstaande uitleg kan doen.

Vergadering XIII

Gedaan

  • Implementatie assignments
    • In de vorm naam = functieoproep
    • Functieoproep uitgebreid met constructor-type en identity-type
      • Constructor: speciaal geval voor constructor
      • Identity: Letterlijk het argument, laat waarden of vroeger toegekende variabelen toe
    • Type van assignment is instelbaar, maar bv. voor identity wordt het afgeleid
    • Constructors nog niet in Haskell, zie #17
      • Willen we dingen vertalen naar Haskell? Indien wel, hoever willen we daarin gaan? Wat is de "manier" om het in the testplan te doen?
    • Wat met speciale ingebouwde functies? Bv. str() (en repr?) of len()?
  • Specificatie van vereiste functies

Mee bezig

  • Refactoren en herschrijven voor idee voor performance

Introduceer expressions & assignments in minimale vorm

In de nota's op de tekst staat een boomstructuur dat een goed begin is. Daarvan verder werken zou wel enkel voordelen opleveren:

  • De omslachtige manier van literals te gebruiken bij function calls wordt opgelost (de speciale "identity" function mag weg)
  • Beperkingen bij function calls wordt opgelost

Concreet zou het gaan om:

  • Hernoem assignment naar statement (in TESTed is het enige soort statement een assignment, ik zou expressions geen statements maken op dit moment)
  • Voer expressions in, die bestaan uit:
    • Literals
    • Identifiers
    • Function calls
  • Bij een function call worden de parameters een expression
  • De expressions hebben op dit moment geen operators, we willen geen programmeertaal maken

Dit zou het ook gemakkelijker maken om ooit uit te breiden naar een meer volledige AST, maar dat is momenteel niet de bedoeling van deze issue.

Default parameters

Ik zou de term "default arguments" hernoemen naar "default parameters". Dan is ook het verschil met "named arguments" duidelijker en wordt ook de meer gangbare terminologie gebruikt.

De term parameter wordt gebruikt voor een (abstracte) invoerkanaal waarop informatie aan een functie/methode kan doorgegeven. Dit wordt vastgelegd bij het definiëren van de functie/methode. Bij de definitie kan er ook een standaardwaarde gekoppeld worden aan het invoerkanaal, en daarom noemen we het een "default parameter".

Bij het aanroepen van een functie kan aan elk invoerkanaal een waarde doorgegeven worden. Die waarde noemen we een argument. De koppeling tussen de argumenten en de parameters kan positioneel of benoemd zijn. We spreken in dat laatste geval van named arguments (benoemde argumenten) of keyword arguments.

aangepaste doch taalonafhankelijke evaluatoren

Zal op een zeer gelijkaardige manier werken als de taalspecifieke evaluatoren, behalve dat hier dezelfde code voor alle talen wordt gebruikt.

Bij de taalspecifieke evaluatoren wordt de waarde rechtstreeks aan de evaluatiefunctie gegeven, die uitgevoerd wordt in de context van de studentcode.

Hier zal de waarde geserialiseerd worden, en via de judge naar de evaluatiecode gestuurd worden. Deze laatste evaluatiecode voert niet uit in de context van de studentcode. De judge zal ook de waarde deserialiseren naar objecten in de taal van de evaluator.

Exitcode enkel in main testcase

Momenteel heb ik de exitcode toegevoegd zoals andere "channels" (stdout, stderr, ...) aan een testcase. Dit is niet helemaal juist: er namelijk slechts één exitcode per context. Potentieel is er dus een probleem, waarbij verschillende testcases binnen één context andere exitcodes verwachten, wat dus nooit gaat werken.

Idee: move to main testcase:

  • Er is maar één main testcase per context
  • Houdt semantisch steek: de main testcase wordt ook al gebruikt voor de main-functie of voor scripts.

[Tekst] Vreemde ligaturen uitzetten

Het lettertype voorziet een ligatuur voor tz:
image

Zoek een manier om dit uit te zetten. Overige ligaturen mogen aan blijven, want die zijn veel subtieler.

constructor calls

Dit zou leuk zijn om ook te hebben in het generieke deel, maar:

  • Wat gebeurt er met talen waar dit helemaal anders is (bv. Haskell)

Zie ook #10.

generieke functie-oproep

Op dit moment heeft de functie-oproep volgende data:

  • type: het soort functie-oproep, zie hieronder
  • name: de naam van de functie
  • object: (optioneel) waarop de functie opgeroepen wordt
  • arguments: lijst van waarden, in het serialisatieformaat

Er zijn momenteel twee soorten functies:

  • top: top-level functies
  • object: functies die op een object opgeroepen worden, bv. een klasse of instantie

Niet elke taal ondersteunt top-level functies (bv. Java). Bij dergelijke talen worden top-leven functies omgezet naar object-functies, met als object het object waarin de ingediende code zich bevindt, om een zo goed mogelijke benadering te maken.

Een voorbeeld schept klaarheid:

  • Code van de student: in Main.java zit een klasse Main, voor Python zit de code in submission.py.
  • In het testplan zit een top-level functie-oproep: max(5, 10).

Dit testplan in Python zou gewoon een oproep genereren voor een functie max in submission.py. In het geval van Java zal een oproep gegenereerd worden voor een statische functie op de klasse van de ingediende code: Main.max.

Zoals in de rest van de judge is er geen ondersteuning voor andere niveau's van indeling, zoals Java-packages of -modules, Haskell-modules, enz.

Te doen:

  • Omzetten naar taalspecifieke conventies, zoals CamelCase en snake_case.
  • Betere ondersteuning voor functies zonder return-waarde. Dit zou nu misschien ook werken, maar niet in alle talen (bv. Haskell).
    • Moet dit gecontroleerd kunnen worden? M.a.w. is het dan verkeerd als de functie toch iets teruggeeft? Indien wel is dit moeilijker te doen, maar zou wel het meest flexibel zijn.

Misschien te doen:

  • Zou er ergens ondersteuning moeten zijn voor constructor calls? Dit zou dan een functie-oproep zijn zonder return-waarde. Op zich nuttig om functies op instanties op te roepen. Eens functies zonder return-waarde er zijn, is dit niet meer zoveel werk.
    • Implementeren als functie, zoals hierboven gezegd, of meer als een soort statement dat voor de testcase uitgevoerd wordt, waarvan de evaluatie er ook mee samenhangt (i.e. dan wordt er geen separator geschreven tussen de twee zaken).

In vroegere versies van de code bevatte het functie-oproep mechanisme een main-type alsook een onderscheid tussen static en niet-statische functies.

Het type main is verwijderd omdat de toegevoegde complexiteit niet opwoog tegen de voordelen. Main-functies worden nu volledig apart behandeld in de sjablonen.

Het onderscheid tussen statische en niet-statische functies is verwijderd omdat het niet nuttig was; dit onderscheid kan nog steeds gebeuren door het attribuut object juist in te stellen.

Constructors in Haskell

Vervolg van #11. De vertaling naar Haskell is niet altijd triviaal. Verdergaan op het voorbeeld, stel dat de Tree een functie right heeft:

public class Tree<T> {
    public Tree<T> right() {
        return this.r;
   }
}

In het testplan zou dit opgesteld worden als twee testcases:

  1. Tree<?> tree = new Tree<>();
  2. assert tree.right() == null;

In Haskell veronderstel ik dat hetzelfde implementeren iets zal worden als:

data Tree a = Parent (Tree a) (Tree a)
            | Leaf


right :: Tree a -> Maybe (Tree a)
right (Parent l r) = Just r
right Leaf = Nothing

In dit geval zijn er ook twee testcases:

  1. t = Parent Leaf Leaf
  2. assert (right t) == Just Leaf

Vragen voor later of tijdens een vergadering:
Willen we dit soort vertaling doen in de judge? Hoever willen we daar in gaan? Welke paradigma willen we ondersteunen? (Misschien is het ook eens de moeite om naar enkele oefeningen te kijken die Haskell gebruiken, om te zien wat er juist nodig is)

Monitor bestanden aanmaken

Er zal wel een mogelijkheid bestaan om te weten welke bestanden aangemaakt zijn door een proces.
Dit zou kunnen gebruikt worden om de "file channel" te verbeteren met de mogelijkheid om het maken van bestanden als fout te rekenen.

Toekomstig werk

Ik begin met de hoofdstuk 5 van de thesis te lezen, en wil graag een overzicht maken van "ontbrekende features die we halen uit analyse van bestaande judges" en "ontbrekende features om meertalige oefeningen te ondersteunen in Dodona". Voor mij ligt nu nog niet vast of dit uiteindelijk nog in hoofdstuk 5 moet komen, en ik vraag me op dit moment ook nog niet af of we de features wel willen en of we ze finaal ook moeten implementeren. Is in eerste plaats een boekhouding van ideeën die nog opkomen, en die eventueel kunnen meegenomen worden in een vervolgthesis of bij het verder afwerken van TESTed.

Explicit newline trimming

When comparing file content line by line, trailing newlines are trimmed by default.

Another option would be not to trim these newlines automatically (.splitlines(True)) and add an explicit option for trailing newline trimming (one? multiple?) to textual comparisons. This way, we leave full freedom in testing whether or not the last line of a text file should end with a newline. This is impossible if newlines are trimmed upfront.

Denken over parallelle uitvoering en time-outs

Bij de parallelle uitvoering zijn twee problemen die ik momenteel zie:

  • Een globale counter werkt niet bij als er meerdere contexten in parallelle uitgevoerd worden, of toch op een andere manier dan als ze niet in parallel uitgevoerd worden. Dit maakt het ook moeilijker om een deftige timeout in te stellen voor een oefening, want je weet niet hoeveel cores er vrij zijn in Dodona als de uitvoering gebeurt.
  • Door de parallelle uitvoering kunnen resultaten pas verwerkt worden na de uitvoeringen (wat nu gebeurt) of er zou een soort worker thread moeten zijn die een queue bijhoudt van verwerkte oefeningen om ze in volgorde en sequentieel te verwerken, maar dat is redelijk wat implementatiewerk, en nog de vraag hoe goed dat werkt met de global interpreter lock enzo.

Dat laatste is een probleem als we ooit toch op Dodona willen overschakelen voor time-outs (stel dat er een modus komt om geplande testen eerst aan te geven, zoals Peter zei).

Een mogelijkheid is geen parallelle uitvoering meer doen; dat is wel jammer omdat de contexten zelf onafhankelijk zijn, maar anderzijds is de snelheidswinst beperkt of onduidelijk (op de workers van dodona in elk geval).

Time-outs beschrijving

  • Eventueel bij robuustheid
  • Bij future work: de huidige implementatie is zoals beslist niet echt geavanceerd. Er wordt bijvoorbeeld geen verschil gemaakt tussen batchcompilatie en contextcompilatie, terwijl in dat laatste geval 20% van de uitvoeringstijd waarschijnlijk niet volstaat voor het compileren (en de judge dus nog steeds gekilled zal worden door Dodona)

Update aan nieuwe implementatie

  • Met #20 moet nagekeken worden of de beschrijving van de evaluatie van de testcases nog in orde is.
  • Beschrijf toestand na #26.
  • Het ziet er naar uit dat de implementatie van #24 net iets anders gaat zijn dan wat de tekst beschrijft, pas de tekst lichtjes aan.

taalspecifieke evaluators

  • Uitgevoerd aan de kant van de studentencode
  • Wat schrijven we naar de judge (en hoe)?
    • Ofwel een eigen formaat
    • Ofwel direct het Dodona-formaat

In beide gevallen misschien een soort functie voorzien zodat niet elke evaluator dit moet implementeren.

Eigen formaat

  • Potentieel eenvoudiger: bv. enkel accepted en een tekstuele diff.
  • Potentieel duplicaat van Dodona-formaat: misschien willen we ook messages enzo.

Dodona-formaat

  • Omgekeerde van eigen formaat

Sowieso in beide gevallen strikte controle in de judge voor we het doorsturen naar Dodona.

exceptions als apart kanaal

Het valt te overwegen om exceptions als apart kanaal te voorzien, om bv. te controleren dat stuk code de juiste exception smijt.

Te bepalen:

  • Kan dit generiek? I.e. voorzien we een reeks standaardexceptions (bv. IndexOutOfBounds, ...) Of is dit taalspecifiek?
  • In elk geval: welk formaat?
  • Voorzien we fallback voor talen die niet gedefinieerd zijn? Als een functie bv. een exception moet gooien bij foute invoer, zouden we kunnen verifiëren dat er iets is op stderr als we geen exception hebben gedefinieerd voor de taal.

Voeg data en "channel" toe aan partieel outputformaat

Voor ik een PR maak op Dodona nog een vraag: momenteel (lokaal) zit dat zo in het formaat:

"close-test": {
        "type": "object",
        "description": "Close the current test. Accepted iff status is correct, but you can overwrite this.",
        "required": ["command", "status", "generated"],
        "properties": {
            "command":   { "enum": ["close-test"] },
            "generated": { "type": "string" },
            "accepted":  { "type": "boolean" },
            "status":    { "$ref": "#/definitions/status" },
            "data":      { "type":  "object" } // <---- HIER
        }
    }

De vraag is of het veldje data "getypeerd" moet worden, of mag dat gewoon een object zijn zoals nu? Indien het een schema moet krijgen, wat mag er dan in zitten?

oefening kiezen voor tussentijdse presentatie

Er zijn drie soorten oefeningen:

  • Invoer-uitvoeroefeningen
  • Oefeningen met eigen evaluatiecode (maar toch taalonafhankelijk)
  • Oefeningen met taalspecifieke dingen

Echter:

  • De eerste soort kan wel getoond worden, maar toont de sterkte van de judge nog niet helemaal: uiteindelijk kan dat ook door gewoon de code uit te voeren (en volstaat een afbeelding van de taal naar een uitvoercommando).
  • Bij de laatste soort is uiteraard zo dat dit niet veel verschilt van de huidige werkwijze, aangezien het maar voor 1 taal is.
  • Ik denk dat de tweede soort oefening de opzet van de thesis het best toont, en ook verklaart waarom er een hele judge rond gebouwd wordt.

Uiteraard kunnen de drie modi vermeld worden, maar ik zou aan de hand van het tweede soort oefening uitleggen hoe alles werkt.

Zoals besproken tijdens de meeting:

  • Mogelijke oefeningen zijn misschien te vinden in de cursus(sen) van Veerle Fack.
  • Anders een eenvoudige oefening met willekeurigheid

formaat voor serialiseren bepalen

Enkele pointers van prof. Dawyndt:

Vereisten:

  • Makkelijk uitbreidbaar naar andere programmeertalen
  • Eventueel ook uitbreidbaar in de types
  • Ondersteunen wat wij willen

Te doen:

  • Lijst opstellen van types die we willen (int, float, double, lijst, boolean, enz.)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.