witter

A Library for White-Box Testing of Introductory Programming Algorithms

https://github.com/ambco-iscte/witter

Science Score: 57.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
    Found CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
    Found .zenodo.json file
  • DOI references
    Found 13 DOI reference(s) in README
  • Academic publication links
  • Committers with academic emails
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (12.6%) to scientific vocabulary

Keywords

assessment feedback programming-education software-testing whitebox-testing
Last synced: 6 months ago · JSON representation ·

Repository

A Library for White-Box Testing of Introductory Programming Algorithms

Basic Info
  • Host: GitHub
  • Owner: ambco-iscte
  • Language: Kotlin
  • Default Branch: master
  • Homepage:
  • Size: 2.12 MB
Statistics
  • Stars: 2
  • Watchers: 2
  • Forks: 0
  • Open Issues: 0
  • Releases: 0
Topics
assessment feedback programming-education software-testing whitebox-testing
Created over 2 years ago · Last pushed over 1 year ago
Metadata Files
Readme Citation

README.md

# Witter **A Library for White-Box Testing of Introductory Programming Algorithms** [![ACM SPLASH'23 - Check out Witter's first paper!](https://img.shields.io/badge/ACM_SPLASH'23-Check_out_Witter's_first_paper!-ebc034?logo=acm)](https://doi.org/10.1145/3622780.3623650) [![ICPEC'24 - Check out Witter's second paper!](https://img.shields.io/badge/ICPEC'24-Check_out_Witter's_second_paper!-052d6d?logo=doi&logoColor=white)](https://doi.org/10.4230/OASIcs.ICPEC.2024.2) [![ISCTE-IUL - Check out the MSc Dissertation for which Witter was developed!](https://img.shields.io/badge/ISCTE--IUL-Check_out_the_MSc_Dissertation_for_which_Witter_was_developed!-14bfb8?logo=doi&logoColor=white)](http://hdl.handle.net/10071/32252) **Witter** is a software testing library that allows programming instructors to define white-box tests for Java source code. Witter analyzes the execution of a method against a reference solution, to verify that the code not only produces correct results but is also in accordance with a desired algorithm behaviour. [Installation](#installation) • [Specifying Reference Solutions](#specifying-reference-solutions) • [Testing Arbitrary Solutions](#testing-arbitrary-solutions) • [Examples](#examples)


Installation

Witter is an experimental library, and as such is not yet available in build automation tools (Gradle, etc.)

To use Witter in your project, first build its .jar file using Gradle's build task. The .jar file is generated under the project root in /build/libs. This file should be copied to your own project's libs folder, and then added as a dependency in your build automation tool of choice. For example, in Gradle: kotlin dependencies { implementation(files("libs/witter-0.5.6.jar")) } Note, of course, that the file name can change when updates for Witter are released, and should be changed in your dependency specification accordingly.

You may additionally need to specify dependencies for the Strudel and ANTLR libraries.


Specifying Reference Solutions

Annotating Reference Solutions

One can define the tester cases for a given exercise by writing a reference solution in a Java method, annotated with a header comment that defines the different tester inputs and the metrics that should be measured during tester execution. The content of the comments has to obey Witter’s Test Specification Language (TSL), whose syntax is similar to Java’s annotation syntax: java /* @Test(new int[] { 1, 2, 3, 4, 5 }) @Test(new int[] { 2, 4, 6 }) @CountLoopIterations @CountArrayReads @CheckSideEffects */ public static int sum(int[] a) { ... }

Instantiating Test Specifications Using Witter’s DSL

Test cases can be configured to use any number of white-box metrics either throughout the test or within a bounded scope (using directive). As in the initial version of Witter, evaluation metrics can be optionally instantiated with a margin parameter that specifies an acceptable deviation interval from the reference value.

An object can be created using the new directive by passing the name of the class to instantiate followed by a list of arguments to one of the class constructors. References to the created objects can be stored using ref. Class methods can be invoked by using the call directive on a previously declared reference. A sequence of these directives defines a stateful test case.

The call directive is used by specifying the name of the method to be invoked and a list of arguments. We may use the "dot notation" to perform calls on instance methods given its reference (ref.call(...)), as opposed to calling an instance method by passing its reference as the first argument (call("methodName", ref, ...)). For every call, the return values of the evaluated method are compared to the reference solution, allowing for regular black-box testing. Additionally, if the optional expected argument is passed, Witter will assert that both the reference solution and the solution under evaluation produce the expected result.

The following example illustrates Witter's DSL with a test suite for list data structures, containing two test cases.

