From 68613f38df0f4bf05685da7902a59d3f2f266440 Mon Sep 17 00:00:00 2001 From: Jacob Date: Thu, 8 Oct 2026 11:24:57 -0400 Subject: [PATCH 1/3] Add simulated parking capacity and Jenkins CI (refs #9) --- .gitignore | 2 + Jenkinsfile | 34 ++++++++++ README.md | 30 ++++++++- ci/compose.yaml | 18 +++++ .../edu/stevens/smartpark/ParkingLot.java | 57 ++++++++++++++++ .../edu/stevens/smartpark/ParkingLotTest.java | 67 +++++++++++++++++++ 6 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 Jenkinsfile create mode 100644 ci/compose.yaml create mode 100644 src/main/java/edu/stevens/smartpark/ParkingLot.java create mode 100644 src/test/java/edu/stevens/smartpark/ParkingLotTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8dcca34 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +*.class diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..cd19bdd --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,34 @@ +pipeline { + agent any + + options { + skipDefaultCheckout() + disableConcurrentBuilds() + timeout(time: 10, unit: 'MINUTES') + buildDiscarder(logRotator(numToKeepStr: '10')) + } + + stages { + stage('Checkout') { + steps { + checkout scm + sh 'git rev-parse HEAD' + } + } + stage('Compile and lint') { + steps { + sh ''' + mkdir -p build/classes + javac -Xlint:all -Werror -d build/classes \ + src/main/java/edu/stevens/smartpark/ParkingLot.java \ + src/test/java/edu/stevens/smartpark/ParkingLotTest.java + ''' + } + } + stage('Unit tests') { + steps { + sh 'java -cp build/classes edu.stevens.smartpark.ParkingLotTest' + } + } + } +} diff --git a/README.md b/README.md index 9031e8d..1687e0a 100644 --- a/README.md +++ b/README.md @@ -54,4 +54,32 @@ EV Charging: Available The SmartPark application itself and live Stevens campus parking data are not currently deployed. -The team's currently deployed operational work hub is Gitea, which is being used for source control, issues, pull requests, reviews, and project history. \ No newline at end of file +The team's currently deployed operational work hub is Gitea, which is being used for source control, issues, pull requests, reviews, and project history. + +## Simulated parking-capacity core + +`ParkingLot` is a small, in-memory Java 21 component for the proposed SmartPark +application. It records a lot name, total capacity, and available spaces. +`vehicleEnters()` decreases availability; `vehicleLeaves()` increases it. The +component rejects counts outside the range from zero to capacity and prevents +arrivals at a full lot or departures from an empty lot. It does not fetch live +campus data, save state, or expose a web interface. + +The repository's `Jenkinsfile` compiles this component with all Java compiler +warnings treated as errors, then runs automated boundary and transition tests. +The only build dependency is JDK 21, already present in the selected Jenkins +image. A clean local run from the repository root is: + +```bash +mkdir -p build/classes +javac -Xlint:all -Werror -d build/classes \ + src/main/java/edu/stevens/smartpark/ParkingLot.java \ + src/test/java/edu/stevens/smartpark/ParkingLotTest.java +java -cp build/classes edu.stevens.smartpark.ParkingLotTest +``` + +Expected test output is `ParkingLot tests passed`. A nonzero exit status means +the compile or test step failed. The generated `build/` directory is ignored by +Git. The Jenkins pipeline is intended for trusted team changes only; it runs +on the Jenkins controller because this small assignment deployment has no +separate build agent. diff --git a/ci/compose.yaml b/ci/compose.yaml new file mode 100644 index 0000000..ae8eedb --- /dev/null +++ b/ci/compose.yaml @@ -0,0 +1,18 @@ +services: + jenkins: + image: jenkins/jenkins:2.580.1-jdk21 + restart: unless-stopped + mem_limit: 768m + environment: + JAVA_OPTS: "-Xms128m -Xmx384m" + volumes: + - jenkins_home:/var/jenkins_home + networks: + - color-net + +volumes: + jenkins_home: + +networks: + color-net: + external: true diff --git a/src/main/java/edu/stevens/smartpark/ParkingLot.java b/src/main/java/edu/stevens/smartpark/ParkingLot.java new file mode 100644 index 0000000..3051ec8 --- /dev/null +++ b/src/main/java/edu/stevens/smartpark/ParkingLot.java @@ -0,0 +1,57 @@ +package edu.stevens.smartpark; + +/** A single parking location with simulated, in-memory availability. */ +public final class ParkingLot { + private final String name; + private final int capacity; + private int availableSpaces; + + public ParkingLot(String name, int capacity, int availableSpaces) { + if (name == null || name.isBlank()) { + throw new IllegalArgumentException("Parking lot name is required"); + } + if (capacity < 0) { + throw new IllegalArgumentException("Capacity cannot be negative"); + } + if (availableSpaces < 0 || availableSpaces > capacity) { + throw new IllegalArgumentException("Available spaces must be between zero and capacity"); + } + this.name = name; + this.capacity = capacity; + this.availableSpaces = availableSpaces; + } + + public String getName() { + return name; + } + + public int getCapacity() { + return capacity; + } + + public int getAvailableSpaces() { + return availableSpaces; + } + + public int getOccupiedSpaces() { + return capacity - availableSpaces; + } + + public boolean isFull() { + return availableSpaces == 0; + } + + public void vehicleEnters() { + if (isFull()) { + throw new IllegalStateException("Parking lot is full"); + } + availableSpaces--; + } + + public void vehicleLeaves() { + if (availableSpaces == capacity) { + throw new IllegalStateException("Parking lot is already empty"); + } + availableSpaces++; + } +} diff --git a/src/test/java/edu/stevens/smartpark/ParkingLotTest.java b/src/test/java/edu/stevens/smartpark/ParkingLotTest.java new file mode 100644 index 0000000..afd1ff0 --- /dev/null +++ b/src/test/java/edu/stevens/smartpark/ParkingLotTest.java @@ -0,0 +1,67 @@ +package edu.stevens.smartpark; + +/** Dependency-free tests that fail the process when parking rules break. */ +public final class ParkingLotTest { + private ParkingLotTest() { + } + + public static void main(String[] args) { + testArrivalsAndDepartures(); + testFullAndEmptyBoundaries(); + testInvalidStartingValues(); + System.out.println("ParkingLot tests passed"); + } + + private static void testArrivalsAndDepartures() { + ParkingLot lot = new ParkingLot("Babbio Garage", 120, 42); + check(lot.getName().equals("Babbio Garage"), "name"); + check(lot.getCapacity() == 120, "capacity"); + check(lot.getOccupiedSpaces() == 78, "initial occupied spaces"); + lot.vehicleEnters(); + check(lot.getAvailableSpaces() == 41, "arrival decreases availability"); + lot.vehicleLeaves(); + check(lot.getAvailableSpaces() == 42, "departure restores availability"); + } + + private static void testFullAndEmptyBoundaries() { + ParkingLot full = new ParkingLot("River Lot", 1, 0); + check(full.isFull(), "zero available spaces means full"); + expectIllegalState(full::vehicleEnters); + full.vehicleLeaves(); + check(full.getAvailableSpaces() == 1, "departure from full lot"); + expectIllegalState(full::vehicleLeaves); + full.vehicleEnters(); + check(full.isFull(), "arrival fills the last space"); + } + + private static void testInvalidStartingValues() { + expectIllegalArgument(() -> new ParkingLot("", 10, 5)); + expectIllegalArgument(() -> new ParkingLot("Lot", -1, 0)); + expectIllegalArgument(() -> new ParkingLot("Lot", 10, -1)); + expectIllegalArgument(() -> new ParkingLot("Lot", 10, 11)); + } + + private static void check(boolean condition, String description) { + if (!condition) { + throw new AssertionError("Failed: " + description); + } + } + + private static void expectIllegalState(Runnable action) { + try { + action.run(); + } catch (IllegalStateException expected) { + return; + } + throw new AssertionError("Expected IllegalStateException"); + } + + private static void expectIllegalArgument(Runnable action) { + try { + action.run(); + } catch (IllegalArgumentException expected) { + return; + } + throw new AssertionError("Expected IllegalArgumentException"); + } +} -- 2.54.0 From 7040a00708cbb3d8ca4d414a623cd581517671f3 Mon Sep 17 00:00:00 2001 From: Jacob Date: Thu, 8 Oct 2026 16:12:43 -0400 Subject: [PATCH 2/3] Demonstrate failing Jenkins check (refs #9) --- src/test/java/edu/stevens/smartpark/ParkingLotTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/edu/stevens/smartpark/ParkingLotTest.java b/src/test/java/edu/stevens/smartpark/ParkingLotTest.java index afd1ff0..218851d 100644 --- a/src/test/java/edu/stevens/smartpark/ParkingLotTest.java +++ b/src/test/java/edu/stevens/smartpark/ParkingLotTest.java @@ -18,7 +18,7 @@ public final class ParkingLotTest { check(lot.getCapacity() == 120, "capacity"); check(lot.getOccupiedSpaces() == 78, "initial occupied spaces"); lot.vehicleEnters(); - check(lot.getAvailableSpaces() == 41, "arrival decreases availability"); + check(lot.getAvailableSpaces() == 40, "arrival decreases availability"); lot.vehicleLeaves(); check(lot.getAvailableSpaces() == 42, "departure restores availability"); } -- 2.54.0 From 1a5f2b4288ecb95fe4ce21fd649e2bf9113596df Mon Sep 17 00:00:00 2001 From: Jacob Date: Thu, 8 Oct 2026 16:24:10 -0400 Subject: [PATCH 3/3] Repair parking availability test (refs #9) --- src/test/java/edu/stevens/smartpark/ParkingLotTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/edu/stevens/smartpark/ParkingLotTest.java b/src/test/java/edu/stevens/smartpark/ParkingLotTest.java index 218851d..afd1ff0 100644 --- a/src/test/java/edu/stevens/smartpark/ParkingLotTest.java +++ b/src/test/java/edu/stevens/smartpark/ParkingLotTest.java @@ -18,7 +18,7 @@ public final class ParkingLotTest { check(lot.getCapacity() == 120, "capacity"); check(lot.getOccupiedSpaces() == 78, "initial occupied spaces"); lot.vehicleEnters(); - check(lot.getAvailableSpaces() == 40, "arrival decreases availability"); + check(lot.getAvailableSpaces() == 41, "arrival decreases availability"); lot.vehicleLeaves(); check(lot.getAvailableSpaces() == 42, "departure restores availability"); } -- 2.54.0