Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Set up JDK 25
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 25
- name: Cache Maven packages
uses: actions/cache@v3
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Build and run tests
run: |
mvn --batch-mode clean verify
18 changes: 18 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,17 @@
</execution>
</executions>
</plugin>
<!-- configure the test runner -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<includes>
<include>**/*Test.java</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
Expand All @@ -76,5 +87,12 @@
<artifactId>forms_rt</artifactId>
<version>7.0.3</version>
</dependency>
<!-- testing framework -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package io.github.compilerstuck.SortingAlgorithms;

import io.github.compilerstuck.Control.ArrayController;
import io.github.compilerstuck.Control.MainController;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import processing.core.PApplet;

import java.util.List;
import java.util.Random;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.*;

class SortingAlgorithmsTest {

/**
* Minimal stub implementation of the Processing runtime used by the visualizer.
* Most of the sorting code only ever calls {@code delay(int)}, so we provide a
* no-op implementation to avoid starting a real graphics context during tests.
*/
static class DummyProcessing extends PApplet {
@Override
public void delay(int ms) {
// do nothing, keep tests fast
}
}

@BeforeAll
static void setupMainController() {
// set static processing field so that algorithms don't hit NPE if they
// accidentally call it even though we disable delays in the tests.
MainController.processing = new DummyProcessing();
}
Comment on lines +34 to +39

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test mutates global static state (MainController.processing) but never restores it. To avoid test-order coupling as the suite grows, store the previous value and restore it in an @AfterAll (or set/reset per-test).

Copilot uses AI. Check for mistakes.

/**
* Create a random permutation of 0..size-1. Using a fixed seed ensures
* deterministic behaviour across runs.
*/
private static int[] randomPermutation(int size, long seed) {
int[] arr = new int[size];
for (int i = 0; i < size; i++) {
arr[i] = i;
}
Random rnd = new Random(seed);
for (int i = size - 1; i > 0; i--) {
int j = rnd.nextInt(i + 1);
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
return arr;
}

/**
* Provide every algorithm except for the deliberately pathological BogoSort.
* Each combination will be executed with two different array sizes to increase
* confidence that the implementation works for both small and moderately
* sized inputs.
*/
static Stream<Arguments> algorithmAndSizes() {
List<Class<? extends SortingAlgorithm>> algs = List.of(
BubbleSort.class,
SelectionSort.class,
InsertionSort.class,
MergeSort.class,
QuickSortMiddlePivot.class,
QuickSortDualPivot.class,
HeapSort.class,
ShellSort.class,
TimSort.class,
CountingSort.class,
RadixLSDSortBase10.class,
BucketSort.class,
AmericanFlagSort.class,
CycleSort.class,
CombSort.class,
GnomeSort.class,
OddEvenSort.class,
ShakerSort.class,
DoubleSelectionSort.class,
PigeonholeSort.class,
GravitySort.class
);

return algs.stream()
.flatMap(alg -> Stream.of(Arguments.of(alg, 10), Arguments.of(alg, 50)));
}

@ParameterizedTest(name = "{0} sorts {1} elements")
@MethodSource("algorithmAndSizes")
void algorithmShouldSort(Class<? extends SortingAlgorithm> algoClass, int size) throws Exception {
// we'll test three common scenarios: random data, already-sorted, and reverse-sorted
int[][] datasets = new int[4][];
datasets[0] = randomPermutation(size, 12345L + size);
datasets[1] = new int[size];
datasets[2] = new int[size];
datasets[3] = new int[size];
Comment on lines +98 to +103

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says the test covers “three common scenarios”, but the code builds 4 datasets (random, sorted, reverse, duplicates). Update the comment or the datasets so they match; otherwise it’s easy to misread what’s actually being tested.

Copilot uses AI. Check for mistakes.
for (int i = 0; i < size; i++) {
datasets[1][i] = i; // already sorted
datasets[2][i] = size - i - 1; // reverse order
datasets[3][i] = i % 5; // many duplicates
}
Comment on lines +104 to +108

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The “duplicates” dataset introduces repeated values (i % 5). The production ArrayController always creates/shuffles permutations of 0..n-1 (unique values), and at least some algorithms here assume that invariant (e.g., CycleSort can increment pos past the end when skipping duplicates). This test case is likely to fail or be out-of-scope for the visualizer’s data model; consider removing it or restricting it to algorithms explicitly intended to handle duplicates.

Copilot uses AI. Check for mistakes.

for (int idx = 0; idx < datasets.length; idx++) {
int[] values = datasets[idx];
ArrayController controller = new ArrayController(size);
for (int i = 0; i < size; i++) {
controller.set(i, values[i]);
}
SortingAlgorithm algorithm = algoClass.getConstructor(ArrayController.class)
.newInstance(controller);
algorithm.setDelay(false);
algorithm.sort();
String type;
switch (idx) {
case 0 -> type = "random";
case 1 -> type = "sorted";
case 2 -> type = "reverse";
case 3 -> type = "duplicates";
default -> type = "unknown";
}
assertTrue(controller.isSorted(), algoClass.getSimpleName() + " failed to sort " + type + " dataset");
}
}

@Test
@DisplayName("BogoSort eventually produces a sorted array (small size)")
@org.junit.jupiter.api.Timeout(value = 5)
void bogoSortSmallArray() {
int size = 4; // small enough that random swaps finish in a reasonable time
ArrayController controller = new ArrayController(size);
int[] values = randomPermutation(size, 42L);
for (int i = 0; i < size; i++) {
controller.set(i, values[i]);
}

BogoSort algo = new BogoSort(controller);
algo.setDelay(false);
// run for a capped number of iterations to avoid infinite loops in case
// something is wrong; the implementation keeps trying until sorted, so we
// simply execute the method on the small array.
algo.sort();
assertTrue(controller.isSorted(), "BogoSort did not sort the small array");
Comment on lines +132 to +149

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BogoSort.sort() uses a non-seeded new Random() loop-until-sorted. Even with a small array and a timeout, this makes the test non-deterministic and potentially flaky on slower/loaded CI. Prefer making the randomness deterministic for the test (e.g., allow injecting a seeded Random) or add a bounded-tries mode that the test can assert on.

Copilot uses AI. Check for mistakes.
}
}
Loading