```kotlin val test = TestSuite(referencePath = "path/to/reference/solution/ArrayList.java") { Case("testContains") { // Create new object and store a reference to it val list = ref { new("ArrayList", 3) }

    // Executed without white-box metrics (black-box only)
    list.call("size", expected = 0)
    list.call("add", "hello")
    list.call("size", expected = 1)
    list.call("add", "world")
    list.call("size", expected = 2)

    using(CountLoopIterations() + CountArrayReadAccesses()) {
        // These calls compare loop iterations
        call("add", list, "algorithm")
        call("size", list, expected = 3)
    }
}

// All the calls within this case compare loop iterations
Case(CountLoopIterations(), "testIsEmpty") {
    val list = ref { new("ArrayList", 3) }
    list.call("isEmpty", expected = true)
    list.call("add", "hello")
    list.call("add", "icpec")
    list.call("isEmpty", expected = false)
}

} ```


Testing Arbitrary Solutions

As Witter is designed for third-party integration, it provides a form of executing the tests programmatically. Tests are executed providing an annotated reference solution or a test suite created using the DSL as described, and a solution that one wishes to assess.

Using an annotated reference solution, one can execute: kotlin val tester: Test = Test("ReferenceSolution.java") val results: List<TestResult> = tester.execute("Solution.java") For a test suite created using the DSL, the process is similar: kotlin val test = TestSuite(referencePath = "path/to/reference/Solution.java") val results = test.apply(subjectPath = "path/to/submitted/Solution.java")

The results consist of a list of feedback items for each aspect defined in the tester specification, holding the following information: - a flag indicating success or failure; - which kind of metric has failed (recall Table 1); - the location of code elements involved in the failed tests (e.g., procedure, parameters, loop structures); - a human-readable descriptive feedback message.

Witter currently supports the following runtime metrics.

| Metric | TSL Annotation
(DSL is identical, without the @)
| Verification | |--------------------|-----------------------------------------------------------|---------------------------------------------------------------------------------------------------| | Return values | @Test([...args]) | Return value is equal to reference solution. Multiple annotations can be used. | | Side effects | @CheckSideEffects | Side effects on arguments (presence and absence) are the same to those of the reference solution. | | Loop iterations | @CountLoopIterations([threshold]) | Total number of loop iterations matches the one of the reference solution. | | Array allocations | @CheckArrayAllocations | The array allocations match those of the reference solution (component types and lengths). | | Array reads | @CountArrayReads([threshold]) | The number of array read accesses is the same as in the reference solution. | | Array writes | @CountArrayWrites([threshold]) | The number of array write accesses is the same as in the reference solution. | | Object allocations | @CheckObjectAllocations | The number of object allocations and their types match those of the reference solution. | | Recursive calls | @CountRecursiveCalls([threshold]) | The number of recursive calls matches the one of the reference solution. |


Examples

Annotated Reference Solutions

Factorial (Recursive) Reference solution with recursion: ```java /* @Test(5) @CountRecursiveCalls(1) */ static int factorial(int n) { if (n == 0) return 1; else return n * factorial (n - 1); } ``` Solution under testing (iterative, with a defect): ```java static int factorial(int n) { int f = 1; for (int i = 0; i <= n; i++) f *= i; // i starts at 0, f always 0 return f; } ``` Witter tester results (black-box and white-box fail): ``` [fail] factorial(5) Expected result: 120 Found: 0 [fail] factorial(5) Expected recursive calls: 4 (± 1) Found: 0 ```


Binary Search (Iterative) Reference solution using binary search: ```java /* @Test(new int[] { 1, 2, 3, 4, 5, 6, 7 }, 1) @Test(new int[] { 1, 3, 7, 9, 11, 13, 17, 19 }, 18) @CountLoopIterations @CheckSideEffects */ static int binarySearch (int[] a, int e) { int l = 0; int r = a. length - 1; while (l <= r) { int m = l + (r - l) / 2; if (a[m] == e) return m; if (a[m] < e) l = m + 1; else r = m - 1; } return -1; } ``` Solution under testing (performing linear search): ```java static int binarySearch (int[] a, int e) { for (int i = 0; i < a. length ; i++) if (a[i] == e) return i; return -1; } ``` Witter tester results (black-box pass, white-box fail): ``` [pass] search([1, 2, 3, 4, 5, 6, 7], 1) Expected result: 0 [fail] search([1, 2, 3, 4, 5, 6, 7], 1) Expected loop iterations: 3 Found: 1 [pass] search([1, 2, 3, 4, 5, 6, 7], 1) Expected side effects of a: [1, 2, 3, 4, 5, 6, 7] [pass] search([1, 2, 3, 4, 5, 6, 7], 1) Expected side effects of e: 1 [pass] search([1, 3, 7, 9, 11, 13, 17, 19], 18) Expected result: -1 [fail] search([1, 3, 7, 9, 11, 13, 17, 19], 18) Expected loop iterations: 4 Found: 8 [pass] search([1, 3, 7, 9, 11, 13, 17, 19], 18) Expected side effects of a: [1, 3, 7, 9, 11, 13, 17, 19] [pass] search([1, 3, 7, 9, 11, 13, 17, 19], 18) Expected side effects of e: 18 ```


