diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 0000000..ddc0a0c --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,46 @@ +# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Java CI with Maven + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: ubuntu-latest + environment: default + env: + SONAR_ORGANIZATION: se-ubt + SONAR_PROJECT_KEY: se-ubt_se24-ci-demo + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: maven + + - name: Build with Maven + run: mvn -B package --file pom.xml + + #- name: Scan with SonarCloud + # run: mvn clean verify sonar:sonar -Dsonar.token=${{ secrets.SONAR_TOKEN }} -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=$SONAR_ORGANIZATION -Dsonar.projectKey=$SONAR_PROJECT_KEY + + # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive + #- name: Update dependency graph + # uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ad23210 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +###################### +# OS generated files # +###################### +*~ +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +#Icon? +ehthumbs.db +Thumbs.db + +############## +# Java/Scala # +############## +*.class + +########################## +# Webstorm/IntelliJ IDEA # +########################## +.idea/ +*.iml + +######### +# Excel # +######### +~$* + +########### +# RStudio # +########### +.Rhistory +.RData + +######### +# Maven # +########## +target/ + +######## +# Misc # +######## +*.log +*.log.* +*.lck diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..5877eb8 --- /dev/null +++ b/.tool-versions @@ -0,0 +1,4 @@ +git 2.44.0 +java temurin-21.0.3+9.0.LTS +maven 3.9.6 + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a4659bc --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2020 Sebastian Baltes + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4276c68 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Software Engineering Summer 2024 + +This repository is used to showcase concepts such as build automation, code quality monitoring, continuous integration, +behavior-driven development, as well as architectures for web applications. diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..0255ef5 --- /dev/null +++ b/pom.xml @@ -0,0 +1,236 @@ + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 3.2.5 + + + + List + de.unibayreuth.se.teaching + list + 0.4.0 + https://github.com/se-ubt/se24-assignment4 + + + 21 + + + UTF-8 + UTF-8 + + + ${java.version} + ${java.version} + de.unibayreuth.se.teaching.list.Main + de.unibayreuth.se.teaching.list.CucumberTestRunner + + + + 3.13.0 + + 3.2.5 + + 3.3.2 + + 4.0.0-M13 + + 3.5.0 + + 3.22.0 + + 0.8.12 + + 3.11.0.3922 + + + + 5.11.0-M1 + + 1.11.0-M1 + + 2.2 + + 7.17.0 + + + + 1.18.32 + + + + + maven-central + maven-central + https://repo1.maven.org/maven2 + + + + + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + test + + + org.junit.platform + junit-platform-suite + ${junit.platform.suite.version} + test + + + org.hamcrest + hamcrest + ${hamcrest.version} + test + + + org.projectlombok + lombok + ${lombok.version} + provided + + + io.cucumber + cucumber-java + ${cucumber.version} + test + + + io.cucumber + cucumber-junit-platform-engine + ${cucumber.version} + test + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework.boot + spring-boot-starter-test + + + + + + + maven-compiler-plugin + ${maven.plugin.compiler.version} + + ${java.version} + ${maven.compiler.source} + ${maven.compiler.target} + + -proc:full + + + + + maven-surefire-plugin + ${maven.plugin.surefire.version} + + ${maven.cucumber.testsuite} + + + + org.apache.maven.plugins + maven-jxr-plugin + ${maven.plugin.jxr.version} + + + generate-cross-references + site + + jxr + + + + + + org.apache.maven.plugins + maven-pmd-plugin + ${maven.plugin.pmd.version} + + true + + + + generate-pmd-report + verify + + check + cpd-check + + + + + + org.jacoco + jacoco-maven-plugin + ${maven.plugin.jacoco.version} + + + prepare-agent + + prepare-agent + + + + report + + report + + + + XML + + + + + + + **/Main.class + + + + + org.sonarsource.scanner.maven + sonar-maven-plugin + ${maven.plugin.sonar.scanner.version} + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + + + + org.apache.maven.plugins + maven-jxr-plugin + ${maven.plugin.jxr.version} + + + + + diff --git a/src/main/java/de/unibayreuth/se/teaching/list/Application.java b/src/main/java/de/unibayreuth/se/teaching/list/Application.java new file mode 100644 index 0000000..1f9c3c2 --- /dev/null +++ b/src/main/java/de/unibayreuth/se/teaching/list/Application.java @@ -0,0 +1,11 @@ +package de.unibayreuth.se.teaching.list; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/src/main/java/de/unibayreuth/se/teaching/list/DoublyLinkedList.java b/src/main/java/de/unibayreuth/se/teaching/list/DoublyLinkedList.java new file mode 100644 index 0000000..6b1458a --- /dev/null +++ b/src/main/java/de/unibayreuth/se/teaching/list/DoublyLinkedList.java @@ -0,0 +1,182 @@ +package de.unibayreuth.se.teaching.list; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Doubly linked list + */ +@Getter +@Setter +@Slf4j +public class DoublyLinkedList { + private Element start; + private Element end; + private int length; + + private static final Logger logger = LoggerFactory.getLogger(DoublyLinkedList.class); + + /** + * Add an element at the end of the list + * @param e New list element + * @param elementValidation If true, the method checks whether the element is already part of a list + */ + public void append(Element e, boolean elementValidation) { + if (elementValidation && (e.getPrev() != null || e.getNext() != null)) { + throw new IllegalArgumentException("Element is already part of a list."); + } + + if (start == null) { + start = e; + end = e; + e.setPrev(null); + e.setNext(null); + } else { + Element tmp = end; + end = e; + tmp.setNext(e); + e.setPrev(tmp); + e.setNext(null); + } + length++; + } + + /** + * Add an element at the end of the list with element validation enabled + * @param e New list element + */ + public void append(Element e) { + append(e, true); + } + + /** + * Create a new element with the provided value and append it to the list + * @param value Value of the new list element + */ + public void append(double value) { + append(new Element(value)); + } + + /** + * Append all elements in the provided list to this list + * @param list The list of which the elements should be appended + */ + public void append(DoublyLinkedList list) { + Element e = list.getStart(); + while (e != null) { + Element next = e.getNext(); // append sets e.next to null + append(e, false); + e = next; + } + } + + /** + * Create one element per value in the array and append the new elements to this list + * @param values Array with values to append + */ + public void append(double[] values) { + for (double value : values) { + append(value); + } + } + + /** + * Create a new array with the elements in this list (in the same order) + * @return Array with list elements (same order) + */ + public double[] asArray() { + double[] array = new double[length]; + Element element = start; + int arrayPos = 0; + while (element != null) { + array[arrayPos] = element.value; + arrayPos++; + element = element.next; + } + return array; + } + + /** + * Print the list to the standard output + */ + @Override + public String toString() { + if (isEmpty()) { + return "Empty."; + } + StringBuilder stringBuilder = new StringBuilder(); + Element pos = start; + stringBuilder.append(start.getValue()); + pos = pos.next; + while (pos != null) { + stringBuilder.append("<->").append(pos.getValue()); + pos = pos.next; + } + return stringBuilder.toString(); + } + + /** + * Remove all list elements + */ + public void empty() { + start = null; + end = null; + length = 0; + } + + /** + * Checks whether list does not contain any elements + * @return true if list is empty + */ + public boolean isEmpty() { + return start == null && end == null && length == 0; + } + + /** + * Add an element at the correct position in a sorted list + * @param e Element to insert into the sorted list + */ + public void insert(Element e) { + if (isEmpty()) { // If the list is empty, just append the new element + append(e); + } else { // Otherwise the element needs to be sorted in... + Element pos = start; + Element pred = null; + // Find position pos, before which the element is supposed to be located + while (pos != null && pos.getValue() < e.getValue()) { + pred = pos; + pos = pos.getNext(); + } + if (pos == null) { // There is no larger element => append new element to the list + append(e); + } else { // Add the new element before element at pos + e.setNext(pos); + pos.setPrev(e); + if (pred != null) { // If pos is not first element in list... + e.setPrev(pred); + pred.setNext(e); + } else { // If pos is the first element in the list... + start = e; + } + length++; + } + } + } + + /** + * Inner class for doubly linked list elements + */ + @Setter + @Getter + @RequiredArgsConstructor + public static class Element { + private final double value; + private Element next; + private Element prev; + } + +} diff --git a/src/main/java/de/unibayreuth/se/teaching/list/Main.java b/src/main/java/de/unibayreuth/se/teaching/list/Main.java new file mode 100644 index 0000000..a55fa4b --- /dev/null +++ b/src/main/java/de/unibayreuth/se/teaching/list/Main.java @@ -0,0 +1,22 @@ +package de.unibayreuth.se.teaching.list; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Main class for demo purposes. + */ +public class Main { + private static final Logger logger = LoggerFactory.getLogger(Main.class); + + public static void main(String[] args) { + + // Create list and add some values... + DoublyLinkedList list = new DoublyLinkedList(); + list.append(new double[]{0.5, 4.2, 3.3, 0.9}); + + logger.info("The list contains the following elements:"); + String listAsString = list.toString(); + logger.info(listAsString); + } +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml new file mode 100644 index 0000000..3c508a6 --- /dev/null +++ b/src/main/resources/application.yaml @@ -0,0 +1,3 @@ +spring: + application: + name: se24-linked-list diff --git a/src/test/java/de/unibayreuth/se/teaching/list/CucumberSteps.java b/src/test/java/de/unibayreuth/se/teaching/list/CucumberSteps.java new file mode 100644 index 0000000..f91270e --- /dev/null +++ b/src/test/java/de/unibayreuth/se/teaching/list/CucumberSteps.java @@ -0,0 +1,73 @@ +package de.unibayreuth.se.teaching.list; + +import io.cucumber.java.Before; +import io.cucumber.java.en.Given; +import io.cucumber.java.en.When; +import io.cucumber.java.en.Then; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class CucumberSteps { + private static final Logger logger = LoggerFactory.getLogger(CucumberSteps.class); + + private DoublyLinkedList list; + private double value; + private double[] arrayFromValues; + private double[] arrayFromList; + + @Before + public void initialization() { + list = new DoublyLinkedList(); + } + + @Given("^an empty list$") + public void anEmptyList() { + assertTrue(list.isEmpty()); + } + + @When("^I append an element with value (\\d+.\\d+)$") + public void iAppendAnElementWithValue(double value) { + this.value = value; + list.append(value); + } + + @Then("^the list should contain that element$") + public void theListShouldContainThatElement() { + assertArrayEquals(new double[]{value}, list.asArray()); + } + + @Given("^I have elements with the following values in my list:$") + public void iHaveElementsWithTheFollowingValuesInMyList(List values) { + values.forEach(list::append); + arrayFromValues = values.stream().mapToDouble(Double::doubleValue).toArray(); + } + + @When("^I convert the list to an array$") + public void iConvertTheListToAnArray() { + arrayFromList = list.asArray(); + } + + @Then("^the array should contain the same elements in the same order$") + public void theArrayShouldContainTheSameElementsInTheSameOrder() { + assertArrayEquals(arrayFromValues, arrayFromList); + } + + @When("^I invert the list$") + public void iInvertTheList() { + logger.info("%s not implemented yet.".formatted(Thread.currentThread().getStackTrace()[1].getMethodName())); + } + + @Then("^the list should contain the elements in the following order:$") + public void theListShouldContainTheElementsInTheFollowingOrder(List values) { + logger.info("%s not implemented yet.".formatted(Thread.currentThread().getStackTrace()[1].getMethodName())); + } + + @Then("the list should contain {int} element(s)") + public void theListShouldContainElement(int count) { + assertEquals(count, list.getLength()); + } +} diff --git a/src/test/java/de/unibayreuth/se/teaching/list/CucumberTestRunner.java b/src/test/java/de/unibayreuth/se/teaching/list/CucumberTestRunner.java new file mode 100644 index 0000000..b4133ac --- /dev/null +++ b/src/test/java/de/unibayreuth/se/teaching/list/CucumberTestRunner.java @@ -0,0 +1,11 @@ +package de.unibayreuth.se.teaching.list; + +import io.cucumber.junit.platform.engine.Constants; +import org.junit.platform.suite.api.*; + +@Suite +@SelectPackages("de.unibayreuth.se.teaching.list") +@ConfigurationParameter(key = Constants.PLUGIN_PROPERTY_NAME,value = "pretty, html:target/cucumber-report/cucumber.html") +public class CucumberTestRunner { + +} \ No newline at end of file diff --git a/src/test/java/de/unibayreuth/se/teaching/list/DoublyLinkedListTest.java b/src/test/java/de/unibayreuth/se/teaching/list/DoublyLinkedListTest.java new file mode 100644 index 0000000..48093a9 --- /dev/null +++ b/src/test/java/de/unibayreuth/se/teaching/list/DoublyLinkedListTest.java @@ -0,0 +1,245 @@ +package de.unibayreuth.se.teaching.list; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class DoublyLinkedListTest { + private DoublyLinkedList list; + + @BeforeEach + void setUp() { + list = new DoublyLinkedList(); + } + + @Test + void testAppendElement() { + // given: the list is empty... + assertEquals(0, list.getLength()); + var element = new DoublyLinkedList.Element(0.9); + // when: appending an element + list.append(element); + // then: the list has one element... + assertEquals(1, list.getLength()); + // ...and the new element is the start and end of the list + assertEquals(element, list.getStart()); + assertEquals(element, list.getEnd()); + // ...and the start and end pointers do not have predecessors or successors + assertNull(list.getStart().getPrev()); + assertNull(list.getStart().getNext()); + assertNull(list.getEnd().getPrev()); + assertNull(list.getEnd().getNext()); + } + + @Test + void testInsertNewMinimum() { + // given: the list as four sorted elements + assertTrue(list.isEmpty()); + list.append(new double[]{0.2, 0.4, 0.5, 0.8}); + // when: inserting a new element with a value smaller than the minimum of the list + list.insert(new DoublyLinkedList.Element(0.1)); + // then: the new element is the start of the list + assertArrayEquals(new double[]{0.1, 0.2, 0.4, 0.5, 0.8}, list.asArray()); + } + + @Test + void testInsertNewMaximum() { + // given: the list as four sorted elements + assertTrue(list.isEmpty()); + list.append(new double[]{0.2, 0.4, 0.5, 0.8}); + // when: inserting a new element with a value greater than the maximum of the list + list.insert(new DoublyLinkedList.Element(0.9)); + // then: the new element is the end of the list + assertArrayEquals(new double[]{0.2, 0.4, 0.5, 0.8, 0.9}, list.asArray()); + } + + @Test + void testInsertNeitherMinimumNorMaximum() { + // given: the list as four sorted elements + assertTrue(list.isEmpty()); + list.append(new double[]{0.2, 0.4, 0.5, 0.8}); + // when: inserting a new element with a value between the minimum and the maximum of the list + list.insert(new DoublyLinkedList.Element(0.6)); + // then: the new element is placed in the right position + assertArrayEquals(new double[]{0.2, 0.4, 0.5, 0.6, 0.8}, list.asArray()); + } + + @Test + void testAppendElementFromOtherList() { + // give: an empty list and another list with three elements + assertTrue(list.isEmpty()); + DoublyLinkedList otherList = new DoublyLinkedList(); + otherList.append(new double[]{0.9, 0.5, 0.4}); + // when: appending the first element from the other list + // then: expect an exception to be raised + var firstElement = otherList.getStart(); + assertThrows(IllegalArgumentException.class, () -> list.append(firstElement)); + // when: appending the second element from the other list + // then: expect an exception to be raised + var secondElement = otherList.getStart().getNext(); + assertThrows(IllegalArgumentException.class, () -> list.append(secondElement)); + // when: appending the third element from the other list + // then: expect an exception to be raised + var lastElement = otherList.getEnd(); + assertThrows(IllegalArgumentException.class, () -> list.append(lastElement)); + } + + @Test + void testAppendOneElementByValue() { + // given: the list is empty + assertEquals(0, list.getLength()); + // when: appending a new element + list.append(0.9); + // then: the list has one element... + assertEquals(1, list.getLength()); + // ...and the new element is the start and end of the list + assertEquals(0.9, list.getStart().getValue()); + assertEquals(0.9, list.getEnd().getValue()); + // ...and the start and end pointers do not have predecessors or successors + assertNull(list.getStart().getPrev()); + assertNull(list.getStart().getNext()); + assertNull(list.getEnd().getPrev()); + assertNull(list.getEnd().getNext()); + } + + @Test + void testAppendTwoElementsByValue() { + // given: the list is empty + assertEquals(0, list.getLength()); + // when: appending two new elements + list.append(0.9); + list.append(0.5); + // then: the list has two elements... + assertEquals(2, list.getLength()); + // ...and the first element is the start and the second element is the end of the list + assertEquals(0.9, list.getStart().getValue()); + assertEquals(0.5, list.getEnd().getValue()); + // ...and they are correctly linked + assertEquals(0.5, list.getStart().getNext().getValue()); + assertEquals(0.9, list.getEnd().getPrev().getValue()); + // ...and the start and end pointers are correctly set + testBeginEndPointers(list); + } + + @Test + void testAppendThreeElementsByValue() { + // given: the list is empty + assertEquals(0, list.getLength()); + // when: appending three new elements + list.append(0.9); + list.append(0.5); + list.append(0.4); + // then: the list has three elements... + assertEquals(3, list.getLength()); + // ...and the first element is the start and the third element is the end of the list + assertEquals(0.9, list.getStart().getValue()); + assertEquals(0.4, list.getEnd().getValue()); + // ...and all elements are correctly linked + assertEquals(0.5, list.getStart().getNext().getValue()); + assertEquals(0.5, list.getEnd().getPrev().getValue()); + // ...and the start and end pointers are correctly set + testBeginEndPointers(list); + } + + @Test + void testEmptyListToArray() { + // given: the list is empty + assertEquals(0, list.getLength()); + // then: the array representation of the list is equal to an empty array + assertArrayEquals(new double[]{}, list.asArray()); + list.append(0.9); + list.append(0.5); + list.append(0.4); + assertArrayEquals(new double[]{0.9, 0.5, 0.4}, list.asArray()); + } + + @Test + void testListToArray() { + // given: the list is empty + assertEquals(0, list.getLength()); + // when: appending three elements + list.append(0.9); + list.append(0.5); + list.append(0.4); + // then: the array representation of the list is equal to the array with the elements in the same order + assertArrayEquals(new double[]{0.9, 0.5, 0.4}, list.asArray()); + } + + @Test + void testAppendArray() { + // given: the list is empty + assertEquals(0, list.getLength()); + // when: appending an array with three elements + double[] data = new double[]{0.9, 0.5, 0.4}; + list.append(data); + // then: the array representation of the list is equal to the appended array + assertArrayEquals(data, list.asArray()); + } + + @Test + void testIsEmptyOnEmptyList() { + // given: the list is empty + assertTrue(list.isEmpty()); + // then: the array representation of the list is equal to an empty array + assertEquals(0, list.asArray().length); + } + + @Test + void testIsEmptyOnNonEmptyList() { + // given: the list has three elements + assertTrue(list.isEmpty()); + list.append(0.9); + list.append(0.5); + list.append(0.4); + // then: the list is not empty + assertFalse(list.isEmpty()); + // when: emptying the list + list.empty(); + // then: the list is empty again + assertTrue(list.isEmpty()); + } + + @Test + void testAppendOtherList() { + // give: an empty list and another list with three elements + assertTrue(list.isEmpty()); + var data = new double[]{0.9, 0.5, 0.4}; + DoublyLinkedList otherList = new DoublyLinkedList(); + otherList.append(data); + // when: appending the other list + list.append(otherList); + // then: expect the list to contain the elements of the other list in the same order + assertArrayEquals(data, list.asArray()); + } + + @Test + void testToStringEmptyList() { + // given: the list is empty + assertTrue(list.isEmpty()); + // then: the string representation of the list is "Empty." + assertEquals("Empty.", list.toString()); + } + + @Test + void testToStringNonEmptyList() { + // given: the list has three elements + assertTrue(list.isEmpty()); + list.append(0.9); + list.append(0.5); + list.append(0.4); + // then: the string representation of the list is "0.9<->0.5<->0.4" + assertEquals("0.9<->0.5<->0.4", list.toString()); + } + + /** + * Helper method to test begin and end pointers of a doubly linked list + * @param list List to test + */ + private static void testBeginEndPointers(DoublyLinkedList list) { + assertNull(list.getStart().getPrev()); + assertSame(list.getStart().getNext().getPrev(), list.getStart()); + assertNull(list.getEnd().getNext()); + assertSame(list.getEnd().getPrev().getNext(), list.getEnd()); + } +} diff --git a/src/test/resources/de/unibayreuth/se/teaching/list/append-to-list.feature b/src/test/resources/de/unibayreuth/se/teaching/list/append-to-list.feature new file mode 100644 index 0000000..1765644 --- /dev/null +++ b/src/test/resources/de/unibayreuth/se/teaching/list/append-to-list.feature @@ -0,0 +1,17 @@ +Feature: AppendToListAcceptanceTests + This feature appends list elements by value. + + Scenario: Append one element by value + Given an empty list + When I append an element with value 0.4 + Then the list should contain 1 element + And the list should contain that element + + Scenario: Append multiple elements to an empty list + Given an empty list + When I append an element with value 0.2 + And I append an element with value 0.3 + Then the list should contain 2 elements + And the list should contain the elements in the following order: + | 0.2 | + | 0.3 | diff --git a/src/test/resources/de/unibayreuth/se/teaching/list/invert-list.feature b/src/test/resources/de/unibayreuth/se/teaching/list/invert-list.feature new file mode 100644 index 0000000..6cb99e4 --- /dev/null +++ b/src/test/resources/de/unibayreuth/se/teaching/list/invert-list.feature @@ -0,0 +1,14 @@ +Feature: InvertListAcceptanceTests + This feature showcases how one can write acceptance tests for features that are not yet implemented. + Here: Inversion of the list. + + Scenario: Invert the list + Given I have elements with the following values in my list: + | 1.1 | + | 2.3 | + | 4.1 | + When I invert the list + Then the list should contain the elements in the following order: + | 4.1 | + | 2.3 | + | 1.1 | diff --git a/src/test/resources/de/unibayreuth/se/teaching/list/list-to-array.feature b/src/test/resources/de/unibayreuth/se/teaching/list/list-to-array.feature new file mode 100644 index 0000000..63a0473 --- /dev/null +++ b/src/test/resources/de/unibayreuth/se/teaching/list/list-to-array.feature @@ -0,0 +1,16 @@ +Feature: ListToArrayAcceptanceTests + This feature converts the list to an array with the same elements in the same order. + + Scenario: Convert a list with one element + Given I have elements with the following values in my list: + | 1.1 | + When I convert the list to an array + Then the array should contain the same elements in the same order + + Scenario: Convert a list with three elements + Given I have elements with the following values in my list: + | 1.1 | + | 2.0 | + | 3.6 | + When I convert the list to an array + Then the array should contain the same elements in the same order