Insertion Sort (Procedure) Reference solution performing insertion sort: ```java /* @Test(new int[] { 5, 4, 3, 2, 1 }) @CountArrayReads @CountArrayWrites @CheckSideEffects */ static void sort(int[] a) { for (int i = 1; i < a. length; i++) { for (int j = i; j > 0; j--) { if (a[j] >= a[j - 1]) break; int tmp = a[i]; a[i] = a[j]; a[j] = tmp; } } } ``` Solution under testing (performing selection sort): ```java static void sort(int[] a) { for (int i = 0; i < a. length - 1; i++) { int min = i; for (int j = i + 1; j < a. length ; j++) if (a[j] < a[min]) min = j; int tmp = a[i]; a[i] = a[min]; a[min] = tmp; } } ``` Witter tester results (black-box pass, white-box fail): ``` [fail] sort([5, 4, 3, 2, 1]) Expected array reads: 40 Found: 28 [fail] sort([5, 4, 3, 2, 1]) Expected array writes: 20 Found: 8 [pass] sort([5, 4, 3, 2, 1]) Expected side effects of a: [5, 4, 3, 2, 1] ```

Tests Specified Using the DSL

Array Average (Procedure) Reference solution (Average.java): ```java static double average(double[] a) { double sum = 0.0; for (int i = 0; i < a.length; i++) sum += a[i]; return sum / a.length; } ``` DSL Test Suite: ```java val tests = TestSuite("path/to/reference/Average.java") { Case(CountLoopIterations()) { call("average", listOf(1,2,3,4,5), expected = 3.0) call("average", listOf(0,2,3,5,7), expected = 3.4) } } ``` Solution under testing with a defect - starts at index 1 (Solution.java): ```java static double average(double[] a) { double sum = 0.0; for (int i = 1; i < a.length; i++) sum += a[i]; return sum / a.length; } ``` Applying the Test Suite to the solution under evaluation: ```java val results: List = tests.apply( subjectPath = "path/to/Solution.java" ) results.forEach { println("$it\n") } ``` Test results: ```java [fail] average([1, 2, 3, 4, 5]) Expected: 3.0 Found: 2.8 [fail] average([1, 2, 3, 4, 5]) Expected loop iterations: 5 Found: 4 [pass] average([0, 2, 3, 5, 7]) Expected: 3.4 [fail] average([0, 2, 3, 5, 7]) Expected loop iterations: 5 Found: 4 ```


Stack (Data Structure) Reference solution's _size_ method (StackReference.java): ```java public int size() { return this.size; // Auxiliary integer attribute } ``` DSL Test Suite: ```java val tests = TestSuite("path/to/reference/StackReference.java") { Case { val stack = ref { new("Stack", 5) } call("push", stack, 1) call("push", stack, 2) call("push", stack, 3) using (CountLoopIterations() + CountArrayReadAccesses()) { call("size", stack, expected = 3) } } } ``` Solution under testing with a defect - counts non-zero items individually (StackSolution.java): ```java public int size() { int s = 0; for (int i = 0; i < stack.length; i++) if (stack[i] != 0) s += 1; return s; } ``` Test results: ```java [pass] size(Stack(stack=[1, 2, 3, 0, 0], size=3)) Expected: 3 [fail] size(Stack(stack=[1, 2, 3, 0, 0], size=3)) Expected loop iterations: 0 Found: 5 [fail] size(Stack(stack=[1, 2, 3, 0, 0], size=3)) Expected array reads: 0 Found: 5 ```


See It in Action 😎

The following is an example of how Witter could be integrated into an existing development system, using a simple GUI custom-made for example purposes.


Citations

If you use or reference Witter in your academic work, you should cite the relevant following publication(s).

Witter: A Library for White-Box Testing of Introductory Programming Algorithms **ACM Reference Format** > Afonso B. Caniço and André L. Santos. 2023. Witter: A Library for White-Box Testing of Introductory Programming Algorithms. In _Proceedings of the 2023 ACM SIGPLAN International Symposium on SPLASH-E (SPLASH-E ’23), October 25, 2023, Cascais, Portugal._ ACM, New York, NY, USA, 6 pages. https://doi.org/10.1145/3622780.3623650 **BibTeX** ```bibtex @inproceedings{canicosantos2023, author = {Cani\c{c}o, Afonso and Santos, Andr\'{e}}, title = {Witter: A Library for White-Box Testing of Introductory Programming Algorithms}, year = {2023}, isbn = {9798400703904}, publisher = {Association for Computing Machinery}, address = {New York, NY, USA}, url = {https://doi.org/10.1145/3622780.3623650}, doi = {10.1145/3622780.3623650}, abstract = {Software testing is mostly performed in a black-box manner, that is, without incorporating any knowledge of the internal workings of programs into the tests. This practice usually suffices for enterprises and general practitioners, where the focus lies on producing reliable results while most algorithmic tasks are provided by third-party libraries. However, for computer science students and the like, it might not be straightforward to discern the underlying causes of an incorrect tester result or to understand why certain algorithmic goals are not met. We present Witter, a software testing library that allows programming educators to define white-box tests for Java source code. Our tests analyze the execution of a method against a reference solution, to verify that the code not only produces correct results but is also in accordance with a desired algorithm behavior.}, booktitle = {Proceedings of the 2023 ACM SIGPLAN International Symposium on SPLASH-E}, pages = {69–74}, numpages = {6}, keywords = {programming education, white-box testing, feedback, assessment}, location = {Cascais, Portugal}, series = {SPLASH-E 2023} } ```


A Domain-Specific Language for Dynamic White-Box Evaluation of Java Assignments **Dagstuhl Reference Format** > Afonso B. Caniço and André L. Santos. A Domain-Specific Language for Dynamic White-Box Evaluation of Java Assignments. In 5th International Computer Programming Education Conference (ICPEC 2024). Open Access Series in Informatics (OASIcs), Volume 122, pp. 2:1-2:13, Schloss Dagstuhl – Leibniz-Zentrum für Informatik (2024) https://doi.org/10.4230/OASIcs.ICPEC.2024.2 **BibTeX** ```bibtex @InProceedings{b.canico_et_al:OASIcs.ICPEC.2024.2, author = {B. Cani\c{c}o, Afonso and Santos, Andr\'{e} L.}, title = {{A Domain-Specific Language for Dynamic White-Box Evaluation of Java Assignments}}, booktitle = {5th International Computer Programming Education Conference (ICPEC 2024)}, pages = {2:1--2:13}, series = {Open Access Series in Informatics (OASIcs)}, ISBN = {978-3-95977-347-8}, ISSN = {2190-6807}, year = {2024}, volume = {122}, editor = {Santos, Andr\'{e} L. and Pinto-Albuquerque, Maria}, publisher = {Schloss Dagstuhl -- Leibniz-Zentrum f{\"u}r Informatik}, address = {Dagstuhl, Germany}, URL = {https://drops.dagstuhl.de/entities/document/10.4230/OASIcs.ICPEC.2024.2}, URN = {urn:nbn:de:0030-drops-209715}, doi = {10.4230/OASIcs.ICPEC.2024.2}, annote = {Keywords: White-box assessment, student assessment, programming education} } ```


White-Box Assessment for Programming Education This is a dissertation produced for the completion of the Master's of Science (MSc) in Computer Science and Engineering by the author [Afonso B. Caniço](https://ciencia.iscte-iul.pt/authors/afonso-canico/cv). **ACM Reference Format** > Afonso Manuel Barral Caniço. 2024. *White-Box Assessment for Programming Education*. Master's Thesis. > Iscte - Instituto Universitário de Lisboa, Avenida das Forças Armadas, 1649-026 Lisboa. > http://hdl.handle.net/10071/32252. **BibTeX** ```bibtex @mastersthesis{canico2024, author = {Cani\c{c}o, Afonso Manuel Barral}, title = {White-Box Assessment for Programming Education}, school = {Iscte - Instituto Universit\'{a}rio de Lisboa}, year = {2024}, type = {Master's Thesis}, address = {Avenida das For\c{c}as Armadas, 1649-026, Lisboa}, month = {July}, note = {\url{http://hdl.handle.net/10071/32252}} } ```


Contacts

If you have any questions regarding Witter, its development process, or the related academic publications, feel free to contact the authors: - Afonso B. Caniço - ambco@iscte-iul.pt (Principal Author) - André L. Santos - andre.santos@iscte-iul.pt (Advisor)


Credit

The Witter library is authored, developed, and currently maintained by Afonso B. Caniço. (That's me! :D)

Credit for the Strudel library, which is used within Witter as the runtime environment for code execution, goes to André L. Santos, its author and main contributor. Contributions to Java translation and minor contributions to some aspects of the Strudel library were made by Afonso B. Caniço.

Owner

  • Name: Afonso Caniço
  • Login: ambco-iscte
  • Kind: user
  • Location: Lisbon, Portugal
  • Company: Iscte - University Institute of Lisbon

Master's Student & Invited Teaching Assistant @ Iscte-IUL

Citation (CITATION.cff)

# This CITATION.cff file was generated with cffinit.
# Visit https://bit.ly/cffinit to generate yours today!

cff-version: 1.2.0
title: >-
  Witter: A Library for White-Box Testing of Introductory
  Programming Algorithms
message: >-
  If you use this software, please cite it using the
  metadata from this file.
type: software
authors:
  - given-names: Afonso
    family-names: B. Caniço
    email: ambco@iscte-iul.pt
    affiliation: Instituto Universitário de Lisboa (ISCTE-IUL)
    orcid: 'https://orcid.org/0009-0009-9334-717X'
  - given-names: André
    family-names: L. Santos
    email: andre.santos@iscte-iul.pt
    orcid: 'https://orcid.org/0000-0002-8247-7413'
    affiliation: Instituto Universitário de Lisboa (ISCTE-IUL)
repository-code: 'https://github.com/ambco-iscte/witter'
abstract: >-
  Software testing is mostly performed in a black-box
  manner, that is, without incorporating any knowledge of
  the internal workings of programs into the tests. This
  practice usually suffices for enterprises and general
  practitioners, where the focus lies on producing reliable
  results while most algorithmic tasks are provided by
  third-party libraries. However, for computer science
  students and the like, it might not be straightforward to
  discern the underlying causes of an incorrect tester result
  or to understand why certain algorithmic goals are not
  met. We present Witter, a software testing library that
  allows programming educators to define white-box tests for
  Java source code. Our tests analyze the execution of a
  method against a reference solution, to verify that the
  code not only produces correct results but is also in
  accordance with a desired algorithm behavior.
keywords:
  - programming education
  - white-box testing
  - assessment
  - feedback
preferred-citation:
  type: conference-paper
  authors:
  - family-names: "Caniço"
    given-names: "Afonso"
    orcid: "https://orcid.org/0009-0009-9334-717X"
  - family-names: "Santos"
    given-names: "André"
    orcid: "https://orcid.org/0000-0002-8247-7413"
  doi: "10.1145/3622780.3623650"
  conference:
    name: "ACM SIGPLAN International Symposium on SPLASH-E"
  title: "Witter: A Library for White-Box Testing of Introductory Programming Algorithms"
  publisher: 
    name: "Association for Computing Machinery"
  collection-title: "Proceedings of the 2023 ACM SIGPLAN International Symposium on SPLASH-E"
  start: 69
  end: 74
  month: 10
  year: 2023

GitHub Events

Total
  • Watch event: 1
Last Year
  • Watch event: 1

Committers

Last synced: about 2 years ago

All Time
  • Total Commits: 22
  • Total Committers: 1
  • Avg Commits per committer: 22.0
  • Development Distribution Score (DDS): 0.0
Past Year
  • Commits: 22
  • Committers: 1
  • Avg Commits per committer: 22.0
  • Development Distribution Score (DDS): 0.0
Top Committers
Name Email Commits
ambco-iscte a****o@i****t 22
Committer Domains (Top 20 + Academic)

Issues and Pull Requests

Last synced: about 2 years ago

All Time
  • Total issues: 0
  • Total pull requests: 0
  • Average time to close issues: N/A
  • Average time to close pull requests: N/A
  • Total issue authors: 0
  • Total pull request authors: 0
  • Average comments per issue: 0
  • Average comments per pull request: 0
  • Merged pull requests: 0
  • Bot issues: 0
  • Bot pull requests: 0
Past Year
  • Issues: 0
  • Pull requests: 0
  • Average time to close issues: N/A
  • Average time to close pull requests: N/A
  • Issue authors: 0
  • Pull request authors: 0
  • Average comments per issue: 0
  • Average comments per pull request: 0
  • Merged pull requests: 0
  • Bot issues: 0
  • Bot pull requests: 0
Top Authors
Issue Authors
Pull Request Authors
Top Labels
Issue Labels
Pull Request Labels

Dependencies

build.gradle.kts maven
  • com.github.javaparser:javaparser-symbol-solver-core 3.25.1 implementation
  • org.antlr:antlr4 4.13.1 implementation
  • org.jetbrains.kotlin:kotlin-reflect * implementation