From 2878559a4a72ea1a01c9c5245b41e9bd91c81831 Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Mon, 7 Mar 2022 21:09:05 +0000 Subject: [PATCH 01/23] AD: add readme change --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a9595e2..35e32f4 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ The topics that'll be covered are: - [ ] Advanced Java: - [X] Exceptions: try, try-catch, try-finally - - [ ] Scanners: + - [ ] Scanners - [X] Design Patterns - [X] Creational - [X] Builder Pattern From 5fb8dc7da25437ab85ee8a2fca15e15238d3e024 Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Tue, 8 Mar 2022 18:30:33 +0000 Subject: [PATCH 02/23] AD: scanner practice added, Unit tests pending --- README.md | 2 +- src/main/java/com/mc/main/Application.java | 2 + src/main/java/com/mc/main/Runner.java | 22 ++--- .../mc/main/advanced/ui/ScannerPractice.java | 62 ++++++++++++++ .../mc/main/advanced/ui/scanner/Console.java | 80 +++++++++++++++++++ .../main/advanced/ui/scanner/ConsoleMock.java | 35 ++++++++ 6 files changed, 193 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/mc/main/advanced/ui/ScannerPractice.java create mode 100644 src/main/java/com/mc/main/advanced/ui/scanner/Console.java create mode 100644 src/main/java/com/mc/main/advanced/ui/scanner/ConsoleMock.java diff --git a/README.md b/README.md index 35e32f4..21525b8 100644 --- a/README.md +++ b/README.md @@ -47,9 +47,9 @@ The topics that'll be covered are: - [X] Behavioural - [X] Observer Pattern - [X] Iterator Pattern + - [ ] File In/Out - [ ] S.O.L.I.D. - [ ] Annotations - - [ ] File In/Out - [ ] Java Pattern/Regular Expression - [ ] Multithreading - [ ] Reflection diff --git a/src/main/java/com/mc/main/Application.java b/src/main/java/com/mc/main/Application.java index fa48d98..a0a67fd 100644 --- a/src/main/java/com/mc/main/Application.java +++ b/src/main/java/com/mc/main/Application.java @@ -2,6 +2,7 @@ import com.mc.main.advanced.DesignPatternPractice; import com.mc.main.advanced.ExceptionPractice; +import com.mc.main.advanced.ui.ScannerPractice; import com.mc.main.oop.ClassApp; import com.mc.main.oop.WrapperClass; import com.mc.main.oop.principles.AbstractionPractice; @@ -73,6 +74,7 @@ public void run() { EnumerationPractice.start(); GenericsPractice.start(); DesignPatternPractice.start(); + ScannerPractice.start(); break; case JAVA8: diff --git a/src/main/java/com/mc/main/Runner.java b/src/main/java/com/mc/main/Runner.java index 7530d13..cde3d2d 100644 --- a/src/main/java/com/mc/main/Runner.java +++ b/src/main/java/com/mc/main/Runner.java @@ -1,11 +1,6 @@ package com.mc.main; -import java.awt.AWTException; -import java.awt.Robot; -import java.awt.event.KeyEvent; -import java.io.IOException; -import java.util.NoSuchElementException; -import java.util.Scanner; +import com.mc.main.advanced.ui.scanner.Console; // Find FlowControlPractice.java to start! public class Runner { @@ -31,9 +26,18 @@ public static void main(String[] args) { System.out.println("HELLO WORLD"); // Continued: - start(); - - System.out.println("Application ENDS"); + try { + start(); + } finally { + // SCANNER close method: + // Clean up active resources at end of application + // finally allows closure post-exception encounter - ensuring closure in most + // if not all error cases + Console.close(); + // Let's return to Console.input()! + + System.out.println("Application ENDS"); + } } protected static void start() { diff --git a/src/main/java/com/mc/main/advanced/ui/ScannerPractice.java b/src/main/java/com/mc/main/advanced/ui/ScannerPractice.java new file mode 100644 index 0000000..8e5a888 --- /dev/null +++ b/src/main/java/com/mc/main/advanced/ui/ScannerPractice.java @@ -0,0 +1,62 @@ +package com.mc.main.advanced.ui; + +import com.mc.main.advanced.ui.scanner.Console; +import com.mc.main.util.Practice; + +// To being, let's review the start method! +public class ScannerPractice extends Practice { + + // We'll use this as an example of several submissions by user entry via mocking + // each space will separate a user submission. + private static final String sampleStreamInput = "Cat Dog Penguin Bird Seagul"; + + // In a basic explanation, Scanners are our best tool for asking users for their + // feedback through user input. + // + // Before we dive in any further, let's look at the Console class! + public static void start() { + printHeader("Scanner: User Input"); + + // Once you've reviewed the Console class, let's look at thi following + try { + // NOTE: + // This redirects input and output streams, effectively allow us + // to provide a stream of potential inputs from our user! + // ~ You do not have to know how this works in order to appreciate that + // we are faking user input. + Console.mockSetup(); + Console.setMockInputStreamMessage(sampleStreamInput); + + // When a Scanner users .next() it attempts to reach into the System.in + // to extract the next byte of information provided to the system input. + // However this process will freeze the application and wait for the user submission + // before proceeding past Scanner.next();! + // + // In this practice class, we have already run it as part of a collective set of practice applications + // and in testing to see if the operation will perform correctly - however this is automated. + // As such, and exclusively for this example, Console has been provided with a "Mock class" + // that fakes user input. + System.out.println("User Input 1: " + Console.input()); + System.out.println("User Input 3: " + Console.input()); + System.out.println("User Input 4: " + Console.input()); + + // Whilst the above will be mocked to automatically run, Console.input() or Scanner.next() + // would wait for a user to input a message and press enter to submit the result! + // we can use System.out.println("message here:") to prompt users to provide data + // then extract their response by saving the result of Scanner.next() into a String! + // + // This can allow us to create a truly interactive application - albeit, a console entry application + // but now we have actual opportunities to create a reactive application, that enables us to + // communicate with a user! + } finally { + // NOTE: + // This resets in/out stream, allowing for normal scanner read in and out + // Should anything goes wrong during the mock, we always revert to our default + // input and output streams! + // ~ You do not have to know how this works in order to appreciate that + // we are faking user input. + Console.mockCleanUp(); + } + } + +} diff --git a/src/main/java/com/mc/main/advanced/ui/scanner/Console.java b/src/main/java/com/mc/main/advanced/ui/scanner/Console.java new file mode 100644 index 0000000..8255ebe --- /dev/null +++ b/src/main/java/com/mc/main/advanced/ui/scanner/Console.java @@ -0,0 +1,80 @@ +package com.mc.main.advanced.ui.scanner; + +import java.util.Scanner; + +// This is a bespoke console class, the purpose of this class is to ensure we only have +// one Scanner instance at any given time. +// +// Let's look at the `getScanner()` method: +public class Console extends ConsoleMock { + + // Our only Scanner: + private static Scanner input; + + // We don't want instances of the Console, there should be one and only one Console in this application! + private Console() { super(); } + + private static boolean hasScanner() { + return input != null; + } + + // Anywhere in our application can use this method + // it's the way we'll initialise and access the Scanner object/instance! + public static Scanner getScanner() { + // `new Scanner(System.in);` + // Above is the way we typically create a Scanner instance - much like any other object! + // but you might be thinking - Why put it in a complex class like Console, what's the point?!? + // + // Scanners access a "universal resource" of your machine, the System.in - or input stream + // This is obviously a very important aspect of your access to data entry so it must be treated with care! + // As such, once we use a scanner, our first thought should be - when do we no longer need the system input? + // as well as how is the system input used in our application! + input = !hasScanner() ? new Scanner(System.in) : input; + + // We only store one instance of Scanner - to make more would be pointless - both separate instances + // would access the singular input stream, so we ensure that console can only have 1 instance. + return input; + + // Let's look at the close() method + } + + // This method is the primary method we want to use, again, this is a static method + // we won't need an instance of Console to call this method! + // + // The idea here is that we use the Scanner.next() method which freezes the application until the + // user has input some kind of entry, once the user submits this message - the scanner will + // read from an array/buffer that represents all user input - extracting the "next" collection of + // bytes and translating them into a String. + // + // We can translate the user entry into other data types using .nextInt() or other methods + // specific to a data value - however the user might not always conform to data entry limited + // to numeric data - this can cause exceptions. + // + // With string data we can represent most if not all character data and handle that data in java. + // In fact, we can use Wrapper classes to test if we can translate string data into numeric and handle + // any exceptions after user entry, rather than relying on users to understand how our application works + // before crashing it entirely! + // + // Let's return to ScannerPractice.start! + public static String input() { + return hasScanner() ? input.next() : getScanner().next(); + } + + // As a public static method, we can access this from any location in our application + // and this is useful! + // + // The method Console.close allows us to access Scanner.close() - an important process in allowing + // us to control when the system.in should be closed - once it is closed during the lifecycle of the + // running application, not other instance, new or old of Scanner can use the System.in stream/ + // that is why when we close it - we close it for good. + // + // additionally we wouldn't want our input to be exploited, as such we should close this important + // resource at the end of our application! + // Please review the Runner.main method to observe this in use! + public static void close() { + if( hasScanner() ) { + input.close(); + } + System.out.println("Scanner access to input stream closed!"); + } +} diff --git a/src/main/java/com/mc/main/advanced/ui/scanner/ConsoleMock.java b/src/main/java/com/mc/main/advanced/ui/scanner/ConsoleMock.java new file mode 100644 index 0000000..6eedac1 --- /dev/null +++ b/src/main/java/com/mc/main/advanced/ui/scanner/ConsoleMock.java @@ -0,0 +1,35 @@ +package com.mc.main.advanced.ui.scanner; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; + +// This abstraction allows us to create fake input and output streams +// allowing us "mock" a input stream with potentially pending entries. +public abstract class ConsoleMock { + + private static ByteArrayOutputStream typeOut; + private final static InputStream systemIn = System.in; + private final static PrintStream systemOut = System.out; + private final static String lineSep = System.getProperty("line.separator"); + + public static void mockSetup() { + //typeOut = new ByteArrayOutputStream(); + //System.setOut(new PrintStream(typeOut)); + + System.out.println("Mocking userinput!"); + } + + public static void mockCleanUp() { + System.setIn(systemIn); + System.setOut(systemOut); + System.out.println("Mock input streams reverted!"); + } + + public static void setMockInputStreamMessage(String str) { + str = str.replace("\n", lineSep); + str = str.trim(); + System.setIn(new ByteArrayInputStream(str.getBytes())); + } +} From d228eea169120c4cd37d9c7e0ec212f86f2ee591 Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Tue, 8 Mar 2022 18:57:40 +0000 Subject: [PATCH 03/23] AD: added unit test template for Scanners, TODO: mocking implementation for scanner.close(), tests disabled until implemented --- README.md | 2 +- .../mc/main/advanced/ui/ScannerPractice.java | 6 +- ...unnerUnitTest.java => RunnerUnitTEST.java} | 2 +- ...ava => DesignPatternPracticeUnitTEST.java} | 2 +- ...st.java => ExceptionPracticeUnitTEST.java} | 2 +- ...tTest.java => AnimalIteratorUnitTEST.java} | 2 +- ...tUnitTest.java => AnimalListUnitTEST.java} | 2 +- ...est.java => AdminPostCheckerUnitTEST.java} | 2 +- ...geUnitTest.java => GroupPageUnitTEST.java} | 2 +- ...rUnitTest.java => SubscriberUnitTEST.java} | 2 +- ...ava => CharacterSheetBuilderUnitTEST.java} | 2 +- ...est.java => SingletonExampleUnitTEST.java} | 2 +- ...seUnitTest.java => PizzaBaseUnitTEST.java} | 2 +- ....java => EnglishInteractionsUnitTEST.java} | 2 +- ...va => EnglishToGermanAdapterUnitTEST.java} | 2 +- ...t.java => GermanInteractionsUnitTEST.java} | 2 +- ...heeseUnitTest.java => CheeseUnitTEST.java} | 2 +- ...niUnitTest.java => PepperoniUnitTEST.java} | 2 +- ...tTest.java => PizzaDecoratorUnitTEST.java} | 2 +- ...UnitTest.java => TomatoSauceUnitTEST.java} | 2 +- .../advanced/ui/ScannerPracticeUnitTEST.java | 18 ++++++ .../ui/scanner/ConsoleMockUnitTEST.java | 9 +++ .../advanced/ui/scanner/ConsoleUnitTEST.java | 63 +++++++++++++++++++ ....java => AbstractionPracticeUnitTEST.java} | 2 +- ...ava => EncapsulationPracticeUnitTEST.java} | 2 +- ....java => InheritancePracticeUnitTEST.java} | 2 +- ...java => PolymorphismPracticeUnitTEST.java} | 2 +- ...UnitTest.java => CompactDiskUnitTEST.java} | 2 +- ...{MediaUnitTest.java => MediaUnitTEST.java} | 2 +- .../{CatUnitTest.java => CatUnitTEST.java} | 2 +- .../{DogUnitTest.java => DogUnitTEST.java} | 2 +- 31 files changed, 122 insertions(+), 28 deletions(-) rename src/test/java/com/mc/main/{RunnerUnitTest.java => RunnerUnitTEST.java} (91%) rename src/test/java/com/mc/main/advanced/{DesignPatternPracticeUnitTest.java => DesignPatternPracticeUnitTEST.java} (84%) rename src/test/java/com/mc/main/advanced/{ExceptionPracticeUnitTest.java => ExceptionPracticeUnitTEST.java} (97%) rename src/test/java/com/mc/main/advanced/designpatterns/behavioural/iterator/{AnimalIteratorUnitTest.java => AnimalIteratorUnitTEST.java} (97%) rename src/test/java/com/mc/main/advanced/designpatterns/behavioural/iterator/{AnimalListUnitTest.java => AnimalListUnitTEST.java} (97%) rename src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/{AdminPostCheckerUnitTest.java => AdminPostCheckerUnitTEST.java} (96%) rename src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/{GroupPageUnitTest.java => GroupPageUnitTEST.java} (99%) rename src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/{SubscriberUnitTest.java => SubscriberUnitTEST.java} (98%) rename src/test/java/com/mc/main/advanced/designpatterns/creational/{CharacterSheetBuilderUnitTest.java => CharacterSheetBuilderUnitTEST.java} (98%) rename src/test/java/com/mc/main/advanced/designpatterns/creational/{SingletonExampleUnitTest.java => SingletonExampleUnitTEST.java} (96%) rename src/test/java/com/mc/main/advanced/designpatterns/structural/{PizzaBaseUnitTest.java => PizzaBaseUnitTEST.java} (95%) rename src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/{EnglishInteractionsUnitTest.java => EnglishInteractionsUnitTEST.java} (94%) rename src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/{EnglishToGermanAdapterUnitTest.java => EnglishToGermanAdapterUnitTEST.java} (94%) rename src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/{GermanInteractionsUnitTest.java => GermanInteractionsUnitTEST.java} (94%) rename src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/{CheeseUnitTest.java => CheeseUnitTEST.java} (96%) rename src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/{PepperoniUnitTest.java => PepperoniUnitTEST.java} (96%) rename src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/{PizzaDecoratorUnitTest.java => PizzaDecoratorUnitTEST.java} (96%) rename src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/{TomatoSauceUnitTest.java => TomatoSauceUnitTEST.java} (96%) create mode 100644 src/test/java/com/mc/main/advanced/ui/ScannerPracticeUnitTEST.java create mode 100644 src/test/java/com/mc/main/advanced/ui/scanner/ConsoleMockUnitTEST.java create mode 100644 src/test/java/com/mc/main/advanced/ui/scanner/ConsoleUnitTEST.java rename src/test/java/com/mc/main/oop/principles/{AbstractionPracticeUnitTest.java => AbstractionPracticeUnitTEST.java} (77%) rename src/test/java/com/mc/main/oop/principles/{EncapsulationPracticeUnitTest.java => EncapsulationPracticeUnitTEST.java} (76%) rename src/test/java/com/mc/main/oop/principles/{InheritancePracticeUnitTest.java => InheritancePracticeUnitTEST.java} (97%) rename src/test/java/com/mc/main/oop/principles/{PolymorphismPracticeUnitTest.java => PolymorphismPracticeUnitTEST.java} (77%) rename src/test/java/com/mc/main/oop/principles/res/inherit/{CompactDiskUnitTest.java => CompactDiskUnitTEST.java} (99%) rename src/test/java/com/mc/main/oop/principles/res/inherit/{MediaUnitTest.java => MediaUnitTEST.java} (99%) rename src/test/java/com/mc/main/oop/principles/res/polymorph/{CatUnitTest.java => CatUnitTEST.java} (97%) rename src/test/java/com/mc/main/oop/principles/res/polymorph/{DogUnitTest.java => DogUnitTEST.java} (97%) diff --git a/README.md b/README.md index 21525b8..41f3103 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ The topics that'll be covered are: - [ ] Advanced Java: - [X] Exceptions: try, try-catch, try-finally - - [ ] Scanners + - [X] Scanners - [X] Design Patterns - [X] Creational - [X] Builder Pattern diff --git a/src/main/java/com/mc/main/advanced/ui/ScannerPractice.java b/src/main/java/com/mc/main/advanced/ui/ScannerPractice.java index 8e5a888..7f3c02a 100644 --- a/src/main/java/com/mc/main/advanced/ui/ScannerPractice.java +++ b/src/main/java/com/mc/main/advanced/ui/ScannerPractice.java @@ -10,6 +10,10 @@ public class ScannerPractice extends Practice { // each space will separate a user submission. private static final String sampleStreamInput = "Cat Dog Penguin Bird Seagul"; + private ScannerPractice() { + super(); + } + // In a basic explanation, Scanners are our best tool for asking users for their // feedback through user input. // @@ -38,7 +42,7 @@ public static void start() { // that fakes user input. System.out.println("User Input 1: " + Console.input()); System.out.println("User Input 3: " + Console.input()); - System.out.println("User Input 4: " + Console.input()); + //System.out.println("User Input 4: " + Console.input()); // Whilst the above will be mocked to automatically run, Console.input() or Scanner.next() // would wait for a user to input a message and press enter to submit the result! diff --git a/src/test/java/com/mc/main/RunnerUnitTest.java b/src/test/java/com/mc/main/RunnerUnitTEST.java similarity index 91% rename from src/test/java/com/mc/main/RunnerUnitTest.java rename to src/test/java/com/mc/main/RunnerUnitTEST.java index 189e179..4ee6af6 100644 --- a/src/test/java/com/mc/main/RunnerUnitTest.java +++ b/src/test/java/com/mc/main/RunnerUnitTEST.java @@ -2,7 +2,7 @@ import org.junit.jupiter.api.Test; -public class RunnerUnitTest { +public class RunnerUnitTEST { // Typically app Runners don't contribute a meaningful unit test // in advanced testing you might mock this behaviour or simply test // that the application runs to completion; although this isn't always possible. diff --git a/src/test/java/com/mc/main/advanced/DesignPatternPracticeUnitTest.java b/src/test/java/com/mc/main/advanced/DesignPatternPracticeUnitTEST.java similarity index 84% rename from src/test/java/com/mc/main/advanced/DesignPatternPracticeUnitTest.java rename to src/test/java/com/mc/main/advanced/DesignPatternPracticeUnitTEST.java index 90d5de5..cb3773c 100644 --- a/src/test/java/com/mc/main/advanced/DesignPatternPracticeUnitTest.java +++ b/src/test/java/com/mc/main/advanced/DesignPatternPracticeUnitTEST.java @@ -4,7 +4,7 @@ import org.junit.jupiter.api.Test; -public class DesignPatternPracticeUnitTest { +public class DesignPatternPracticeUnitTEST { @Test public void exceptionStart() { diff --git a/src/test/java/com/mc/main/advanced/ExceptionPracticeUnitTest.java b/src/test/java/com/mc/main/advanced/ExceptionPracticeUnitTEST.java similarity index 97% rename from src/test/java/com/mc/main/advanced/ExceptionPracticeUnitTest.java rename to src/test/java/com/mc/main/advanced/ExceptionPracticeUnitTEST.java index 6684bb0..0192a9b 100644 --- a/src/test/java/com/mc/main/advanced/ExceptionPracticeUnitTest.java +++ b/src/test/java/com/mc/main/advanced/ExceptionPracticeUnitTEST.java @@ -9,7 +9,7 @@ import com.mc.main.advanced.ExceptionPractice; -public class ExceptionPracticeUnitTest { +public class ExceptionPracticeUnitTEST { @Test public void exceptionStart() { diff --git a/src/test/java/com/mc/main/advanced/designpatterns/behavioural/iterator/AnimalIteratorUnitTest.java b/src/test/java/com/mc/main/advanced/designpatterns/behavioural/iterator/AnimalIteratorUnitTEST.java similarity index 97% rename from src/test/java/com/mc/main/advanced/designpatterns/behavioural/iterator/AnimalIteratorUnitTest.java rename to src/test/java/com/mc/main/advanced/designpatterns/behavioural/iterator/AnimalIteratorUnitTEST.java index 19b873b..400de9f 100644 --- a/src/test/java/com/mc/main/advanced/designpatterns/behavioural/iterator/AnimalIteratorUnitTest.java +++ b/src/test/java/com/mc/main/advanced/designpatterns/behavioural/iterator/AnimalIteratorUnitTEST.java @@ -12,7 +12,7 @@ import com.mc.main.oop.principles.res.polymorph.Cat; import com.mc.main.oop.principles.res.polymorph.Dog; -public class AnimalIteratorUnitTest { +public class AnimalIteratorUnitTEST { private static AnimalIterator testIterator; private static Animal[] listOfAnimals; diff --git a/src/test/java/com/mc/main/advanced/designpatterns/behavioural/iterator/AnimalListUnitTest.java b/src/test/java/com/mc/main/advanced/designpatterns/behavioural/iterator/AnimalListUnitTEST.java similarity index 97% rename from src/test/java/com/mc/main/advanced/designpatterns/behavioural/iterator/AnimalListUnitTest.java rename to src/test/java/com/mc/main/advanced/designpatterns/behavioural/iterator/AnimalListUnitTEST.java index 8eda266..9a69719 100644 --- a/src/test/java/com/mc/main/advanced/designpatterns/behavioural/iterator/AnimalListUnitTest.java +++ b/src/test/java/com/mc/main/advanced/designpatterns/behavioural/iterator/AnimalListUnitTEST.java @@ -10,7 +10,7 @@ import com.mc.main.oop.principles.res.polymorph.Cat; import com.mc.main.oop.principles.res.polymorph.Dog; -public class AnimalListUnitTest { +public class AnimalListUnitTEST { private static AnimalList testList; diff --git a/src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/AdminPostCheckerUnitTest.java b/src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/AdminPostCheckerUnitTEST.java similarity index 96% rename from src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/AdminPostCheckerUnitTest.java rename to src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/AdminPostCheckerUnitTEST.java index ac42364..f5aa89d 100644 --- a/src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/AdminPostCheckerUnitTest.java +++ b/src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/AdminPostCheckerUnitTEST.java @@ -8,7 +8,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -public class AdminPostCheckerUnitTest { +public class AdminPostCheckerUnitTEST { private static AdminPostChecker testObserver; private static GroupPage testPage; diff --git a/src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/GroupPageUnitTest.java b/src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/GroupPageUnitTEST.java similarity index 99% rename from src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/GroupPageUnitTest.java rename to src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/GroupPageUnitTEST.java index e611e75..ff9ee16 100644 --- a/src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/GroupPageUnitTest.java +++ b/src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/GroupPageUnitTEST.java @@ -13,7 +13,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -public class GroupPageUnitTest { +public class GroupPageUnitTEST { private static String pageName; private static GroupPage testPage; diff --git a/src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/SubscriberUnitTest.java b/src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/SubscriberUnitTEST.java similarity index 98% rename from src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/SubscriberUnitTest.java rename to src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/SubscriberUnitTEST.java index 080e521..a4b861f 100644 --- a/src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/SubscriberUnitTest.java +++ b/src/test/java/com/mc/main/advanced/designpatterns/behavioural/observer/SubscriberUnitTEST.java @@ -12,7 +12,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -public class SubscriberUnitTest { +public class SubscriberUnitTEST { private static Subscriber testSub; private static String testName; diff --git a/src/test/java/com/mc/main/advanced/designpatterns/creational/CharacterSheetBuilderUnitTest.java b/src/test/java/com/mc/main/advanced/designpatterns/creational/CharacterSheetBuilderUnitTEST.java similarity index 98% rename from src/test/java/com/mc/main/advanced/designpatterns/creational/CharacterSheetBuilderUnitTest.java rename to src/test/java/com/mc/main/advanced/designpatterns/creational/CharacterSheetBuilderUnitTEST.java index c8bf64a..fc84c16 100644 --- a/src/test/java/com/mc/main/advanced/designpatterns/creational/CharacterSheetBuilderUnitTest.java +++ b/src/test/java/com/mc/main/advanced/designpatterns/creational/CharacterSheetBuilderUnitTEST.java @@ -8,7 +8,7 @@ import com.mc.main.advanced.res.CharacterSheet; -public class CharacterSheetBuilderUnitTest { +public class CharacterSheetBuilderUnitTEST { private static CharacterSheetBuilder builder; private static CharacterSheet build; diff --git a/src/test/java/com/mc/main/advanced/designpatterns/creational/SingletonExampleUnitTest.java b/src/test/java/com/mc/main/advanced/designpatterns/creational/SingletonExampleUnitTEST.java similarity index 96% rename from src/test/java/com/mc/main/advanced/designpatterns/creational/SingletonExampleUnitTest.java rename to src/test/java/com/mc/main/advanced/designpatterns/creational/SingletonExampleUnitTEST.java index 788a8b5..aa6f7c7 100644 --- a/src/test/java/com/mc/main/advanced/designpatterns/creational/SingletonExampleUnitTest.java +++ b/src/test/java/com/mc/main/advanced/designpatterns/creational/SingletonExampleUnitTEST.java @@ -6,7 +6,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -public class SingletonExampleUnitTest { +public class SingletonExampleUnitTEST { private static SingletonExample testObj; diff --git a/src/test/java/com/mc/main/advanced/designpatterns/structural/PizzaBaseUnitTest.java b/src/test/java/com/mc/main/advanced/designpatterns/structural/PizzaBaseUnitTEST.java similarity index 95% rename from src/test/java/com/mc/main/advanced/designpatterns/structural/PizzaBaseUnitTest.java rename to src/test/java/com/mc/main/advanced/designpatterns/structural/PizzaBaseUnitTEST.java index cdaaf11..8854f0c 100644 --- a/src/test/java/com/mc/main/advanced/designpatterns/structural/PizzaBaseUnitTest.java +++ b/src/test/java/com/mc/main/advanced/designpatterns/structural/PizzaBaseUnitTEST.java @@ -6,7 +6,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -public class PizzaBaseUnitTest { +public class PizzaBaseUnitTEST { private static PizzaBase testPizza; diff --git a/src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/EnglishInteractionsUnitTest.java b/src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/EnglishInteractionsUnitTEST.java similarity index 94% rename from src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/EnglishInteractionsUnitTest.java rename to src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/EnglishInteractionsUnitTEST.java index 1c73ee0..be28866 100644 --- a/src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/EnglishInteractionsUnitTest.java +++ b/src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/EnglishInteractionsUnitTEST.java @@ -6,7 +6,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -public class EnglishInteractionsUnitTest { +public class EnglishInteractionsUnitTEST { private static EnglishInteractions testIteraction; diff --git a/src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/EnglishToGermanAdapterUnitTest.java b/src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/EnglishToGermanAdapterUnitTEST.java similarity index 94% rename from src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/EnglishToGermanAdapterUnitTest.java rename to src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/EnglishToGermanAdapterUnitTEST.java index 7b92b8a..8727356 100644 --- a/src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/EnglishToGermanAdapterUnitTest.java +++ b/src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/EnglishToGermanAdapterUnitTEST.java @@ -6,7 +6,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -public class EnglishToGermanAdapterUnitTest { +public class EnglishToGermanAdapterUnitTEST { private static EnglishToGermanAdapter testAdapter; diff --git a/src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/GermanInteractionsUnitTest.java b/src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/GermanInteractionsUnitTEST.java similarity index 94% rename from src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/GermanInteractionsUnitTest.java rename to src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/GermanInteractionsUnitTEST.java index 7014bf9..b9e4105 100644 --- a/src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/GermanInteractionsUnitTest.java +++ b/src/test/java/com/mc/main/advanced/designpatterns/structural/adapter/GermanInteractionsUnitTEST.java @@ -6,7 +6,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -public class GermanInteractionsUnitTest { +public class GermanInteractionsUnitTEST { private static GermanInteractions testIteraction; diff --git a/src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/CheeseUnitTest.java b/src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/CheeseUnitTEST.java similarity index 96% rename from src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/CheeseUnitTest.java rename to src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/CheeseUnitTEST.java index 70b55ad..bc64b3e 100644 --- a/src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/CheeseUnitTest.java +++ b/src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/CheeseUnitTEST.java @@ -8,7 +8,7 @@ import com.mc.main.advanced.designpatterns.structural.PizzaBase; -public class CheeseUnitTest { +public class CheeseUnitTEST { private static Cheese testDecoration; diff --git a/src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/PepperoniUnitTest.java b/src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/PepperoniUnitTEST.java similarity index 96% rename from src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/PepperoniUnitTest.java rename to src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/PepperoniUnitTEST.java index f9b4da8..66e5f6f 100644 --- a/src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/PepperoniUnitTest.java +++ b/src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/PepperoniUnitTEST.java @@ -8,7 +8,7 @@ import com.mc.main.advanced.designpatterns.structural.PizzaBase; -public class PepperoniUnitTest { +public class PepperoniUnitTEST { private static Pepperoni testDecoration; diff --git a/src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/PizzaDecoratorUnitTest.java b/src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/PizzaDecoratorUnitTEST.java similarity index 96% rename from src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/PizzaDecoratorUnitTest.java rename to src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/PizzaDecoratorUnitTEST.java index 0a3a8af..377418b 100644 --- a/src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/PizzaDecoratorUnitTest.java +++ b/src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/PizzaDecoratorUnitTEST.java @@ -9,7 +9,7 @@ import com.mc.main.advanced.designpatterns.structural.PizzaBase; -public class PizzaDecoratorUnitTest { +public class PizzaDecoratorUnitTEST { private static Cheese testDecorator; diff --git a/src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/TomatoSauceUnitTest.java b/src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/TomatoSauceUnitTEST.java similarity index 96% rename from src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/TomatoSauceUnitTest.java rename to src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/TomatoSauceUnitTEST.java index e64c59f..d2387c0 100644 --- a/src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/TomatoSauceUnitTest.java +++ b/src/test/java/com/mc/main/advanced/designpatterns/structural/decorator/TomatoSauceUnitTEST.java @@ -9,7 +9,7 @@ import com.mc.main.advanced.designpatterns.structural.PizzaBase; -public class TomatoSauceUnitTest { +public class TomatoSauceUnitTEST { private static TomatoSauce testDecoration; diff --git a/src/test/java/com/mc/main/advanced/ui/ScannerPracticeUnitTEST.java b/src/test/java/com/mc/main/advanced/ui/ScannerPracticeUnitTEST.java new file mode 100644 index 0000000..149fb9f --- /dev/null +++ b/src/test/java/com/mc/main/advanced/ui/ScannerPracticeUnitTEST.java @@ -0,0 +1,18 @@ +package com.mc.main.advanced.ui; + +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.Test; + +public class ScannerPracticeUnitTEST { + + @Test + public void exceptionStart() { + try { + ScannerPractice.start(); + }catch (Exception e) { + fail(); + } + } + +} diff --git a/src/test/java/com/mc/main/advanced/ui/scanner/ConsoleMockUnitTEST.java b/src/test/java/com/mc/main/advanced/ui/scanner/ConsoleMockUnitTEST.java new file mode 100644 index 0000000..a3d25b9 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/ui/scanner/ConsoleMockUnitTEST.java @@ -0,0 +1,9 @@ +package com.mc.main.advanced.ui.scanner; + +// This abstraction allows us to create fake input and output streams +// allowing us "mock" a input stream with potentially pending entries. +public abstract class ConsoleMockUnitTEST { + + //TODO: Mocking + +} diff --git a/src/test/java/com/mc/main/advanced/ui/scanner/ConsoleUnitTEST.java b/src/test/java/com/mc/main/advanced/ui/scanner/ConsoleUnitTEST.java new file mode 100644 index 0000000..a614a58 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/ui/scanner/ConsoleUnitTEST.java @@ -0,0 +1,63 @@ +package com.mc.main.advanced.ui.scanner; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Scanner; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +public class ConsoleUnitTEST { + + //TODO: add tests to control private static context + //TODO: Mocking + private static final String userInput = "TEST_WORD"; + private static final String div = "=".repeat(40); + private static int testCount; + + @BeforeEach + @Disabled + public void beforeTest() { + System.out.println(div+"\nScanner Test #" + ++testCount + "\n"+div); + Console.mockSetup(); + Console.setMockInputStreamMessage(userInput); + } + + @AfterEach + @Disabled + public void afterTest() { + Console.mockCleanUp(); + System.out.println("\nTest #" + testCount + " END\n" + div + "\n"); + } + + @AfterAll + @Disabled + public static void CleanUp() { + System.out.println(div+"\nCleaning Up Test Resources\n" + div); + Console.mockCleanUp(); + // Console.close(); - closed as part of runner test/conflict occurs + // issue associated with lack of application wide object mocking capabilities + // TODO: implement tests with Mocking + } + + @Test + @Disabled + public void getScannerTest() { + Scanner testScanner = Console.getScanner(); + assertTrue(testScanner instanceof Scanner); + assertTrue(Console.getScanner() instanceof Scanner); + assertEquals(testScanner, Console.getScanner() ); + } + + @Test + @Disabled + public void inputTest() { + String expect = userInput; + assertEquals(expect, Console.input()); + } + +} diff --git a/src/test/java/com/mc/main/oop/principles/AbstractionPracticeUnitTest.java b/src/test/java/com/mc/main/oop/principles/AbstractionPracticeUnitTEST.java similarity index 77% rename from src/test/java/com/mc/main/oop/principles/AbstractionPracticeUnitTest.java rename to src/test/java/com/mc/main/oop/principles/AbstractionPracticeUnitTEST.java index c597a1c..92774ed 100644 --- a/src/test/java/com/mc/main/oop/principles/AbstractionPracticeUnitTest.java +++ b/src/test/java/com/mc/main/oop/principles/AbstractionPracticeUnitTEST.java @@ -2,7 +2,7 @@ import org.junit.jupiter.api.Test; -public class AbstractionPracticeUnitTest { +public class AbstractionPracticeUnitTEST { @Test public void startTest() { diff --git a/src/test/java/com/mc/main/oop/principles/EncapsulationPracticeUnitTest.java b/src/test/java/com/mc/main/oop/principles/EncapsulationPracticeUnitTEST.java similarity index 76% rename from src/test/java/com/mc/main/oop/principles/EncapsulationPracticeUnitTest.java rename to src/test/java/com/mc/main/oop/principles/EncapsulationPracticeUnitTEST.java index ac1fa16..c372fc2 100644 --- a/src/test/java/com/mc/main/oop/principles/EncapsulationPracticeUnitTest.java +++ b/src/test/java/com/mc/main/oop/principles/EncapsulationPracticeUnitTEST.java @@ -2,7 +2,7 @@ import org.junit.jupiter.api.Test; -public class EncapsulationPracticeUnitTest { +public class EncapsulationPracticeUnitTEST { @Test public void startTest() { diff --git a/src/test/java/com/mc/main/oop/principles/InheritancePracticeUnitTest.java b/src/test/java/com/mc/main/oop/principles/InheritancePracticeUnitTEST.java similarity index 97% rename from src/test/java/com/mc/main/oop/principles/InheritancePracticeUnitTest.java rename to src/test/java/com/mc/main/oop/principles/InheritancePracticeUnitTEST.java index 3b73668..161b429 100644 --- a/src/test/java/com/mc/main/oop/principles/InheritancePracticeUnitTest.java +++ b/src/test/java/com/mc/main/oop/principles/InheritancePracticeUnitTEST.java @@ -8,7 +8,7 @@ import com.mc.main.oop.principles.res.inherit.Book; -public class InheritancePracticeUnitTest { +public class InheritancePracticeUnitTEST { private final Book myBook = new Book("Example Book Title","Alex", "20/02/2022", "Educational", 202); private InheritancePractice.Library exampleLibrary; diff --git a/src/test/java/com/mc/main/oop/principles/PolymorphismPracticeUnitTest.java b/src/test/java/com/mc/main/oop/principles/PolymorphismPracticeUnitTEST.java similarity index 77% rename from src/test/java/com/mc/main/oop/principles/PolymorphismPracticeUnitTest.java rename to src/test/java/com/mc/main/oop/principles/PolymorphismPracticeUnitTEST.java index f8b5a35..732ac04 100644 --- a/src/test/java/com/mc/main/oop/principles/PolymorphismPracticeUnitTest.java +++ b/src/test/java/com/mc/main/oop/principles/PolymorphismPracticeUnitTEST.java @@ -2,7 +2,7 @@ import org.junit.jupiter.api.Test; -public class PolymorphismPracticeUnitTest { +public class PolymorphismPracticeUnitTEST { @Test public void startTest() { diff --git a/src/test/java/com/mc/main/oop/principles/res/inherit/CompactDiskUnitTest.java b/src/test/java/com/mc/main/oop/principles/res/inherit/CompactDiskUnitTEST.java similarity index 99% rename from src/test/java/com/mc/main/oop/principles/res/inherit/CompactDiskUnitTest.java rename to src/test/java/com/mc/main/oop/principles/res/inherit/CompactDiskUnitTEST.java index 2949878..11126b3 100644 --- a/src/test/java/com/mc/main/oop/principles/res/inherit/CompactDiskUnitTest.java +++ b/src/test/java/com/mc/main/oop/principles/res/inherit/CompactDiskUnitTEST.java @@ -11,7 +11,7 @@ import com.mc.main.oop.principles.res.inherit.CompactDisk; import com.mc.main.oop.principles.res.inherit.Media; -public class CompactDiskUnitTest { +public class CompactDiskUnitTEST { private CompactDisk testCD; diff --git a/src/test/java/com/mc/main/oop/principles/res/inherit/MediaUnitTest.java b/src/test/java/com/mc/main/oop/principles/res/inherit/MediaUnitTEST.java similarity index 99% rename from src/test/java/com/mc/main/oop/principles/res/inherit/MediaUnitTest.java rename to src/test/java/com/mc/main/oop/principles/res/inherit/MediaUnitTEST.java index f6556aa..81f9a90 100644 --- a/src/test/java/com/mc/main/oop/principles/res/inherit/MediaUnitTest.java +++ b/src/test/java/com/mc/main/oop/principles/res/inherit/MediaUnitTEST.java @@ -6,7 +6,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -public class MediaUnitTest { +public class MediaUnitTEST { private Media testMedia; diff --git a/src/test/java/com/mc/main/oop/principles/res/polymorph/CatUnitTest.java b/src/test/java/com/mc/main/oop/principles/res/polymorph/CatUnitTEST.java similarity index 97% rename from src/test/java/com/mc/main/oop/principles/res/polymorph/CatUnitTest.java rename to src/test/java/com/mc/main/oop/principles/res/polymorph/CatUnitTEST.java index c6787a3..a8286e1 100644 --- a/src/test/java/com/mc/main/oop/principles/res/polymorph/CatUnitTest.java +++ b/src/test/java/com/mc/main/oop/principles/res/polymorph/CatUnitTEST.java @@ -9,7 +9,7 @@ import com.mc.main.oop.principles.res.polymorph.Cat; -public class CatUnitTest { +public class CatUnitTEST { private Cat cat; diff --git a/src/test/java/com/mc/main/oop/principles/res/polymorph/DogUnitTest.java b/src/test/java/com/mc/main/oop/principles/res/polymorph/DogUnitTEST.java similarity index 97% rename from src/test/java/com/mc/main/oop/principles/res/polymorph/DogUnitTest.java rename to src/test/java/com/mc/main/oop/principles/res/polymorph/DogUnitTEST.java index 686245a..5d910d1 100644 --- a/src/test/java/com/mc/main/oop/principles/res/polymorph/DogUnitTest.java +++ b/src/test/java/com/mc/main/oop/principles/res/polymorph/DogUnitTEST.java @@ -9,7 +9,7 @@ import com.mc.main.oop.principles.res.polymorph.Dog; -public class DogUnitTest { +public class DogUnitTEST { private Dog dog; From 3974491756181138d9757aec6815659410405fbd Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Sat, 19 Mar 2022 00:35:22 +0000 Subject: [PATCH 04/23] AD: added i/o practice, pending unit tests --- src/main/java/com/mc/main/Application.java | 2 + .../com/mc/main/advanced/io/FileHandler.java | 124 ++++++++++++++++++ .../com/mc/main/advanced/io/IOPractice.java | 42 ++++++ 3 files changed, 168 insertions(+) create mode 100644 src/main/java/com/mc/main/advanced/io/FileHandler.java create mode 100644 src/main/java/com/mc/main/advanced/io/IOPractice.java diff --git a/src/main/java/com/mc/main/Application.java b/src/main/java/com/mc/main/Application.java index a0a67fd..250ee42 100644 --- a/src/main/java/com/mc/main/Application.java +++ b/src/main/java/com/mc/main/Application.java @@ -2,6 +2,7 @@ import com.mc.main.advanced.DesignPatternPractice; import com.mc.main.advanced.ExceptionPractice; +import com.mc.main.advanced.io.IOPractice; import com.mc.main.advanced.ui.ScannerPractice; import com.mc.main.oop.ClassApp; import com.mc.main.oop.WrapperClass; @@ -75,6 +76,7 @@ public void run() { GenericsPractice.start(); DesignPatternPractice.start(); ScannerPractice.start(); + IOPractice.start(); break; case JAVA8: diff --git a/src/main/java/com/mc/main/advanced/io/FileHandler.java b/src/main/java/com/mc/main/advanced/io/FileHandler.java new file mode 100644 index 0000000..c3557ed --- /dev/null +++ b/src/main/java/com/mc/main/advanced/io/FileHandler.java @@ -0,0 +1,124 @@ +package com.mc.main.advanced.io; + +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Scanner; + +public class FileHandler { + + private FileHandler() { super(); } + + public static boolean writeToNewFile(File file, String msg) { + // In order to create a files we'll need to use the .createNewFile method + // of the File instance, however we'll also want to make changes to this file. + // + // We can use the FileWriter to target a file we'd like to make changes to + try (FileWriter writeToFile = new FileWriter(file.getAbsolutePath())) { + + if (!file.isFile()) { + file.createNewFile(); + } + + writeToFile.write(msg); + + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + public static void readFile(File file, Class readerType) { + // This example is directed to another method + // normally we'd attempt to keep a method focused only on what it should do + // however in this practice there are multiple ways to achieve the same thing + // + // The example below uses the Scanner class to read through a file, but there's + // also a way + + // let's set up a way to capture contents and establish the path for the + // to respective reader. + String fileContents; + String path = file.getAbsolutePath(); + + // review each of the specific methods to generate fileContents + // to explore implementation methods for each approach. + // + // This isn't an exhaustive list, but it shows that there are a few ways + // to read data in an iterative fashion. + if(readerType == Scanner.class) { + fileContents = readScanner(path); + } else if(readerType == BufferedReader.class) { + fileContents = readBuffer(path); + } else if(readerType == FileReader.class) { + fileContents = readFileReader(path); + } else { + fileContents = "Error, could not read file!"; + } + + System.out.println(fileContents); + } + + private static String readScanner(String filePath) { + + StringBuilder fileContents = new StringBuilder(); + + try (Scanner readFile = new Scanner(new File(filePath))) { + + while(readFile.hasNextLine()) { + fileContents.append(readFile.nextLine()); + } + + return fileContents.toString(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + private static String readBuffer(String filePath) { + + StringBuilder fileContents = new StringBuilder(); + String line; + + try ( BufferedReader br = new BufferedReader(new FileReader(new File(filePath))) ) { + + while ((line = br.readLine()) != null) { + fileContents.append(line); + } + + return fileContents.toString(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + private static String readFileReader(String filePath) { + + StringBuilder fileContents = new StringBuilder(); + String line; + + try ( FileReader reader = new FileReader(filePath) ) { + int x; + + while ((x = reader.read()) != -1) { + fileContents.append((char)x); + } + + return fileContents.toString(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + public static boolean deleteTargetFile(File file) { + return file.delete(); + } + +} diff --git a/src/main/java/com/mc/main/advanced/io/IOPractice.java b/src/main/java/com/mc/main/advanced/io/IOPractice.java new file mode 100644 index 0000000..0c7d24f --- /dev/null +++ b/src/main/java/com/mc/main/advanced/io/IOPractice.java @@ -0,0 +1,42 @@ +package com.mc.main.advanced.io; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.util.Scanner; + +import com.mc.main.util.Practice; + +public class IOPractice extends Practice { + + private static String testFolder; + private static File file; + private static final String message = "Hello World!"; + private static final String filePath = "\\src\\main\\resources\\"; + private static final String fileName = "testFile.txt"; + + public static void start() { + printHeader("Input/Output"); + + // In order to create and read folders, we'll need a path in the form of a + // string + // this will allow us to target a location in which to read and write to. + testFolder = new File("").getAbsolutePath() + filePath; + file = new File(testFolder + fileName); + + // Let's explore how to write and read a respective file + // we'll pass our message and file to a class we created called FileHandler. + FileHandler.writeToNewFile(file, message); + + // There are different classes provided that can aid in the reading of the contents of a file + // the follow methods are drive by their respective classes. + FileHandler.readFile(file, Scanner.class); + FileHandler.readFile(file, BufferedReader.class); + FileHandler.readFile(file, FileReader.class); + + // Remove file for rerun purposes + FileHandler.deleteTargetFile(file); + } + + +} From c6ebaf53d1f393c8173ec27303e308ecb834ad2a Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Sat, 19 Mar 2022 01:47:30 +0000 Subject: [PATCH 05/23] AD: added in changes and unit tests --- README.md | 4 +- .../com/mc/main/advanced/io/FileHandler.java | 14 +- .../com/mc/main/advanced/io/IOPractice.java | 8 +- .../main/advanced/io/FileHandlerUnitTEST.java | 129 ++++++++++++++++++ .../main/advanced/io/IOPracticeUnitTEST.java | 18 +++ 5 files changed, 159 insertions(+), 14 deletions(-) create mode 100644 src/test/java/com/mc/main/advanced/io/FileHandlerUnitTEST.java create mode 100644 src/test/java/com/mc/main/advanced/io/IOPracticeUnitTEST.java diff --git a/README.md b/README.md index 41f3103..e829f83 100644 --- a/README.md +++ b/README.md @@ -47,12 +47,12 @@ The topics that'll be covered are: - [X] Behavioural - [X] Observer Pattern - [X] Iterator Pattern - - [ ] File In/Out - - [ ] S.O.L.I.D. + - [X] File In/Out - [ ] Annotations - [ ] Java Pattern/Regular Expression - [ ] Multithreading - [ ] Reflection + - [ ] S.O.L.I.D. --- diff --git a/src/main/java/com/mc/main/advanced/io/FileHandler.java b/src/main/java/com/mc/main/advanced/io/FileHandler.java index c3557ed..3769079 100644 --- a/src/main/java/com/mc/main/advanced/io/FileHandler.java +++ b/src/main/java/com/mc/main/advanced/io/FileHandler.java @@ -6,6 +6,7 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.nio.file.Path; import java.util.Scanner; public class FileHandler { @@ -17,12 +18,7 @@ public static boolean writeToNewFile(File file, String msg) { // of the File instance, however we'll also want to make changes to this file. // // We can use the FileWriter to target a file we'd like to make changes to - try (FileWriter writeToFile = new FileWriter(file.getAbsolutePath())) { - - if (!file.isFile()) { - file.createNewFile(); - } - + try (FileWriter writeToFile = new FileWriter(file.getAbsolutePath())) { writeToFile.write(msg); return true; @@ -32,7 +28,7 @@ public static boolean writeToNewFile(File file, String msg) { } } - public static void readFile(File file, Class readerType) { + public static String readFile(File file, Class readerType) { // This example is directed to another method // normally we'd attempt to keep a method focused only on what it should do // however in this practice there are multiple ways to achieve the same thing @@ -60,7 +56,7 @@ public static void readFile(File file, Class readerType) { fileContents = "Error, could not read file!"; } - System.out.println(fileContents); + return fileContents; } private static String readScanner(String filePath) { @@ -104,7 +100,7 @@ private static String readFileReader(String filePath) { String line; try ( FileReader reader = new FileReader(filePath) ) { - int x; + int x; while ((x = reader.read()) != -1) { fileContents.append((char)x); diff --git a/src/main/java/com/mc/main/advanced/io/IOPractice.java b/src/main/java/com/mc/main/advanced/io/IOPractice.java index 0c7d24f..2915114 100644 --- a/src/main/java/com/mc/main/advanced/io/IOPractice.java +++ b/src/main/java/com/mc/main/advanced/io/IOPractice.java @@ -14,6 +14,8 @@ public class IOPractice extends Practice { private static final String message = "Hello World!"; private static final String filePath = "\\src\\main\\resources\\"; private static final String fileName = "testFile.txt"; + + private IOPractice() { super(); } public static void start() { printHeader("Input/Output"); @@ -30,9 +32,9 @@ public static void start() { // There are different classes provided that can aid in the reading of the contents of a file // the follow methods are drive by their respective classes. - FileHandler.readFile(file, Scanner.class); - FileHandler.readFile(file, BufferedReader.class); - FileHandler.readFile(file, FileReader.class); + System.out.println( FileHandler.readFile(file, Scanner.class) ); + System.out.println( FileHandler.readFile(file, BufferedReader.class) ); + System.out.println( FileHandler.readFile(file, FileReader.class) ); // Remove file for rerun purposes FileHandler.deleteTargetFile(file); diff --git a/src/test/java/com/mc/main/advanced/io/FileHandlerUnitTEST.java b/src/test/java/com/mc/main/advanced/io/FileHandlerUnitTEST.java new file mode 100644 index 0000000..7e292aa --- /dev/null +++ b/src/test/java/com/mc/main/advanced/io/FileHandlerUnitTEST.java @@ -0,0 +1,129 @@ +package com.mc.main.advanced.io; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.Reader; +import java.util.Scanner; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class FileHandlerUnitTEST { + + private static String testFolder; + private static File file; + private static final String message = "Hello World!"; + private static final String filePath = "\\src\\test\\resources\\"; + private static final String fileName = "testFile.txt"; + + @BeforeAll + public static void setup() { + testFolder = new File("").getAbsolutePath() + filePath; + file = new File(testFolder + fileName); + } + + @AfterEach + public void testCleanUp() { + FileHandler.deleteTargetFile(file); + } + + @Test + public void writeToNewFile() { + boolean expect = FileHandler.writeToNewFile(file, message); + + assertTrue(expect); + } + + @Test + public void writeToNewFile_Failure() { + File existingFile = new File(testFolder); + boolean expect = FileHandler.writeToNewFile(existingFile, message); + String msg = FileHandler.readFile(existingFile, Scanner.class); + + System.out.println(msg); + + assertNull(msg); + assertFalse(expect); + } + + @Test + public void writeToNewFile_Scanner() { + FileHandler.writeToNewFile(file, message); + String expect = FileHandler.readFile(file, Scanner.class); + + assertNotNull(expect); + assertEquals(message, expect); + } + + @Test + public void writeToNewFile_ReadBuffer() { + FileHandler.writeToNewFile(file, message); + String expect = FileHandler.readFile(file, BufferedReader.class); + + assertNotNull(expect); + assertEquals(message, expect); + } + + + + @Test + public void writeToNewFile_ReadFileReader() { + FileHandler.writeToNewFile(file, message); + String expect = FileHandler.readFile(file, FileReader.class); + + assertNotNull(expect); + assertEquals(message, expect); + } + + @Test + public void writeToNewFile_ScannerFailure() { + FileHandler.writeToNewFile(file, message); + String expect = FileHandler.readFile(new File(""), Scanner.class); + + assertNull(expect); + assertEquals(null, expect); + } + + @Test + public void writeToNewFile_ReadBufferFailure() { + FileHandler.writeToNewFile(file, message); + String expect = FileHandler.readFile(new File(""), BufferedReader.class); + + assertNull(expect); + assertEquals(null, expect); + } + + @Test + public void writeToNewFile_ReadFileReaderFailure() { + FileHandler.writeToNewFile(new File(""), message); + String expect = FileHandler.readFile(file, FileReader.class); + + assertNull(expect); + assertEquals(null, expect); + } + + @Test + public void writeToNewFile_NoReader() { + FileHandler.writeToNewFile(file, message); + String expect = FileHandler.readFile(file, Reader.class); + + assertNotNull(expect); + assertEquals("Error, could not read file!", expect); + } + + @Test + public void deleteTargetFile() { + FileHandler.deleteTargetFile(file); + + assertFalse(file.exists()); + } + +} diff --git a/src/test/java/com/mc/main/advanced/io/IOPracticeUnitTEST.java b/src/test/java/com/mc/main/advanced/io/IOPracticeUnitTEST.java new file mode 100644 index 0000000..e0d6a16 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/io/IOPracticeUnitTEST.java @@ -0,0 +1,18 @@ +package com.mc.main.advanced.io; + +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.Test; + +public class IOPracticeUnitTEST { + + @Test + public void exceptionStart() { + try { + IOPractice.start(); + }catch (Exception e) { + fail(); + } + } + +} From 7fca5bc3534524f0d9680745eea8b626e22fe849 Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Mon, 21 Mar 2022 00:42:37 +0000 Subject: [PATCH 06/23] AD: added reflection practice, pending unit testing --- src/main/java/com/mc/main/Application.java | 2 + .../mc/main/advanced/ReflectionPractice.java | 168 ++++++++++++++++++ .../advanced/reflect/PrivateCalculator.java | 19 ++ 3 files changed, 189 insertions(+) create mode 100644 src/main/java/com/mc/main/advanced/ReflectionPractice.java create mode 100644 src/main/java/com/mc/main/advanced/reflect/PrivateCalculator.java diff --git a/src/main/java/com/mc/main/Application.java b/src/main/java/com/mc/main/Application.java index 250ee42..d1a8dfb 100644 --- a/src/main/java/com/mc/main/Application.java +++ b/src/main/java/com/mc/main/Application.java @@ -2,6 +2,7 @@ import com.mc.main.advanced.DesignPatternPractice; import com.mc.main.advanced.ExceptionPractice; +import com.mc.main.advanced.ReflectionPractice; import com.mc.main.advanced.io.IOPractice; import com.mc.main.advanced.ui.ScannerPractice; import com.mc.main.oop.ClassApp; @@ -77,6 +78,7 @@ public void run() { DesignPatternPractice.start(); ScannerPractice.start(); IOPractice.start(); + ReflectionPractice.start(); break; case JAVA8: diff --git a/src/main/java/com/mc/main/advanced/ReflectionPractice.java b/src/main/java/com/mc/main/advanced/ReflectionPractice.java new file mode 100644 index 0000000..3b2bcf7 --- /dev/null +++ b/src/main/java/com/mc/main/advanced/ReflectionPractice.java @@ -0,0 +1,168 @@ +package com.mc.main.advanced; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import com.mc.main.advanced.reflect.PrivateCalculator; +import com.mc.main.util.Practice; + +public class ReflectionPractice extends Practice { + + /** We don't want to create an instance of this class */ + private ReflectionPractice() { + super(); + } + + public static void start() { + printHeader("Reflection"); + + // Reflection allows us to capture the definition of our class and execute code + // through internal inspection. + // + // Whilst it might be tempting to use reflection to execute code that should not be accessed + // this would violate good coding practices and would be difficult to maintain. + // + // To some extents, we should never need to use reflection to use code we've written + // as that code adheres to strict structural design that ideally shouldn't be + // circumvented. + + // As an example, we might use reflection to call behaviour specific to a class + // that has private implementation for testing purposes! + // + // In cases where we cannot test code, we can use reflection to introspectively + // expose code to our runtime that would otherwise be private. + // + // We'd avoid this practice for the implementation of code systems to adhere to + // best coding practice, respect of encapsulation as well as security; but in testing + // the idea is to ensure that code works as expected - this is where reflection shines! + + + // Let's observe how to access private class members such as Attributes and Methods! + // Review the PrivateCalculator class before proceeding! + try { + printStaticAttribute(); + printStaticMethod(); + privateClassMethod(); + } catch (NoSuchFieldException | SecurityException | + IllegalArgumentException | IllegalAccessException | + NoSuchMethodException | InstantiationException | + InvocationTargetException e) { + e.printStackTrace(); + } + // Additionally issues to consider: + // Calling classes and their member fields/methods can be process intensive + // and implementation is separate to structural design; meaning reflection might + // not be aware of the changes to the invoked or changed code. + // + // Any errors that caused might only be caused because reflection attempts to access it! + // this means that refactored code may work fine, but reflection did not refactor inline with + // changes. This can produce errors during runtime, rather than compile time - as compiling + // cannot infer functionality that reflection invokes and whether that should be correct! + } + + private static void printStaticAttribute() throws NoSuchFieldException, SecurityException, + IllegalArgumentException, IllegalAccessException { + // In order to access a "Field" or "Method", we need to use the "java.lang.reflect" library + // of classes to utilise the respective java reflection API to gain access to classes + // that allow us to represent and store this class member; + Field myAttribute; + int result; + + // To extract a Field or methods, we can request it through the + // class attribute of any class object; using the respective "getX" method + // to retrieve the field we want. + myAttribute = PrivateCalculator.class.getDeclaredField("myValue"); + myAttribute.setAccessible(true); + // because the field is private static final, particularly "private", we can use + // .setAccessible(true) to overwrite this behaviour. + // + // WARNING: if you set accessibility, ensure that you only change it for the time you need to + // leaving this state post execution could introduce security flaws or vulnerabilities + // not intended for your reflection instance. + // typically this wouldn't be an issue, our instances are moderated separate from reflection + // and reflection works as an effective copy; however you are responsible for the security of + // this functionality. + + // We can now retrieve the field by passing itself into the respective type "getter" + // this will return the expected value assuming that the value type is respected. + result = myAttribute.getInt(myAttribute); + + System.out.println("PrivateCalculator.myValue = " + result); + + // We have no more use for the reflected instances, preserve intended accessibility. + myAttribute.setAccessible(false); + } + + private static void printStaticMethod() throws NoSuchMethodException, SecurityException, + IllegalAccessException, IllegalArgumentException, InvocationTargetException { + Method add; + String result; + + // We use a string to provide the method name, because there are no parameters, we are not + // required to mention them. + add = PrivateCalculator.class.getDeclaredMethod("message"); + add.setAccessible(true); + + // For methods we use "invoke", much like the term we've been accustom to using throughout + // this refresher. We need to provide a class reference as part of the invocation process. + // Java can then create a "reflected copy" of the object to use. + result = (String) add.invoke(PrivateCalculator.class); + + System.out.println("PrivateCalculator.message() = \"" + result +"\""); + + // We have no more use for the reflected instances, preserve intended accessibility. + add.setAccessible(false); + } + + private static void privateClassMethod() throws NoSuchMethodException, SecurityException, + InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException { + + // So far we've only been accessing the static context of a class + // however difficulties arise when you attempt to use reflection on a class + // that requires an instance. + + // In order to achieve this we will need to create an instance first BEFORE reflection can take + // place. + + // let's take the situation that we have a private class, it was never intended + // to be created as an instance, we could technically by pass this using reflection + // to create a "reflected instance" of this class; it has very little value + // as the purpose of a class with a private constructor is arguably to be unavailable to + // instancing. + + // Here we'll save our class constructor, instance and method, as well as the final result + // of the non-static method call (some value of type int). + Constructor myClass; + PrivateCalculator myInstance; + Method add; + int result; + + // first we need to store our defined constructor and method + myClass = PrivateCalculator.class.getDeclaredConstructor(); + // Notice that methods with parameters should provide the type of parameters respectively + // this means we can also reflect overloads of a method too! + add = PrivateCalculator.class.getDeclaredMethod("add", int.class, int.class); + + // Set both to be accessible + myClass.setAccessible(true); + add.setAccessible(true); + + // Here we use our reflection to create an instance, much like the "method.invoke()" approach + // storing the instance of a class that should typically not + // provide an instance! + myInstance = (PrivateCalculator) myClass.newInstance(); + // Because our method had parameters, (int, int) - we use the parameters post-class reference + // to provide these values as part of the reflection call. + result = (int) add.invoke(myInstance, 1, 2); + + System.out.println("PrivateCalculator.add(1,2) = " + result); + + // We have no more use for the reflected instances, preserve intended accessibility. + myClass.setAccessible(false); + add.setAccessible(false); + } + +} diff --git a/src/main/java/com/mc/main/advanced/reflect/PrivateCalculator.java b/src/main/java/com/mc/main/advanced/reflect/PrivateCalculator.java new file mode 100644 index 0000000..38b09c3 --- /dev/null +++ b/src/main/java/com/mc/main/advanced/reflect/PrivateCalculator.java @@ -0,0 +1,19 @@ +package com.mc.main.advanced.reflect; + +public class PrivateCalculator { + + private static final int myValue = 6; + + private PrivateCalculator() { + super(); + } + + private static String message() { + return "message!"; + } + + private int add(int x, int y) { + return x + y; + } + +} From 705cdc80cde22c02368fb43a2085a0f9b7ba1220 Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Mon, 21 Mar 2022 01:23:09 +0000 Subject: [PATCH 07/23] AD: unit tested provided for ReflectionPractice --- .../advanced/ReflectionPracticeUnitTEST.java | 18 ++++++ .../reflect/PrivateCalculatorUnitTEST.java | 55 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 src/test/java/com/mc/main/advanced/ReflectionPracticeUnitTEST.java create mode 100644 src/test/java/com/mc/main/advanced/reflect/PrivateCalculatorUnitTEST.java diff --git a/src/test/java/com/mc/main/advanced/ReflectionPracticeUnitTEST.java b/src/test/java/com/mc/main/advanced/ReflectionPracticeUnitTEST.java new file mode 100644 index 0000000..f31e5b0 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/ReflectionPracticeUnitTEST.java @@ -0,0 +1,18 @@ +package com.mc.main.advanced; + +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.Test; + +public class ReflectionPracticeUnitTEST { + + @Test + public void exceptionStart() { + try { + ReflectionPractice.start(); + }catch (Exception e) { + fail(); + } + } + +} diff --git a/src/test/java/com/mc/main/advanced/reflect/PrivateCalculatorUnitTEST.java b/src/test/java/com/mc/main/advanced/reflect/PrivateCalculatorUnitTEST.java new file mode 100644 index 0000000..aee2ff0 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/reflect/PrivateCalculatorUnitTEST.java @@ -0,0 +1,55 @@ +package com.mc.main.advanced.reflect; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class PrivateCalculatorUnitTEST { + + private static Class testClass; + private static Constructor testConstructor; + private static Method testMessage, testAdd; + + @BeforeAll + public static void setup() throws NoSuchMethodException, SecurityException { + testClass = PrivateCalculator.class; + + testConstructor = testClass.getDeclaredConstructor(); + testMessage = testClass.getDeclaredMethod("message"); + testAdd = testClass.getDeclaredMethod("add", int.class, int.class); + + testConstructor.setAccessible(true); + testMessage.setAccessible(true); + testAdd.setAccessible(true); + } + + @AfterAll + public static void cleanUp() throws NoSuchMethodException, SecurityException { + testConstructor.setAccessible(true); + testMessage.setAccessible(false); + testAdd.setAccessible(false); + } + + @Test + public void message() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { + String expect = "message!"; + String actual = (String) testMessage.invoke(PrivateCalculator.class); + + assertEquals(expect, actual); + } + + @Test + public void add() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException, NoSuchMethodException, SecurityException { + int expect = 25; + int actual = (int) testAdd.invoke((PrivateCalculator) testConstructor.newInstance(), 15, 10); + + assertEquals(expect, actual); + } + +} From 3d63f767e7c50e46512b0d3187b93c5a4498b2c5 Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Mon, 21 Mar 2022 01:23:39 +0000 Subject: [PATCH 08/23] AD: updated readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e829f83..801f40d 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ The topics that'll be covered are: - [ ] Annotations - [ ] Java Pattern/Regular Expression - [ ] Multithreading - - [ ] Reflection + - [X] Reflection - [ ] S.O.L.I.D. --- From 4c7dec2594bfdd137c955fbd7de88b25facda1c4 Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Mon, 21 Mar 2022 01:46:27 +0000 Subject: [PATCH 09/23] AD: updated ioPractice and tests to included new lines --- src/main/java/com/mc/main/Application.java | 4 ++- .../mc/main/advanced/{io => }/IOPractice.java | 3 ++- .../com/mc/main/advanced/PatternPractice.java | 26 +++++++++++++++++++ .../com/mc/main/advanced/io/FileHandler.java | 4 +-- .../advanced/{io => }/IOPracticeUnitTEST.java | 4 ++- .../main/advanced/io/FileHandlerUnitTEST.java | 2 +- 6 files changed, 37 insertions(+), 6 deletions(-) rename src/main/java/com/mc/main/advanced/{io => }/IOPractice.java (95%) create mode 100644 src/main/java/com/mc/main/advanced/PatternPractice.java rename src/test/java/com/mc/main/advanced/{io => }/IOPracticeUnitTEST.java (77%) diff --git a/src/main/java/com/mc/main/Application.java b/src/main/java/com/mc/main/Application.java index d1a8dfb..8b6dc75 100644 --- a/src/main/java/com/mc/main/Application.java +++ b/src/main/java/com/mc/main/Application.java @@ -2,8 +2,9 @@ import com.mc.main.advanced.DesignPatternPractice; import com.mc.main.advanced.ExceptionPractice; +import com.mc.main.advanced.IOPractice; +import com.mc.main.advanced.PatternPractice; import com.mc.main.advanced.ReflectionPractice; -import com.mc.main.advanced.io.IOPractice; import com.mc.main.advanced.ui.ScannerPractice; import com.mc.main.oop.ClassApp; import com.mc.main.oop.WrapperClass; @@ -79,6 +80,7 @@ public void run() { ScannerPractice.start(); IOPractice.start(); ReflectionPractice.start(); + PatternPractice.start(); break; case JAVA8: diff --git a/src/main/java/com/mc/main/advanced/io/IOPractice.java b/src/main/java/com/mc/main/advanced/IOPractice.java similarity index 95% rename from src/main/java/com/mc/main/advanced/io/IOPractice.java rename to src/main/java/com/mc/main/advanced/IOPractice.java index 2915114..b42fe22 100644 --- a/src/main/java/com/mc/main/advanced/io/IOPractice.java +++ b/src/main/java/com/mc/main/advanced/IOPractice.java @@ -1,10 +1,11 @@ -package com.mc.main.advanced.io; +package com.mc.main.advanced; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.util.Scanner; +import com.mc.main.advanced.io.FileHandler; import com.mc.main.util.Practice; public class IOPractice extends Practice { diff --git a/src/main/java/com/mc/main/advanced/PatternPractice.java b/src/main/java/com/mc/main/advanced/PatternPractice.java new file mode 100644 index 0000000..a7fafde --- /dev/null +++ b/src/main/java/com/mc/main/advanced/PatternPractice.java @@ -0,0 +1,26 @@ +package com.mc.main.advanced; + +import java.io.File; +import java.io.FileReader; + +import com.mc.main.advanced.io.FileHandler; +import com.mc.main.util.Practice; + +public class PatternPractice extends Practice { + /** We don't want to create an instance of this class */ + private PatternPractice() { + super(); + } + + public static void start() { + printHeader("Regular Expressions/Pattern Matching"); + + File myReadMeFile; + String fileContents; + + myReadMeFile = new File( new File("").getAbsolutePath() + "\\README.md"); + fileContents = FileHandler.readFile(myReadMeFile, FileReader.class); + + System.out.println(fileContents); + } +} diff --git a/src/main/java/com/mc/main/advanced/io/FileHandler.java b/src/main/java/com/mc/main/advanced/io/FileHandler.java index 3769079..5a3f77b 100644 --- a/src/main/java/com/mc/main/advanced/io/FileHandler.java +++ b/src/main/java/com/mc/main/advanced/io/FileHandler.java @@ -66,7 +66,7 @@ private static String readScanner(String filePath) { try (Scanner readFile = new Scanner(new File(filePath))) { while(readFile.hasNextLine()) { - fileContents.append(readFile.nextLine()); + fileContents.append(readFile.nextLine() + "\r\n"); } return fileContents.toString(); @@ -84,7 +84,7 @@ private static String readBuffer(String filePath) { try ( BufferedReader br = new BufferedReader(new FileReader(new File(filePath))) ) { while ((line = br.readLine()) != null) { - fileContents.append(line); + fileContents.append(line + "\r\n"); } return fileContents.toString(); diff --git a/src/test/java/com/mc/main/advanced/io/IOPracticeUnitTEST.java b/src/test/java/com/mc/main/advanced/IOPracticeUnitTEST.java similarity index 77% rename from src/test/java/com/mc/main/advanced/io/IOPracticeUnitTEST.java rename to src/test/java/com/mc/main/advanced/IOPracticeUnitTEST.java index e0d6a16..a902033 100644 --- a/src/test/java/com/mc/main/advanced/io/IOPracticeUnitTEST.java +++ b/src/test/java/com/mc/main/advanced/IOPracticeUnitTEST.java @@ -1,9 +1,11 @@ -package com.mc.main.advanced.io; +package com.mc.main.advanced; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; +import com.mc.main.advanced.IOPractice; + public class IOPracticeUnitTEST { @Test diff --git a/src/test/java/com/mc/main/advanced/io/FileHandlerUnitTEST.java b/src/test/java/com/mc/main/advanced/io/FileHandlerUnitTEST.java index 7e292aa..cc02f32 100644 --- a/src/test/java/com/mc/main/advanced/io/FileHandlerUnitTEST.java +++ b/src/test/java/com/mc/main/advanced/io/FileHandlerUnitTEST.java @@ -20,7 +20,7 @@ public class FileHandlerUnitTEST { private static String testFolder; private static File file; - private static final String message = "Hello World!"; + private static final String message = "Hello World!\r\n"; private static final String filePath = "\\src\\test\\resources\\"; private static final String fileName = "testFile.txt"; From c258fb99a4605aafb319b76da632c308dfb3e3f0 Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Mon, 21 Mar 2022 02:35:21 +0000 Subject: [PATCH 10/23] AD: added Pattern Practice & corrected unit test method names --- .../com/mc/main/advanced/PatternPractice.java | 30 ++++++++++++++++++- .../DesignPatternPracticeUnitTEST.java | 2 +- .../mc/main/advanced/IOPracticeUnitTEST.java | 2 +- .../advanced/ReflectionPracticeUnitTEST.java | 2 +- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/mc/main/advanced/PatternPractice.java b/src/main/java/com/mc/main/advanced/PatternPractice.java index a7fafde..0c9a95b 100644 --- a/src/main/java/com/mc/main/advanced/PatternPractice.java +++ b/src/main/java/com/mc/main/advanced/PatternPractice.java @@ -2,6 +2,10 @@ import java.io.File; import java.io.FileReader; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; import com.mc.main.advanced.io.FileHandler; import com.mc.main.util.Practice; @@ -17,10 +21,34 @@ public static void start() { File myReadMeFile; String fileContents; + Stream matches; + long count; + // Let's load in the README File and attempt to match every case where "Java" appears myReadMeFile = new File( new File("").getAbsolutePath() + "\\README.md"); fileContents = FileHandler.readFile(myReadMeFile, FileReader.class); - System.out.println(fileContents); + // When we use regular expression, there's a unique way of using characters, wild cards and symbols + // to express patterns within files based on certain character combinations. + // + // We won't cover Regex here, but for the time being we'll use the simple combo of + // match "up to and including Java". + matches = patternMatcher(fileContents, ".*Java"); + + matches.forEach(x -> System.out.println(x.group()) ); + } + + private static Stream patternMatcher(String src, String pattern) { + // First we use a Pattern, so we use the Pattern.compile - using a string that represents our regular + // expression. + Pattern filter = Pattern.compile(pattern); + // We can then use the pattern and apply it to a source String. + Matcher check = filter.matcher(src); + + // There are many things we can do once we've applied the matcher, in this case + // we'll return it as a Stream - but we could do many things with the matched results + // sorted in "check" + return check.results(); } + } diff --git a/src/test/java/com/mc/main/advanced/DesignPatternPracticeUnitTEST.java b/src/test/java/com/mc/main/advanced/DesignPatternPracticeUnitTEST.java index cb3773c..5fb777b 100644 --- a/src/test/java/com/mc/main/advanced/DesignPatternPracticeUnitTEST.java +++ b/src/test/java/com/mc/main/advanced/DesignPatternPracticeUnitTEST.java @@ -7,7 +7,7 @@ public class DesignPatternPracticeUnitTEST { @Test - public void exceptionStart() { + public void designPatternStart() { try { DesignPatternPractice.start(); }catch (Exception e) { diff --git a/src/test/java/com/mc/main/advanced/IOPracticeUnitTEST.java b/src/test/java/com/mc/main/advanced/IOPracticeUnitTEST.java index a902033..e6d1677 100644 --- a/src/test/java/com/mc/main/advanced/IOPracticeUnitTEST.java +++ b/src/test/java/com/mc/main/advanced/IOPracticeUnitTEST.java @@ -9,7 +9,7 @@ public class IOPracticeUnitTEST { @Test - public void exceptionStart() { + public void ioPracticeStart() { try { IOPractice.start(); }catch (Exception e) { diff --git a/src/test/java/com/mc/main/advanced/ReflectionPracticeUnitTEST.java b/src/test/java/com/mc/main/advanced/ReflectionPracticeUnitTEST.java index f31e5b0..51554e4 100644 --- a/src/test/java/com/mc/main/advanced/ReflectionPracticeUnitTEST.java +++ b/src/test/java/com/mc/main/advanced/ReflectionPracticeUnitTEST.java @@ -7,7 +7,7 @@ public class ReflectionPracticeUnitTEST { @Test - public void exceptionStart() { + public void reflectionStart() { try { ReflectionPractice.start(); }catch (Exception e) { From 3e8c079c032e6323ff56814b07b7a6a9f726c309 Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Mon, 21 Mar 2022 02:38:40 +0000 Subject: [PATCH 11/23] AD: added unit tests for PatternPractice --- README.md | 2 +- .../mc/main/procedural/StringPractice.java | 2 +- .../advanced/PatternPracticeUnitTEST.java | 27 +++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 src/test/java/com/mc/main/advanced/PatternPracticeUnitTEST.java diff --git a/README.md b/README.md index 801f40d..d62febc 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ The topics that'll be covered are: - [X] Iterator Pattern - [X] File In/Out - [ ] Annotations - - [ ] Java Pattern/Regular Expression + - [X] Java Pattern/Regular Expression - [ ] Multithreading - [X] Reflection - [ ] S.O.L.I.D. diff --git a/src/main/java/com/mc/main/procedural/StringPractice.java b/src/main/java/com/mc/main/procedural/StringPractice.java index 2b6a65b..1783eba 100644 --- a/src/main/java/com/mc/main/procedural/StringPractice.java +++ b/src/main/java/com/mc/main/procedural/StringPractice.java @@ -16,7 +16,7 @@ public static void start() { // there are some data types that are considered more complex. //stringComparision(); stringMethods(); - + stringComparision(); // Next: Explore Application.java //TODO: add escape & control character examples! diff --git a/src/test/java/com/mc/main/advanced/PatternPracticeUnitTEST.java b/src/test/java/com/mc/main/advanced/PatternPracticeUnitTEST.java new file mode 100644 index 0000000..1b68326 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/PatternPracticeUnitTEST.java @@ -0,0 +1,27 @@ +package com.mc.main.advanced; + +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.File; +import java.io.FileReader; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; + +import com.mc.main.advanced.io.FileHandler; +import com.mc.main.util.Practice; + +public class PatternPracticeUnitTEST { + + @Test + public void patternStart() { + try { + PatternPractice.start(); + }catch (Exception e) { + fail(); + } + } +} From 8e13647ff90c612c497c7994fd5da32f4d9c9728 Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Thu, 24 Mar 2022 08:20:21 +0000 Subject: [PATCH 12/23] AD: adjusted formatting, provided ThreadPractice --- src/main/java/com/mc/main/Application.java | 12 +- src/main/java/com/mc/main/Runner.java | 11 +- .../main/advanced/DesignPatternPractice.java | 25 +- .../com/mc/main/advanced/ThreadPractice.java | 250 ++++++++++++++++++ .../behavioural/observer/Subscriber.java | 10 +- .../mc/main/advanced/threads/ArrayPrint.java | 26 ++ .../main/advanced/threads/ArrayPrintWait.java | 32 +++ .../advanced/threads/InterruptThread.java | 40 +++ .../advanced/threads/MessageInterrupt.java | 16 ++ .../main/advanced/threads/PrintMessage.java | 38 +++ .../advanced/threads/ThreadLifeCycle.java | 22 ++ .../oop/principles/AbstractionPractice.java | 6 +- .../oop/principles/InheritancePractice.java | 1 - .../main/oop/structs/EnumerationPractice.java | 3 +- .../com/mc/main/util/ApplicationTime.java | 12 + src/main/java/com/mc/main/util/Practice.java | 5 +- .../java/com/mc/main/util/StringConst.java | 18 ++ .../advanced/ui/scanner/ConsoleUnitTEST.java | 2 +- 18 files changed, 494 insertions(+), 35 deletions(-) create mode 100644 src/main/java/com/mc/main/advanced/ThreadPractice.java create mode 100644 src/main/java/com/mc/main/advanced/threads/ArrayPrint.java create mode 100644 src/main/java/com/mc/main/advanced/threads/ArrayPrintWait.java create mode 100644 src/main/java/com/mc/main/advanced/threads/InterruptThread.java create mode 100644 src/main/java/com/mc/main/advanced/threads/MessageInterrupt.java create mode 100644 src/main/java/com/mc/main/advanced/threads/PrintMessage.java create mode 100644 src/main/java/com/mc/main/advanced/threads/ThreadLifeCycle.java create mode 100644 src/main/java/com/mc/main/util/ApplicationTime.java create mode 100644 src/main/java/com/mc/main/util/StringConst.java diff --git a/src/main/java/com/mc/main/Application.java b/src/main/java/com/mc/main/Application.java index 8b6dc75..87fc14d 100644 --- a/src/main/java/com/mc/main/Application.java +++ b/src/main/java/com/mc/main/Application.java @@ -5,6 +5,7 @@ import com.mc.main.advanced.IOPractice; import com.mc.main.advanced.PatternPractice; import com.mc.main.advanced.ReflectionPractice; +import com.mc.main.advanced.ThreadPractice; import com.mc.main.advanced.ui.ScannerPractice; import com.mc.main.oop.ClassApp; import com.mc.main.oop.WrapperClass; @@ -22,6 +23,7 @@ import com.mc.main.procedural.OperatorsPractice; import com.mc.main.procedural.StringPractice; import com.mc.main.procedural.VariablesPractice; +import com.mc.main.util.StringConst; /** * Provides a series of applications as values @@ -45,11 +47,8 @@ public enum Application { * which application you would like to run * grouping instruction methods to execute */ - public void run() { - - String div = "=".repeat(40); - - System.out.println( this.name() + "\n" + div ); + public void run() { + System.out.println( this.name() + "\n" + StringConst.DIV ); switch(this) { case PROCEDURAL: @@ -81,6 +80,7 @@ public void run() { IOPractice.start(); ReflectionPractice.start(); PatternPractice.start(); + ThreadPractice.start(); break; case JAVA8: @@ -91,7 +91,7 @@ public void run() { break; } - System.out.println(div + String.format("\nEND of %s\n", this.name()) + div + "\n"); + System.out.format("%s\nEND of %s\n%s", StringConst.DIV, this.name(), StringConst.DIV + "\n"); // View Runner.java, seeing how this code works // Enums will be covered in OOP. diff --git a/src/main/java/com/mc/main/Runner.java b/src/main/java/com/mc/main/Runner.java index cde3d2d..eca62a4 100644 --- a/src/main/java/com/mc/main/Runner.java +++ b/src/main/java/com/mc/main/Runner.java @@ -1,10 +1,12 @@ package com.mc.main; import com.mc.main.advanced.ui.scanner.Console; +import com.mc.main.util.ApplicationTime; +import com.mc.main.util.StringConst; // Find FlowControlPractice.java to start! public class Runner { - + /** *

* This project is a refresher of Java coding for `Alan Davies`, comments here @@ -24,7 +26,7 @@ public class Runner { public static void main(String[] args) { // A simple print function, outputting to console System.out.println("HELLO WORLD"); - + // Continued: try { start(); @@ -37,11 +39,14 @@ public static void main(String[] args) { // Let's return to Console.input()! System.out.println("Application ENDS"); + ApplicationTime.printElapsedTime(); } } + + private static final ApplicationTime time = new ApplicationTime(); protected static void start() { - System.out.println("=".repeat(40) + "\n"); + System.out.println(StringConst.DIV + "\n"); Application.PROCEDURAL.run(); Application.OOP.run(); diff --git a/src/main/java/com/mc/main/advanced/DesignPatternPractice.java b/src/main/java/com/mc/main/advanced/DesignPatternPractice.java index 69d1ee0..e1dd8b3 100644 --- a/src/main/java/com/mc/main/advanced/DesignPatternPractice.java +++ b/src/main/java/com/mc/main/advanced/DesignPatternPractice.java @@ -15,8 +15,8 @@ import com.mc.main.advanced.designpatterns.structural.Pizza; import com.mc.main.advanced.designpatterns.structural.PizzaBase; import com.mc.main.advanced.designpatterns.structural.adapter.EnglishInteractions; -import com.mc.main.advanced.designpatterns.structural.adapter.GermanInteractions; import com.mc.main.advanced.designpatterns.structural.adapter.EnglishToGermanAdapter; +import com.mc.main.advanced.designpatterns.structural.adapter.GermanInteractions; import com.mc.main.advanced.designpatterns.structural.adapter.LanguageInteractions; import com.mc.main.advanced.designpatterns.structural.decorator.Cheese; import com.mc.main.advanced.designpatterns.structural.decorator.Pepperoni; @@ -26,9 +26,12 @@ import com.mc.main.oop.principles.res.polymorph.Cat; import com.mc.main.oop.principles.res.polymorph.Dog; import com.mc.main.util.Practice; +import com.mc.main.util.StringConst; public class DesignPatternPractice extends Practice { + private static final String div10 = "=".repeat(10); + private DesignPatternPractice() { super(); } public static void start() { @@ -40,26 +43,26 @@ public static void start() { } public static void creational() { - System.out.println("CREATIONAL:\n"+"=".repeat(40)); - System.out.println("\nSingleton:\n"+"=".repeat(10)); + System.out.println("CREATIONAL:\n" + StringConst.DIV); + System.out.println("\nSingleton:\n" + div10); singletonPattern(); - System.out.println("\nBuilder:\n"+"=".repeat(10)); + System.out.println("\nBuilder:\n" + div10); builderPattern(); } public static void structural() { - System.out.println("STRUCTURAL:\n"+"=".repeat(40)); - System.out.println("\nDecorator:\n"+"=".repeat(10)); + System.out.println("STRUCTURAL:\n" + StringConst.DIV); + System.out.println("\nDecorator:\n" + div10); decoratorPattern(); - System.out.println("\nAdapter:\n"+"=".repeat(10)); + System.out.println("\nAdapter:\n" + div10); adapterPattern(); } public static void behavioural() { - System.out.println("BEHAVIOURAL:\n"+"=".repeat(40)); - System.out.println("\nITERATOR:\n"+"=".repeat(10)); + System.out.println("BEHAVIOURAL:\n"+ StringConst.DIV); + System.out.println("\nITERATOR:\n"+div10); iteratorPattern(); - System.out.println("\nOBSERVER:\n"+"=".repeat(10)); + System.out.println("\nOBSERVER:\n"+div10); observerPattern(); } @@ -284,7 +287,7 @@ public static void observerPattern() { // to block a sub group from receiving this message! catGroupPage.post("Check out our page relating to dogs!" + "\n"); - System.out.println("PAGE BAN LIST:\n" + "=".repeat(40)); + System.out.println("PAGE BAN LIST:\n" + StringConst.DIV); for(MediaSubject sub : AdminPostChecker.retrieveBanList()) { if(sub instanceof GroupPage) { System.out.println("\t- " + ((GroupPage)sub).getName()); diff --git a/src/main/java/com/mc/main/advanced/ThreadPractice.java b/src/main/java/com/mc/main/advanced/ThreadPractice.java new file mode 100644 index 0000000..4e0632f --- /dev/null +++ b/src/main/java/com/mc/main/advanced/ThreadPractice.java @@ -0,0 +1,250 @@ +package com.mc.main.advanced; + +import java.util.List; + +import com.mc.main.advanced.threads.ArrayPrint; +import com.mc.main.advanced.threads.ArrayPrintWait; +import com.mc.main.advanced.threads.InterruptThread; +import com.mc.main.advanced.threads.MessageInterrupt; +import com.mc.main.advanced.threads.PrintMessage; +import com.mc.main.advanced.threads.ThreadLifeCycle; +import com.mc.main.util.Practice; +import com.mc.main.util.StringConst; + +public class ThreadPractice extends Practice { + // When dealing with programming in java, we've only really looked + // at programming as a procedural process - there have been some exceptions to + // the rule + // but the idea is that our application runs through each line of code + // sequentially. + // + // But imagine if all computers HAD to complete one task before doing another, + // we'd + // have no way of being able to perform multiple actions at the same time! + // + // Perhaps you've previously watched a video on your computer whilst + // reading an interesting article or logging into your social media account! + // + // Computers have the means to break down instructions and process instructions + // independently + // of the lines of code that are written, this is because they have multiple + // processes - either + // in terms of hardware or "logical processors" which help perform calculations + // independently of eachother. + // + // If you are on a windows machine, you can even see how active each "cpu" group + // is by reviewing + // your "resource monitor" on the cpu tab. + + private ThreadPractice() { + super(); + } + + public static void start() { + // When we discuss threading, there are two terms we want to ensure we are + // familiar with before + // we get into more specific details. + // + // Processes vs Threads + // Processes are much like self-contained programs or applications, they set an + // environment in which + // code operations take place. + // + // Threads are like isolated "light-weight" programs that might exist within the + // space of a process + // or program, often a set of instructions that work towards breaking a larger + // task + // into several smaller tasks. + // + // Why use threading? + // Threading has a massive benefit as part of your application, this comes back + // to the notion + // of using one processor on your machine VS using many processors on your + // machine. + // + // Threading allows us to take a part of our java application and distribute the + // task to available + // resources the machine has to help process the task at hand. Coding is + // resolved by a set of mathematical + // calculations that are processed by a cpu core, each core can perform many, + // many operations per second + // but imagine trying to push all your tasks through one cpu calculator when you + // have 3-7 free cores + // not doing anything! + // + // When we break up instruction intense operations into thread, we can directly + // benefit from multi-core processing and enhance the efficiency of our code by + // making use of unutilised processing units. + printHeader("Threads"); + + // all threads have the capability of throwing an InterruptedException + // as such we'll capture any throws here + try { + // Let's begin looking at a very basic example! + startingThreads(); + + // Let's understand the life cycle of a thread and how we can get the most out + // of a thread! + lifeCycle(); + + // Now we will move on to how we can control thread behaviour and what to watch + // out for during + // invocation and execution of code with threading! + threadControl(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private static void startingThreads() { + // We create a thread much like we create any class instance. + PrintMessage thread = new PrintMessage("Hello from ThreadPractice!"); + + // The inheritance allows us to inform java that + // we want this method to run its behaviour when we invoke the start() method. + thread.start(); + + // If you try this then you must understand that threads execute separately - + // evaluating + // when time and resources are allocated to the thread - rather than traditional + // procedural code. + // + // Our public static void main() method that acts as our entry into the + // application + // is in fact a thread. When we create a method that is called within main - it + // runs parallel to the + // application at run time - meaning the order of output or execution is now + // seperate from the + // main application. + // + // We can control how thread operate and their respect to each-other - this is + // in-fact hugely important + // in getting the most out of our application and available hardware! + } + + private static void lifeCycle() throws InterruptedException { + + // Threads have many different states that are outlined as part of a STATE Enum + // State: + // - NEW + // - RUNNABLE + // - BLOCKED + // - WAITING + // - TIMED_WAITING + // - TERMINATED + + // Typically a Thread will be declared, started and then will complete + // we typically start with: + // - NEW state; ready to be started, ready to execute. + // - RUNNABLE state; the thread is actively running and executing commands. + // - TERMINATED state; the thread has concluded its instruction set and has + // finished. + // + // Let's observe this in action below, with the simple ThreadLifeCycle thread: + + // Starting a thread - NEW + ThreadLifeCycle thread = new ThreadLifeCycle(); + System.out.println("Thread State: " + thread.getState()); + + // Running a thread - RUNNABLE, triggered within the + thread.start(); + threadMessage(thread, StringConst.DIV.toString()); + + // Let's have main wait on this thread, by using join() - we'll look at this + // afterwards. + thread.join(); + + // Exit/termination of a thread - TERMINATED + System.out.println("Thread State: " + thread.getState()); + + // TODO: Explore BLOCKED, WAITING, TIME_WAITING + + // BLOCKED - is waiting on a monitor lock resolution, we'll investigate this a + // little more + // in the near future. + + // WAITING - this thread is actively waiting for another thread to complete a + // task. + // This typically manifests when the .wait or .join method has been utilised + // without a timeout + // period. + + // TIME_WAITING - this is waiting on a join, sleep, wait or some kind of + // thread-related + // that typically has some timeout element before it can proceed. + + } + + private static void threadMessage(Thread x, String msg) { + String name = x.getName(); + System.out.format("%s : %s\n", name, msg); + } + + private static void threadControl() throws InterruptedException { + List myList = List.of("Cat", "Dog", "Penguin", "Seagul"); + List myIntList = List.of(15, 21, 32, 14); + + // Threads declared + ArrayPrint myThread = new ArrayPrint<>(myList); + ArrayPrint myIntThread = new ArrayPrint<>(myIntList); + ArrayPrintWait myThreadWaits = new ArrayPrintWait<>(myList); + InterruptThread myThreadInterrupt = new InterruptThread(); + // This example shows that as long as a class implements the Runnable interface + // we can use it as a thread by passing an instance into a new Thread constructor! + Thread myLoopIterrupt = new Thread(new MessageInterrupt()); + + // Joins + // ======================================== + myThread.start(); + threadMessage(myThread, StringConst.DIV.toString()); + // Joining a thread + myThread.join(); // Stops Main to complete this thread + + myIntThread.start(); + threadMessage(myIntThread, StringConst.DIV.toString()); + myIntThread.join(); // Stops Main to complete this thread + + // Waits + // ======================================== + myThreadWaits.start(); + threadMessage(myThreadWaits, StringConst.DIV.toString()); + myThreadWaits.join(); + + // Interrupts + // ======================================== + myThreadInterrupt.start(); + threadMessage(myThreadInterrupt, StringConst.DIV.toString()); + myThreadWaits.join(); + + // Interrupt due to failure on wait + long startTime = System.currentTimeMillis(); + long timeLimit = 100; + + myLoopIterrupt.start(); + threadMessage(myLoopIterrupt, StringConst.DIV.toString()); + + // Let's keep track of our thread + threadMessage(myLoopIterrupt, "Starting process...."); + + while (myLoopIterrupt.isAlive()) { + threadMessage(myLoopIterrupt, "Still waiting..."); + + // Now we pause main for the thread to finish + myLoopIterrupt.join(50); + + // If an iteration takes longer than expected and the thread is alive + // request a interruption that'll terminate the thread. + if (((System.currentTimeMillis() - startTime) > timeLimit) && myLoopIterrupt.isAlive()) { + threadMessage(myLoopIterrupt, "exceeded time limit, requesting interrupt termination!"); + myLoopIterrupt.interrupt(); + + // We'll wait until our thread terminates + myLoopIterrupt.join(); + } + } + threadMessage(myLoopIterrupt, "State - " + myLoopIterrupt.getState()); + + } + +} diff --git a/src/main/java/com/mc/main/advanced/designpatterns/behavioural/observer/Subscriber.java b/src/main/java/com/mc/main/advanced/designpatterns/behavioural/observer/Subscriber.java index 297e1eb..2fb8118 100644 --- a/src/main/java/com/mc/main/advanced/designpatterns/behavioural/observer/Subscriber.java +++ b/src/main/java/com/mc/main/advanced/designpatterns/behavioural/observer/Subscriber.java @@ -3,6 +3,8 @@ import java.util.HashMap; import java.util.Map.Entry; +import com.mc.main.util.StringConst; + public class Subscriber implements MediaObserver { private String username; @@ -61,15 +63,13 @@ public boolean editMessage(int key, String message) { return true; } - public void printInbox() { - String div = "=".repeat(40); - - System.out.println(username + "'s Inbox:\n" + div); + public void printInbox() { + System.out.println(username + "'s Inbox:\n" + StringConst.DIV); for(Entry message : inbox.entrySet()) { System.out.println("Message #" + message.getKey() + ":\n\t" + message.getValue()); } - System.out.println(div); + System.out.println(StringConst.DIV); } } diff --git a/src/main/java/com/mc/main/advanced/threads/ArrayPrint.java b/src/main/java/com/mc/main/advanced/threads/ArrayPrint.java new file mode 100644 index 0000000..9e58442 --- /dev/null +++ b/src/main/java/com/mc/main/advanced/threads/ArrayPrint.java @@ -0,0 +1,26 @@ +package com.mc.main.advanced.threads; + +import java.util.Collection; + +public class ArrayPrint extends Thread { + + private Collection array; + + public ArrayPrint(Collection array) { + this.array = array.size() > 0 && (array != null) ? array : null; + } + + public void run() { + super.run(); + + int y = 0; + + if (array != null) { + for(Object item : array) { + System.out.format("%s: - #%d : %s\n", Thread.currentThread().getName(), ++y, item); + } + } + + } + +} diff --git a/src/main/java/com/mc/main/advanced/threads/ArrayPrintWait.java b/src/main/java/com/mc/main/advanced/threads/ArrayPrintWait.java new file mode 100644 index 0000000..a865038 --- /dev/null +++ b/src/main/java/com/mc/main/advanced/threads/ArrayPrintWait.java @@ -0,0 +1,32 @@ +package com.mc.main.advanced.threads; + +import java.util.Collection; + +public class ArrayPrintWait extends Thread { + + private Collection array; + + public ArrayPrintWait(Collection array) { + this.array = array.size() > 0 && (array != null) ? array : null; + } + + public void run() { + super.run(); + + int y = 0; + + if (array != null) { + for(Object item : array) { + System.out.format("%s: - #%d : %s\n", Thread.currentThread().getName(), ++y, item); + try { + Thread.sleep(50); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + } + +} diff --git a/src/main/java/com/mc/main/advanced/threads/InterruptThread.java b/src/main/java/com/mc/main/advanced/threads/InterruptThread.java new file mode 100644 index 0000000..6847df3 --- /dev/null +++ b/src/main/java/com/mc/main/advanced/threads/InterruptThread.java @@ -0,0 +1,40 @@ +package com.mc.main.advanced.threads; + +public class InterruptThread extends Thread { + + public InterruptThread() { + super(); + } + + @Override + public void run() { + super.run(); + + try{ + crunchNumbers(); + }catch (Exception e) { + System.out.format("%s : %s\n", Thread.currentThread().getName(),e); + return; + } + } + + private void crunchNumbers() throws InterruptedException { + + int value = 0; + + for(int x = 0; x < 100; x++) { + + value += algorithm(x); + + if(Thread.interrupted()) { + throw new InterruptedException(); + } + } + } + + private double algorithm(int x) { + if (x > 90) { this.interrupt(); } + return x * 2; + } + +} diff --git a/src/main/java/com/mc/main/advanced/threads/MessageInterrupt.java b/src/main/java/com/mc/main/advanced/threads/MessageInterrupt.java new file mode 100644 index 0000000..edbfc07 --- /dev/null +++ b/src/main/java/com/mc/main/advanced/threads/MessageInterrupt.java @@ -0,0 +1,16 @@ +package com.mc.main.advanced.threads; + +public class MessageInterrupt implements Runnable { + @Override + public void run() { + String[] list = { "cat", "dog", "penguin", "seagul", "car" }; + try { + for (String msg : list) { + System.out.format("%s :\t- %s\n", Thread.currentThread().getName(), msg); + Thread.sleep(100); + } + } catch (InterruptedException e) { + System.out.format("%s : WARNING: did not complete full task!\n", Thread.currentThread().getName()); + } + } +} diff --git a/src/main/java/com/mc/main/advanced/threads/PrintMessage.java b/src/main/java/com/mc/main/advanced/threads/PrintMessage.java new file mode 100644 index 0000000..0ff219f --- /dev/null +++ b/src/main/java/com/mc/main/advanced/threads/PrintMessage.java @@ -0,0 +1,38 @@ +package com.mc.main.advanced.threads; + +// Creating a thread is relatively simple, we can either +// create a new Thread and pass in a class that implements the "Runnable" interface +// or create a class that inherits functionality from the Thread class as part of a +// is-a relationship. +public class PrintMessage extends Thread { + + private String message; + + public PrintMessage(String msg) { + super(); + + this.message = msg; + } + + // When we want to execute a thread, we use the start method + // this will then proceed to use the run method to execute instructions + // in this unique "thread". + // + // One of the important things to recongise here is that a "thread" is a processing + // term that has meaning to the compiler - but to us, it looks like we are simply performing + // inheritance on a random class. + // + // This isn't just a simple java class anymore, but a "new thread" processing information + // Separately from our entry class "Runner.main()". + // + // Infact, it might help to think of PrintMessage.start() similar to Runner.main()! + // this is because they are both threads of the java application, executing lines + // of code within the JVM! + @Override + public void run() { + super.run(); + + System.out.println(message); + } + +} diff --git a/src/main/java/com/mc/main/advanced/threads/ThreadLifeCycle.java b/src/main/java/com/mc/main/advanced/threads/ThreadLifeCycle.java new file mode 100644 index 0000000..d407703 --- /dev/null +++ b/src/main/java/com/mc/main/advanced/threads/ThreadLifeCycle.java @@ -0,0 +1,22 @@ +package com.mc.main.advanced.threads; + +public class ThreadLifeCycle extends Thread { + + public ThreadLifeCycle() { + super(); + } + + public void run() { + super.run(); + + try { + Thread.sleep(1500); + System.out.println("Thread State: " + this.getState().toString()); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + + } + +} diff --git a/src/main/java/com/mc/main/oop/principles/AbstractionPractice.java b/src/main/java/com/mc/main/oop/principles/AbstractionPractice.java index 2666154..7e0340e 100644 --- a/src/main/java/com/mc/main/oop/principles/AbstractionPractice.java +++ b/src/main/java/com/mc/main/oop/principles/AbstractionPractice.java @@ -7,6 +7,7 @@ import com.mc.main.oop.principles.res.abstraction.FlyingBird; import com.mc.main.oop.principles.res.abstraction.Penguin; import com.mc.main.oop.principles.res.abstraction.Seagul; +import com.mc.main.util.StringConst; public class AbstractionPractice { @@ -34,8 +35,7 @@ public static void start() { birdList.add(new Penguin("Fiordland Penguin")); - System.out.println("Birds and their capabilities:"); - System.out.println("=".repeat(40)); + System.out.format("Birds and their capabilities:\n%s\n", StringConst.DIV); FlyingBird flyingBird; // We can now go through a list of birds and hand them respectively based on their @@ -77,7 +77,7 @@ public static void start() { System.out.println(""); } - System.out.println("=".repeat(40)); + System.out.println(StringConst.DIV); } } diff --git a/src/main/java/com/mc/main/oop/principles/InheritancePractice.java b/src/main/java/com/mc/main/oop/principles/InheritancePractice.java index 2086aec..2679702 100644 --- a/src/main/java/com/mc/main/oop/principles/InheritancePractice.java +++ b/src/main/java/com/mc/main/oop/principles/InheritancePractice.java @@ -71,7 +71,6 @@ public static class Library { // ======================================== public Library() { super(); - // TODO Auto-generated constructor stub } public Library(String name, String address, Book exampleBook) { diff --git a/src/main/java/com/mc/main/oop/structs/EnumerationPractice.java b/src/main/java/com/mc/main/oop/structs/EnumerationPractice.java index b963cba..aef261c 100644 --- a/src/main/java/com/mc/main/oop/structs/EnumerationPractice.java +++ b/src/main/java/com/mc/main/oop/structs/EnumerationPractice.java @@ -1,6 +1,7 @@ package com.mc.main.oop.structs; import com.mc.main.util.Practice; +import com.mc.main.util.StringConst; public class EnumerationPractice extends Practice { @@ -21,7 +22,7 @@ public static void start() { // available constants. // // We can do this using the '.values()' method. - System.out.println("List of planets:\n"+ "=".repeat(40)); + System.out.println("List of planets:\n" + StringConst.DIV); for(Planet planet : Planet.values()) { System.out.print(planet); diff --git a/src/main/java/com/mc/main/util/ApplicationTime.java b/src/main/java/com/mc/main/util/ApplicationTime.java new file mode 100644 index 0000000..330c076 --- /dev/null +++ b/src/main/java/com/mc/main/util/ApplicationTime.java @@ -0,0 +1,12 @@ +package com.mc.main.util; + +public class ApplicationTime { + + private static final long startTime = System.currentTimeMillis(); + + public static void printElapsedTime() { + long runTime = System.currentTimeMillis() - startTime; + + System.out.println("Elapsed time: " + runTime + "ms"); + } +} diff --git a/src/main/java/com/mc/main/util/Practice.java b/src/main/java/com/mc/main/util/Practice.java index 785196b..1c1b08c 100644 --- a/src/main/java/com/mc/main/util/Practice.java +++ b/src/main/java/com/mc/main/util/Practice.java @@ -5,10 +5,7 @@ public abstract class Practice { protected Practice() { super(); } protected static void printHeader(String className) { - System.out.println( - String.format( - "%s%s%s%s%s%s", "\n", "=".repeat(40), "\n", className, "\n", "=".repeat(40)) - ); + System.out.format("\n%s\n%s\n%s\n", StringConst.DIV, className, StringConst.DIV); } } diff --git a/src/main/java/com/mc/main/util/StringConst.java b/src/main/java/com/mc/main/util/StringConst.java new file mode 100644 index 0000000..f9ac3b9 --- /dev/null +++ b/src/main/java/com/mc/main/util/StringConst.java @@ -0,0 +1,18 @@ +package com.mc.main.util; + +public enum StringConst { + + DIV("=".repeat(40)); + + String val; + + StringConst(String x){ + val = x; + } + + @Override + public String toString() { + return val; + } + +} diff --git a/src/test/java/com/mc/main/advanced/ui/scanner/ConsoleUnitTEST.java b/src/test/java/com/mc/main/advanced/ui/scanner/ConsoleUnitTEST.java index a614a58..27dc1f2 100644 --- a/src/test/java/com/mc/main/advanced/ui/scanner/ConsoleUnitTEST.java +++ b/src/test/java/com/mc/main/advanced/ui/scanner/ConsoleUnitTEST.java @@ -16,7 +16,7 @@ public class ConsoleUnitTEST { //TODO: add tests to control private static context //TODO: Mocking private static final String userInput = "TEST_WORD"; - private static final String div = "=".repeat(40); + private static final String div = StringConst.DIV; private static int testCount; @BeforeEach From 58967411db9018ccd0d45a7022adf1826a94c139 Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Thu, 24 Mar 2022 09:11:06 +0000 Subject: [PATCH 13/23] AD: added unit tests for ThreadsPractice --- README.md | 2 +- .../com/mc/main/advanced/ThreadPractice.java | 1 - .../main/advanced/threads/ArrayPrintWait.java | 8 ++- .../main/advanced/ThreadPracticeUnitTEST.java | 18 ++++++ .../advanced/threads/ArrayPrintUnitTEST.java | 38 ++++++++++++ .../threads/ArrayPrintWaitUnitTEST.java | 53 ++++++++++++++++ .../threads/InterruptThreadUnitTEST.java | 43 +++++++++++++ .../threads/MessageInterruptUnitTEST.java | 61 +++++++++++++++++++ .../threads/PrintMessageUnitTEST.java | 36 +++++++++++ .../threads/ThreadLifeCycleUnitTEST.java | 51 ++++++++++++++++ .../advanced/ui/scanner/ConsoleUnitTEST.java | 4 +- 11 files changed, 309 insertions(+), 6 deletions(-) create mode 100644 src/test/java/com/mc/main/advanced/ThreadPracticeUnitTEST.java create mode 100644 src/test/java/com/mc/main/advanced/threads/ArrayPrintUnitTEST.java create mode 100644 src/test/java/com/mc/main/advanced/threads/ArrayPrintWaitUnitTEST.java create mode 100644 src/test/java/com/mc/main/advanced/threads/InterruptThreadUnitTEST.java create mode 100644 src/test/java/com/mc/main/advanced/threads/MessageInterruptUnitTEST.java create mode 100644 src/test/java/com/mc/main/advanced/threads/PrintMessageUnitTEST.java create mode 100644 src/test/java/com/mc/main/advanced/threads/ThreadLifeCycleUnitTEST.java diff --git a/README.md b/README.md index d62febc..202f074 100644 --- a/README.md +++ b/README.md @@ -50,9 +50,9 @@ The topics that'll be covered are: - [X] File In/Out - [ ] Annotations - [X] Java Pattern/Regular Expression + - [X] Threads - [ ] Multithreading - [X] Reflection - - [ ] S.O.L.I.D. --- diff --git a/src/main/java/com/mc/main/advanced/ThreadPractice.java b/src/main/java/com/mc/main/advanced/ThreadPractice.java index 4e0632f..90116b5 100644 --- a/src/main/java/com/mc/main/advanced/ThreadPractice.java +++ b/src/main/java/com/mc/main/advanced/ThreadPractice.java @@ -92,7 +92,6 @@ public static void start() { // invocation and execution of code with threading! threadControl(); } catch (InterruptedException e) { - // TODO Auto-generated catch block e.printStackTrace(); } } diff --git a/src/main/java/com/mc/main/advanced/threads/ArrayPrintWait.java b/src/main/java/com/mc/main/advanced/threads/ArrayPrintWait.java index a865038..ff1319b 100644 --- a/src/main/java/com/mc/main/advanced/threads/ArrayPrintWait.java +++ b/src/main/java/com/mc/main/advanced/threads/ArrayPrintWait.java @@ -19,10 +19,12 @@ public void run() { for(Object item : array) { System.out.format("%s: - #%d : %s\n", Thread.currentThread().getName(), ++y, item); try { - Thread.sleep(50); - } catch (InterruptedException e) { - // TODO Auto-generated catch block + if(Thread.interrupted()) { + throw new InterruptedException(); + } + }catch (Exception e) { e.printStackTrace(); + return; } } } diff --git a/src/test/java/com/mc/main/advanced/ThreadPracticeUnitTEST.java b/src/test/java/com/mc/main/advanced/ThreadPracticeUnitTEST.java new file mode 100644 index 0000000..7444502 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/ThreadPracticeUnitTEST.java @@ -0,0 +1,18 @@ +package com.mc.main.advanced; + +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.Test; + +public class ThreadPracticeUnitTEST { + + @Test + public void threadStart() { + try { + ThreadPractice.start(); + }catch (Exception e) { + fail(); + } + } + +} diff --git a/src/test/java/com/mc/main/advanced/threads/ArrayPrintUnitTEST.java b/src/test/java/com/mc/main/advanced/threads/ArrayPrintUnitTEST.java new file mode 100644 index 0000000..035c9b5 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/threads/ArrayPrintUnitTEST.java @@ -0,0 +1,38 @@ +package com.mc.main.advanced.threads; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.List; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ArrayPrintUnitTEST { + + private static ArrayPrint testThread; + + @BeforeAll + public static void setup() { + List testList = List.of("cat", "dog", "seagul"); + + testThread = new ArrayPrint(testList); + } + + @Test + public void constructor() { + assertTrue(testThread instanceof ArrayPrint); + } + + @Test + public void run() { + try { + testThread.run(); + }catch (Exception e) { + fail(); + } + + assertTrue(testThread instanceof ArrayPrint); + } + +} diff --git a/src/test/java/com/mc/main/advanced/threads/ArrayPrintWaitUnitTEST.java b/src/test/java/com/mc/main/advanced/threads/ArrayPrintWaitUnitTEST.java new file mode 100644 index 0000000..7110373 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/threads/ArrayPrintWaitUnitTEST.java @@ -0,0 +1,53 @@ +package com.mc.main.advanced.threads; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.List; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ArrayPrintWaitUnitTEST { + + private static ArrayPrintWait testThread; + + @BeforeAll + public static void setup() { + List testList = List.of("cat", "dog", "seagul"); + + testThread = new ArrayPrintWait(testList); + } + + @Test + public void constructor() { + assertTrue(testThread instanceof ArrayPrintWait); + } + + @Test + public void run() { + try { + testThread.run(); + }catch (Exception e) { + fail(); + } + assertTrue(testThread instanceof ArrayPrintWait); + } + + @Test + public void run_failure() throws InterruptedException { + + boolean expect; + + testThread.start(); + testThread.interrupt(); + expect = testThread.isInterrupted(); + testThread.join(); + + assertTrue(expect); + assertEquals(Thread.State.TERMINATED, testThread.getState()); + assertTrue(testThread instanceof ArrayPrintWait); + } + +} diff --git a/src/test/java/com/mc/main/advanced/threads/InterruptThreadUnitTEST.java b/src/test/java/com/mc/main/advanced/threads/InterruptThreadUnitTEST.java new file mode 100644 index 0000000..ee8ed95 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/threads/InterruptThreadUnitTEST.java @@ -0,0 +1,43 @@ +package com.mc.main.advanced.threads; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +public class InterruptThreadUnitTEST { + + private static InterruptThread testThread; + + @BeforeAll + public static void setup() { + testThread = new InterruptThread(); + } + + @Test + public void constructor() { + assertTrue(testThread instanceof InterruptThread); + } + + @Test + public void run() { + try { + testThread.run(); + }catch (Exception e) { + fail(); + } + + assertTrue(testThread instanceof InterruptThread); + } + + //TODO: Mock crunchNumbers + @Disabled + public void crunchNumbers() {} + + //TODO: Mock algorithm + @Disabled + public void algorithm() {} + +} diff --git a/src/test/java/com/mc/main/advanced/threads/MessageInterruptUnitTEST.java b/src/test/java/com/mc/main/advanced/threads/MessageInterruptUnitTEST.java new file mode 100644 index 0000000..bdc4c43 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/threads/MessageInterruptUnitTEST.java @@ -0,0 +1,61 @@ +package com.mc.main.advanced.threads; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class MessageInterruptUnitTEST { + + private static long startTime; + private static long timeLimit = 50; + private static MessageInterrupt testRunnable; + + @BeforeAll + public static void setup() { + testRunnable = new MessageInterrupt(); + } + + @Test + public void constructor() { + assertTrue(testRunnable instanceof MessageInterrupt); + } + + @Test + public void run() { + try { + testRunnable.run(); + }catch (Exception e) { + fail(); + } + assertTrue(testRunnable instanceof MessageInterrupt); + } + + //TODO: Improve failure capture, code refactor likely + @Test + public void runFailure() { + try { + Thread testThread = new Thread(new MessageInterrupt()); + + startTime = System.currentTimeMillis(); + testThread.start(); + + while (testThread.isAlive()) { + testThread.join(200); + + // If an iteration takes longer than expected and the thread is alive + // request a interruption that'll terminate the thread. + if (((System.currentTimeMillis() - startTime) > timeLimit) && testThread.isAlive()) { + testThread.interrupt(); + testThread.join(); + } + } + }catch (Exception e) { + fail(); + } + + assertTrue(testRunnable instanceof MessageInterrupt); + } + +} diff --git a/src/test/java/com/mc/main/advanced/threads/PrintMessageUnitTEST.java b/src/test/java/com/mc/main/advanced/threads/PrintMessageUnitTEST.java new file mode 100644 index 0000000..46ab8a0 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/threads/PrintMessageUnitTEST.java @@ -0,0 +1,36 @@ +package com.mc.main.advanced.threads; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.List; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class PrintMessageUnitTEST { + + private static PrintMessage testThread; + + @BeforeAll + public static void setup() { + testThread = new PrintMessage("Standard Message"); + } + + @Test + public void constructor() { + assertTrue(testThread instanceof PrintMessage); + } + + @Test + public void run() { + try { + testThread.run(); + }catch (Exception e) { + fail(); + } + + assertTrue(testThread instanceof PrintMessage); + } + +} diff --git a/src/test/java/com/mc/main/advanced/threads/ThreadLifeCycleUnitTEST.java b/src/test/java/com/mc/main/advanced/threads/ThreadLifeCycleUnitTEST.java new file mode 100644 index 0000000..a450611 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/threads/ThreadLifeCycleUnitTEST.java @@ -0,0 +1,51 @@ +package com.mc.main.advanced.threads; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.List; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class ThreadLifeCycleUnitTEST { + + private static ThreadLifeCycle testThread; + + @BeforeAll + public static void setup() { + testThread = new ThreadLifeCycle(); + } + + @Test + public void constructor() { + assertTrue(testThread instanceof ThreadLifeCycle); + } + + @Test + public void run() { + try { + testThread.run(); + }catch (Exception e) { + fail(); + } + assertTrue(testThread instanceof ThreadLifeCycle); + } + + @Test + public void run_failure() throws InterruptedException { + + boolean expect; + + testThread.start(); + testThread.interrupt(); + expect = testThread.isInterrupted(); + testThread.join(); + + assertTrue(expect); + assertEquals(Thread.State.TERMINATED, testThread.getState()); + assertTrue(testThread instanceof ThreadLifeCycle); + } + +} diff --git a/src/test/java/com/mc/main/advanced/ui/scanner/ConsoleUnitTEST.java b/src/test/java/com/mc/main/advanced/ui/scanner/ConsoleUnitTEST.java index 27dc1f2..eb40ea1 100644 --- a/src/test/java/com/mc/main/advanced/ui/scanner/ConsoleUnitTEST.java +++ b/src/test/java/com/mc/main/advanced/ui/scanner/ConsoleUnitTEST.java @@ -11,12 +11,14 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import com.mc.main.util.StringConst; + public class ConsoleUnitTEST { //TODO: add tests to control private static context //TODO: Mocking private static final String userInput = "TEST_WORD"; - private static final String div = StringConst.DIV; + private static final String div = StringConst.DIV.toString(); private static int testCount; @BeforeEach From c9297dd5ff6f5f789d2d4e97a6eb08b4c2a64492 Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Tue, 29 Mar 2022 11:49:29 +0100 Subject: [PATCH 14/23] AD: ConcurrencyPractice, pending Unit Tests --- src/main/java/com/mc/main/Application.java | 2 + src/main/java/com/mc/main/Runner.java | 1 + .../mc/main/advanced/ConcurrencyPractice.java | 181 ++++++++++++++++++ .../com/mc/main/advanced/ThreadPractice.java | 26 ++- .../threads/concurrency/ClickerThread.java | 24 +++ .../threads/concurrency/Consumer.java | 28 +++ .../advanced/threads/concurrency/Counter.java | 9 + .../threads/concurrency/CounterPOJO.java | 30 +++ .../threads/concurrency/CounterSync.java | 30 +++ .../threads/concurrency/Provider.java | 35 ++++ .../threads/concurrency/TradeExchange.java | 50 +++++ .../java/com/mc/main/util/ReportFormater.java | 19 ++ .../advanced/ConcurrencyPracticeUnitTEST.java | 18 ++ .../concurrency/ClickerThreadUnitTEST.java | 5 + .../threads/concurrency/ConsumerUnitTEST.java | 5 + .../concurrency/CounterPOJOUnitTEST.java | 5 + .../concurrency/CounterSyncUnitTEST.java | 5 + .../threads/concurrency/ProviderUnitTEST.java | 5 + .../concurrency/TradeExchangeUnitTEST.java | 5 + 19 files changed, 475 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/mc/main/advanced/ConcurrencyPractice.java create mode 100644 src/main/java/com/mc/main/advanced/threads/concurrency/ClickerThread.java create mode 100644 src/main/java/com/mc/main/advanced/threads/concurrency/Consumer.java create mode 100644 src/main/java/com/mc/main/advanced/threads/concurrency/Counter.java create mode 100644 src/main/java/com/mc/main/advanced/threads/concurrency/CounterPOJO.java create mode 100644 src/main/java/com/mc/main/advanced/threads/concurrency/CounterSync.java create mode 100644 src/main/java/com/mc/main/advanced/threads/concurrency/Provider.java create mode 100644 src/main/java/com/mc/main/advanced/threads/concurrency/TradeExchange.java create mode 100644 src/main/java/com/mc/main/util/ReportFormater.java create mode 100644 src/test/java/com/mc/main/advanced/ConcurrencyPracticeUnitTEST.java create mode 100644 src/test/java/com/mc/main/advanced/threads/concurrency/ClickerThreadUnitTEST.java create mode 100644 src/test/java/com/mc/main/advanced/threads/concurrency/ConsumerUnitTEST.java create mode 100644 src/test/java/com/mc/main/advanced/threads/concurrency/CounterPOJOUnitTEST.java create mode 100644 src/test/java/com/mc/main/advanced/threads/concurrency/CounterSyncUnitTEST.java create mode 100644 src/test/java/com/mc/main/advanced/threads/concurrency/ProviderUnitTEST.java create mode 100644 src/test/java/com/mc/main/advanced/threads/concurrency/TradeExchangeUnitTEST.java diff --git a/src/main/java/com/mc/main/Application.java b/src/main/java/com/mc/main/Application.java index 87fc14d..415d25c 100644 --- a/src/main/java/com/mc/main/Application.java +++ b/src/main/java/com/mc/main/Application.java @@ -1,5 +1,6 @@ package com.mc.main; +import com.mc.main.advanced.ConcurrencyPractice; import com.mc.main.advanced.DesignPatternPractice; import com.mc.main.advanced.ExceptionPractice; import com.mc.main.advanced.IOPractice; @@ -81,6 +82,7 @@ public void run() { ReflectionPractice.start(); PatternPractice.start(); ThreadPractice.start(); + ConcurrencyPractice.start(); break; case JAVA8: diff --git a/src/main/java/com/mc/main/Runner.java b/src/main/java/com/mc/main/Runner.java index eca62a4..4da14d6 100644 --- a/src/main/java/com/mc/main/Runner.java +++ b/src/main/java/com/mc/main/Runner.java @@ -1,5 +1,6 @@ package com.mc.main; +import com.mc.main.advanced.ConcurrencyPractice; import com.mc.main.advanced.ui.scanner.Console; import com.mc.main.util.ApplicationTime; import com.mc.main.util.StringConst; diff --git a/src/main/java/com/mc/main/advanced/ConcurrencyPractice.java b/src/main/java/com/mc/main/advanced/ConcurrencyPractice.java new file mode 100644 index 0000000..c242460 --- /dev/null +++ b/src/main/java/com/mc/main/advanced/ConcurrencyPractice.java @@ -0,0 +1,181 @@ +package com.mc.main.advanced; + +import com.mc.main.advanced.threads.concurrency.ClickerThread; +import com.mc.main.advanced.threads.concurrency.Consumer; +import com.mc.main.advanced.threads.concurrency.Counter; +import com.mc.main.advanced.threads.concurrency.CounterPOJO; +import com.mc.main.advanced.threads.concurrency.CounterSync; +import com.mc.main.advanced.threads.concurrency.Provider; +import com.mc.main.advanced.threads.concurrency.TradeExchange; +import com.mc.main.util.Practice; +import com.mc.main.util.ReportFormater; +import com.mc.main.util.StringConst; + +public class ConcurrencyPractice extends Practice { + + private static int loops = 1500; + + private ConcurrencyPractice() { + super(); + } + + public static void start() { + printHeader("Multi-Threading"); + + try { + // When we attempt to access singluar resources with multiple threads, + // we can encounter strange behaviour with standard implementation. + // + // For the next section, please review CounterPOJO and the "concurrencyRace()" method. + concurrencyRace(new CounterPOJO(loops)); + + // To get the most out of having threads running parallel to each other + // We must first respect the issues of: + // - Thread Interference: + // Where two or more threads attempt to access a single object attribute or resources + // - Memory Consistency Issues: + // Where memory maybe accessed before reporting, showing a result influenced by + // an external thread before being exposed. (review "happens-before" relationships) + // - The synchronise keyword + // - Intrinsic Locks: + // or Monitor lock, an Internal state of objects that helps inform + // thread ownership. + // - Atomic access: + // An action or state that happens once or not at all + + // For the next section, please review CounterSync and the "concurrencySynchronized()" method. + concurrencySynchronized(new CounterSync(loops)); + + + // There are many ways we can use Synchronize to establish respect to intrinsic locks + // Intrinsic or monitor locks are an internal state to objects that are handled. + // + // A thread should request exclusive and consistent access to an objects lock + // in order to inform other threads that this object is being handled specifically by + // a unique thread. + // + // We're not going to investigate additional ways of controlling Synchronization of + // objects or the manual creation of locks in this project. + + // Liveliness + // ~ a state of how smooth a concurrent application runs based on continuous + // running threads - rather than the traffic jam of stalled processes and + // sluggish behaviour. + // ======================================== + // Deadlock: + // Thread pair or groups that rely on eachother to proceed internally and thus + // forever stuck! + // Starvation: + // A resource that is accessed excessively by a singular thread + // as such that it's greed might lock out other threads accessing + // the resource and holding reserve thread processes up. + // Livelock: + // Two Threads attempt to interact with eachother to the detriment of another + // they aren't stuck so much as they are too busy to proceed and + // might attempt to make way for another thread only to infact block it. + // Immutable values: + // Concurrency often works best when threads access immutable values - change + // can present considerations for parallel access; immutability makes a value + // safer for a thread to utilise as a lookup reference. No doubt is placed on + // the variable nature of value shifting. + + // Guarded Block + // ======================================== + // for those that program applications, you'll understand that most operations are often spun as a + // result of a while loop iteration; continuously evaluating conditions to see if they are true. + // + // Then finally closing or resuming the intended functionality. + // guarded blocks allow us to introduce reliance on thread change, rather than + // this reduces the stress a thread might have by reserving it stall until + // another thread informs it is appropriate to continue operation. + guardedBlock(); + + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private static void guardedBlock() throws InterruptedException { + // In our example we'll use a one-way trading system, the exchange takes place between + // two types of thread/objects - a provider and consumer. + // + // They'll both need to interact with the TradeExchange Object, a consumer cannot take something + // until our provider has produced data as part of the exchange. + ReportFormater.reportHeader("\tGuarded Block"); + TradeExchange exchange = new TradeExchange(); + + Thread factory = new Thread(new Provider(exchange)); + Thread customer = new Thread(new Consumer(exchange)); + + factory.start(); + customer.start(); + + factory.join(); + + System.out.format("%s\n\n", StringConst.DIV); + } + + private static void sharedCounter(Counter counter) throws InterruptedException { + // We'll have a global thread with 2 child threads + // this way the operation is completely seperate and unbias towards Main + Thread testThread = new Thread() { + + public void run() { + Thread myClickerTrue = new Thread(new ClickerThread(counter, true)); + Thread myClickerFalse = new Thread(new ClickerThread(counter, false)); + + myClickerTrue.start(); + myClickerFalse.start(); + + try { + myClickerFalse.join(); + myClickerTrue.join(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + }; + + testThread.start(); + testThread.join(); + + int result = (int) counter.value(); + boolean flag = result == 0 | result == loops; + + ReportFormater.reportDivided( + String.format("%s Final Value: %s\n%s", + counter.getClass().getSimpleName(), counter.value(), + flag ? "INFO: Operation Completed successfully!" : + "ERROR: An error occurred when accessing Counter") + ); + System.out.format("Threads resolved!\n\n", StringConst.DIV); + } + + private static void concurrencyRace(CounterPOJO counter) throws InterruptedException { + // Both threads have an objective, either add 5000 or remove 5000 + // values from a shared resources - myCounter.count; + // + // However when we run threads accessing the shared memory location + // strange behaviour can arise as a result of 2 threads accessing the same + // resource. + // because they do not rely on eachother - they don't inform eachother of what + // they are doing to the shared space. + ReportFormater.reportHeader("\tConcurrency Race"); + + sharedCounter(counter); + } + + private static void concurrencySynchronized(CounterSync counter) throws InterruptedException { + // In this case, we are doing the same as concurrencyRace, however the Counter Object now + // implements synchronized behaviour with object access. + // + // This expands the respect of accessing shared resources by allowing us + // to inform java that this object might be accessed by independent threads and to + // respect the order of access to avoid unusual behaviour. + ReportFormater.reportHeader("\tConcurrency Synchronized"); + + sharedCounter(counter); + } + +} diff --git a/src/main/java/com/mc/main/advanced/ThreadPractice.java b/src/main/java/com/mc/main/advanced/ThreadPractice.java index 90116b5..b953499 100644 --- a/src/main/java/com/mc/main/advanced/ThreadPractice.java +++ b/src/main/java/com/mc/main/advanced/ThreadPractice.java @@ -72,12 +72,17 @@ public static void start() { // have 3-7 free cores // not doing anything! // - // When we break up instruction intense operations into thread, we can directly + // When we break up instruction intense operations into threads, we can directly // benefit from multi-core processing and enhance the efficiency of our code by // making use of unutilised processing units. + // + // However the usage of benefiting from parallel processing will primarily be covered in + // ConcurrencyPractice. + // + // First we need to appreciate the nuance of threads and their lifecycles. printHeader("Threads"); - // all threads have the capability of throwing an InterruptedException + // All threads have the capability of throwing an InterruptedException // as such we'll capture any throws here try { // Let's begin looking at a very basic example! @@ -114,12 +119,11 @@ private static void startingThreads() { // is in fact a thread. When we create a method that is called within main - it // runs parallel to the // application at run time - meaning the order of output or execution is now - // seperate from the - // main application. + // separate from the main application. // - // We can control how thread operate and their respect to each-other - this is - // in-fact hugely important - // in getting the most out of our application and available hardware! + // We can control how threads operate and their respect to each-other - this is + // in-fact hugely important in getting the most out of our application + // and available hardware! } private static void lifeCycle() throws InterruptedException { @@ -243,7 +247,13 @@ private static void threadControl() throws InterruptedException { } } threadMessage(myLoopIterrupt, "State - " + myLoopIterrupt.getState()); - + + // So far we've looked at some examples of procedural threads, pausing main for the benefit + // of expression of the application through a secondary thread before resuming + // our main thread. + // + // In Concurrency there are conditions of control needed to be understood that require control + // interactions beyond just joins, let's move onto Concurrency! } } diff --git a/src/main/java/com/mc/main/advanced/threads/concurrency/ClickerThread.java b/src/main/java/com/mc/main/advanced/threads/concurrency/ClickerThread.java new file mode 100644 index 0000000..5ba4843 --- /dev/null +++ b/src/main/java/com/mc/main/advanced/threads/concurrency/ClickerThread.java @@ -0,0 +1,24 @@ +package com.mc.main.advanced.threads.concurrency; + +public class ClickerThread implements Runnable { + + private Counter count; + private boolean flag; + + public ClickerThread(Counter count, boolean increment) { + super(); + this.count = count; + this.flag = increment; + } + + @Override + public void run() { + if(flag) { + count.increment(); + } else { + count.decrement(); + } + System.out.format("Counter%s Value: %d\n", flag ? "++" : "--", count.value()); + } + +} diff --git a/src/main/java/com/mc/main/advanced/threads/concurrency/Consumer.java b/src/main/java/com/mc/main/advanced/threads/concurrency/Consumer.java new file mode 100644 index 0000000..d9b95bf --- /dev/null +++ b/src/main/java/com/mc/main/advanced/threads/concurrency/Consumer.java @@ -0,0 +1,28 @@ +package com.mc.main.advanced.threads.concurrency; + +import java.util.Random; + +public class Consumer implements Runnable { + + private TradeExchange exchange; + + public Consumer(TradeExchange exchange) { + super(); + this.exchange = exchange; + } + + @Override + public void run() { + + Random random = new Random(); + for (String message = exchange.retrieve(); + ! message.equals("DONE"); + message = exchange.retrieve()) { + System.out.format("MESSAGE RECEIVED: %s%n", message); + try { + Thread.sleep(random.nextInt(500)); + } catch (InterruptedException e) {} + } + } + +} diff --git a/src/main/java/com/mc/main/advanced/threads/concurrency/Counter.java b/src/main/java/com/mc/main/advanced/threads/concurrency/Counter.java new file mode 100644 index 0000000..197bdec --- /dev/null +++ b/src/main/java/com/mc/main/advanced/threads/concurrency/Counter.java @@ -0,0 +1,9 @@ +package com.mc.main.advanced.threads.concurrency; + +public interface Counter { + + void increment(); + void decrement(); + T value(); + +} diff --git a/src/main/java/com/mc/main/advanced/threads/concurrency/CounterPOJO.java b/src/main/java/com/mc/main/advanced/threads/concurrency/CounterPOJO.java new file mode 100644 index 0000000..dc6fcdb --- /dev/null +++ b/src/main/java/com/mc/main/advanced/threads/concurrency/CounterPOJO.java @@ -0,0 +1,30 @@ +package com.mc.main.advanced.threads.concurrency; + +public class CounterPOJO implements Counter { + + private static int count; + private int iterations; + + public CounterPOJO(int iterations) { + super(); + count = 0; + this.iterations = iterations > 0 ? iterations : 1; + } + + public void increment() { + for(int x = 0; x < 5000 ; x++) { + count++; + } + } + + public void decrement() { + for(int x = 0; x < 5000 ; x++) { + count--; + } + } + + public Integer value() { + return count; + } + +} diff --git a/src/main/java/com/mc/main/advanced/threads/concurrency/CounterSync.java b/src/main/java/com/mc/main/advanced/threads/concurrency/CounterSync.java new file mode 100644 index 0000000..ba2b90d --- /dev/null +++ b/src/main/java/com/mc/main/advanced/threads/concurrency/CounterSync.java @@ -0,0 +1,30 @@ +package com.mc.main.advanced.threads.concurrency; + +public class CounterSync implements Counter { + + private static int count; + private int iterations; + + public CounterSync(int iterations) { + super(); + count = 0; + this.iterations = iterations > 0 ? iterations : 1; + } + + public synchronized void increment() { + for(int x = 0; x < iterations ; x++) { + count++; + } + } + + public synchronized void decrement() { + for(int x = 0; x < iterations ; x++) { + count--; + } + } + + public Integer value() { + return count; + } + +} diff --git a/src/main/java/com/mc/main/advanced/threads/concurrency/Provider.java b/src/main/java/com/mc/main/advanced/threads/concurrency/Provider.java new file mode 100644 index 0000000..62f621b --- /dev/null +++ b/src/main/java/com/mc/main/advanced/threads/concurrency/Provider.java @@ -0,0 +1,35 @@ +package com.mc.main.advanced.threads.concurrency; + +import java.util.Random; + +public class Provider implements Runnable { + + private TradeExchange exchange; + + public Provider(TradeExchange exchange) { + super(); + this.exchange = exchange; + } + + @Override + public void run() { + String importantInfo[] = { + "Mares eat oats", + "Does eat oats", + "Little lambs eat ivy", + "A kid will eat ivy too" + }; + Random random = new Random(); + + for (int i = 0; + i < importantInfo.length; + i++) { + exchange.put(importantInfo[i]); + try { + Thread.sleep(random.nextInt(500)); + } catch (InterruptedException e) {} + } + exchange.put("DONE"); + } + +} diff --git a/src/main/java/com/mc/main/advanced/threads/concurrency/TradeExchange.java b/src/main/java/com/mc/main/advanced/threads/concurrency/TradeExchange.java new file mode 100644 index 0000000..9d1b1a7 --- /dev/null +++ b/src/main/java/com/mc/main/advanced/threads/concurrency/TradeExchange.java @@ -0,0 +1,50 @@ +package com.mc.main.advanced.threads.concurrency; + +public class TradeExchange { + + private String hold; + private boolean empty; + + public TradeExchange() { + super(); + empty = true; // ensure that we don't default to false + } + + public synchronized void put(String msg) { + // Wait until message has been retrieved. + while (!empty) { + try { + wait(); + } catch (InterruptedException e) {} + } + + // Store message + this.hold = msg; + // Ensure that empty status for loop + empty = false; + + // Notify consumer that status has changed. + notifyAll(); + } + + public synchronized String retrieve() { + String localHold = null; + + // Wait until message is available. + while (empty) { + try { + wait(); + } catch (InterruptedException e) {} + } + + // Toggle status. + localHold = hold; + hold = null; + empty = true; + + // Notify producer that status has changed. + notifyAll(); + return localHold; + } + +} diff --git a/src/main/java/com/mc/main/util/ReportFormater.java b/src/main/java/com/mc/main/util/ReportFormater.java new file mode 100644 index 0000000..b928b06 --- /dev/null +++ b/src/main/java/com/mc/main/util/ReportFormater.java @@ -0,0 +1,19 @@ +package com.mc.main.util; + +public class ReportFormater { + + public static void reportHeader(String header) { + System.out.format("%1$s%2$s\n%1$s", StringConst.DIV + "\n", header); + } + + public static void reportDivided(String header, String... contents) { + System.out.format("%1$s%2$s\n", StringConst.DIV + "\n", header); + + for(String content : contents) { + System.out.format("%1$s%2$s\n", StringConst.DIV + "\n", content); + } + + System.out.format("%s", StringConst.DIV + "\n"); + } + +} diff --git a/src/test/java/com/mc/main/advanced/ConcurrencyPracticeUnitTEST.java b/src/test/java/com/mc/main/advanced/ConcurrencyPracticeUnitTEST.java new file mode 100644 index 0000000..22033cf --- /dev/null +++ b/src/test/java/com/mc/main/advanced/ConcurrencyPracticeUnitTEST.java @@ -0,0 +1,18 @@ +package com.mc.main.advanced; + +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.Test; + +public class ConcurrencyPracticeUnitTEST { + + @Test + public void concurrencyStart() { + try { + ConcurrencyPractice.start(); + }catch (Exception e) { + fail(); + } + } + +} diff --git a/src/test/java/com/mc/main/advanced/threads/concurrency/ClickerThreadUnitTEST.java b/src/test/java/com/mc/main/advanced/threads/concurrency/ClickerThreadUnitTEST.java new file mode 100644 index 0000000..fce9c53 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/threads/concurrency/ClickerThreadUnitTEST.java @@ -0,0 +1,5 @@ +package com.mc.main.advanced.threads.concurrency; + +public class ClickerThreadUnitTEST { + +} diff --git a/src/test/java/com/mc/main/advanced/threads/concurrency/ConsumerUnitTEST.java b/src/test/java/com/mc/main/advanced/threads/concurrency/ConsumerUnitTEST.java new file mode 100644 index 0000000..f8b3026 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/threads/concurrency/ConsumerUnitTEST.java @@ -0,0 +1,5 @@ +package com.mc.main.advanced.threads.concurrency; + +public class ConsumerUnitTEST { + +} diff --git a/src/test/java/com/mc/main/advanced/threads/concurrency/CounterPOJOUnitTEST.java b/src/test/java/com/mc/main/advanced/threads/concurrency/CounterPOJOUnitTEST.java new file mode 100644 index 0000000..e137f96 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/threads/concurrency/CounterPOJOUnitTEST.java @@ -0,0 +1,5 @@ +package com.mc.main.advanced.threads.concurrency; + +public class CounterPOJOUnitTEST { + +} \ No newline at end of file diff --git a/src/test/java/com/mc/main/advanced/threads/concurrency/CounterSyncUnitTEST.java b/src/test/java/com/mc/main/advanced/threads/concurrency/CounterSyncUnitTEST.java new file mode 100644 index 0000000..efb4a18 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/threads/concurrency/CounterSyncUnitTEST.java @@ -0,0 +1,5 @@ +package com.mc.main.advanced.threads.concurrency; + +public class CounterSyncUnitTEST { + +} \ No newline at end of file diff --git a/src/test/java/com/mc/main/advanced/threads/concurrency/ProviderUnitTEST.java b/src/test/java/com/mc/main/advanced/threads/concurrency/ProviderUnitTEST.java new file mode 100644 index 0000000..3332920 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/threads/concurrency/ProviderUnitTEST.java @@ -0,0 +1,5 @@ +package com.mc.main.advanced.threads.concurrency; + +public class ProviderUnitTEST { + +} \ No newline at end of file diff --git a/src/test/java/com/mc/main/advanced/threads/concurrency/TradeExchangeUnitTEST.java b/src/test/java/com/mc/main/advanced/threads/concurrency/TradeExchangeUnitTEST.java new file mode 100644 index 0000000..502dea8 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/threads/concurrency/TradeExchangeUnitTEST.java @@ -0,0 +1,5 @@ +package com.mc.main.advanced.threads.concurrency; + +public class TradeExchangeUnitTEST { + +} \ No newline at end of file From 007d47caf0941df4365cab616f4761fb7b7d5435 Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Thu, 31 Mar 2022 15:04:59 +0100 Subject: [PATCH 15/23] AD: added in tests for Concurrency, recommend revisit testing --- .../threads/concurrency/ClickerThread.java | 4 +- .../threads/concurrency/CounterPOJO.java | 4 +- .../threads/concurrency/Provider.java | 18 +++---- .../concurrency/ClickerThreadUnitTEST.java | 52 +++++++++++++++++++ .../threads/concurrency/ConsumerUnitTEST.java | 46 ++++++++++++++++ .../concurrency/CounterPOJOUnitTEST.java | 46 ++++++++++++++++ .../concurrency/CounterSyncUnitTEST.java | 46 ++++++++++++++++ .../threads/concurrency/ProviderUnitTEST.java | 46 ++++++++++++++++ .../concurrency/TradeExchangeUnitTEST.java | 31 +++++++++++ 9 files changed, 279 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/mc/main/advanced/threads/concurrency/ClickerThread.java b/src/main/java/com/mc/main/advanced/threads/concurrency/ClickerThread.java index 5ba4843..cba2f3e 100644 --- a/src/main/java/com/mc/main/advanced/threads/concurrency/ClickerThread.java +++ b/src/main/java/com/mc/main/advanced/threads/concurrency/ClickerThread.java @@ -5,10 +5,10 @@ public class ClickerThread implements Runnable { private Counter count; private boolean flag; - public ClickerThread(Counter count, boolean increment) { + public ClickerThread(Counter count, boolean shouldIncrement) { super(); this.count = count; - this.flag = increment; + this.flag = shouldIncrement; } @Override diff --git a/src/main/java/com/mc/main/advanced/threads/concurrency/CounterPOJO.java b/src/main/java/com/mc/main/advanced/threads/concurrency/CounterPOJO.java index dc6fcdb..653521f 100644 --- a/src/main/java/com/mc/main/advanced/threads/concurrency/CounterPOJO.java +++ b/src/main/java/com/mc/main/advanced/threads/concurrency/CounterPOJO.java @@ -12,13 +12,13 @@ public CounterPOJO(int iterations) { } public void increment() { - for(int x = 0; x < 5000 ; x++) { + for(int x = 0; x < iterations ; x++) { count++; } } public void decrement() { - for(int x = 0; x < 5000 ; x++) { + for(int x = 0; x < iterations ; x++) { count--; } } diff --git a/src/main/java/com/mc/main/advanced/threads/concurrency/Provider.java b/src/main/java/com/mc/main/advanced/threads/concurrency/Provider.java index 62f621b..f66f3a6 100644 --- a/src/main/java/com/mc/main/advanced/threads/concurrency/Provider.java +++ b/src/main/java/com/mc/main/advanced/threads/concurrency/Provider.java @@ -13,20 +13,18 @@ public Provider(TradeExchange exchange) { @Override public void run() { - String importantInfo[] = { - "Mares eat oats", - "Does eat oats", - "Little lambs eat ivy", - "A kid will eat ivy too" + String messageList[] = { + "Message 1", + "Message 2", + "Message 3", + "Message 4" }; Random random = new Random(); - for (int i = 0; - i < importantInfo.length; - i++) { - exchange.put(importantInfo[i]); + for (int x = 0; x < messageList.length; x++) { + exchange.put(messageList[x]); try { - Thread.sleep(random.nextInt(500)); + Thread.sleep(random.nextInt(500)); } catch (InterruptedException e) {} } exchange.put("DONE"); diff --git a/src/test/java/com/mc/main/advanced/threads/concurrency/ClickerThreadUnitTEST.java b/src/test/java/com/mc/main/advanced/threads/concurrency/ClickerThreadUnitTEST.java index fce9c53..800d8d1 100644 --- a/src/test/java/com/mc/main/advanced/threads/concurrency/ClickerThreadUnitTEST.java +++ b/src/test/java/com/mc/main/advanced/threads/concurrency/ClickerThreadUnitTEST.java @@ -1,5 +1,57 @@ package com.mc.main.advanced.threads.concurrency; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + public class ClickerThreadUnitTEST { + private static ClickerThread testClicker; + private static CounterSync testCounter; + private static final int iterations = 500; + + @BeforeEach + public void testStart() { + testCounter = new CounterSync(iterations); + testClicker = new ClickerThread(testCounter, true); + } + + @Test + public void constructor() { + assertTrue(testClicker instanceof ClickerThread); + } + + //TODO: Implement mocking/concurrency solution + // Do not implement unit tests that rely on other objects - this is akin to a System test!!! + @Test + public void runIncrement() { + Thread testThread = new Thread(testClicker); + try { + testThread.start(); + testThread.join(); + + assertEquals(iterations, testCounter.value()); + } catch (Exception e) { + fail(e); + } + } + + @Test + public void runDecrement() { + testClicker = new ClickerThread(testCounter, false); + + Thread testThread = new Thread(testClicker); + try { + testThread.start(); + testThread.join(); + + assertEquals(-iterations, testCounter.value()); + } catch (Exception e) { + fail(e); + } + } + } diff --git a/src/test/java/com/mc/main/advanced/threads/concurrency/ConsumerUnitTEST.java b/src/test/java/com/mc/main/advanced/threads/concurrency/ConsumerUnitTEST.java index f8b3026..8f2ffb7 100644 --- a/src/test/java/com/mc/main/advanced/threads/concurrency/ConsumerUnitTEST.java +++ b/src/test/java/com/mc/main/advanced/threads/concurrency/ConsumerUnitTEST.java @@ -1,5 +1,51 @@ package com.mc.main.advanced.threads.concurrency; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + public class ConsumerUnitTEST { + + private static Consumer testConsumer; + private static TradeExchange testTradeExchange; + + @BeforeEach + public void testStart() { + testTradeExchange = new TradeExchange(); + testConsumer = new Consumer(testTradeExchange); + } + + @Test + public void constructor() { + assertTrue(testConsumer instanceof Runnable); + assertTrue(testConsumer instanceof Consumer); + } + + //TODO: Implement mocking/concurrency solution + // Do not implement unit tests that rely on other objects - this is akin to a System test!!! + @Test + @Timeout(2500) + public void runTest() { + Thread testProvider, testConsumer; + try { + testConsumer = new Thread(new Consumer(testTradeExchange)); + testProvider = new Thread(new Provider(testTradeExchange)); + + testProvider.start(); + testConsumer.start(); + + testProvider.join(); + testConsumer.join(); + + assertEquals(Thread.State.TERMINATED, testConsumer.getState()); + assertEquals(Thread.State.TERMINATED, testProvider.getState()); + }catch (Exception e) { + fail(); + } + } } diff --git a/src/test/java/com/mc/main/advanced/threads/concurrency/CounterPOJOUnitTEST.java b/src/test/java/com/mc/main/advanced/threads/concurrency/CounterPOJOUnitTEST.java index e137f96..2b55a36 100644 --- a/src/test/java/com/mc/main/advanced/threads/concurrency/CounterPOJOUnitTEST.java +++ b/src/test/java/com/mc/main/advanced/threads/concurrency/CounterPOJOUnitTEST.java @@ -1,5 +1,51 @@ package com.mc.main.advanced.threads.concurrency; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + public class CounterPOJOUnitTEST { + + private static CounterPOJO testCounter; + private static final int iterations = 50; + + @BeforeEach + public void testStart() { + testCounter = new CounterPOJO(iterations); + } + + @Test + public void constructor() { + assertTrue(testCounter instanceof Counter); + assertTrue(testCounter instanceof CounterPOJO); + assertEquals(0, testCounter.value()); + } + + @Test + public void increment() { + testCounter.increment(); + + int result = testCounter.value(); + + assertEquals(iterations, testCounter.value()); + } + + @Test + public void decrement() { + testCounter.decrement(); + + int result = testCounter.value(); + + assertEquals(-iterations, testCounter.value()); + } + + @Test + public void value() { + int result = testCounter.value(); + + assertEquals(0, testCounter.value()); + } } \ No newline at end of file diff --git a/src/test/java/com/mc/main/advanced/threads/concurrency/CounterSyncUnitTEST.java b/src/test/java/com/mc/main/advanced/threads/concurrency/CounterSyncUnitTEST.java index efb4a18..e19e8de 100644 --- a/src/test/java/com/mc/main/advanced/threads/concurrency/CounterSyncUnitTEST.java +++ b/src/test/java/com/mc/main/advanced/threads/concurrency/CounterSyncUnitTEST.java @@ -1,5 +1,51 @@ package com.mc.main.advanced.threads.concurrency; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + public class CounterSyncUnitTEST { + private static CounterSync testCounter; + private static final int iterations = 50; + + @BeforeEach + public void testStart() { + testCounter = new CounterSync(iterations); + } + + @Test + public void constructor() { + assertTrue(testCounter instanceof Counter); + assertTrue(testCounter instanceof CounterSync); + assertEquals(0, testCounter.value()); + } + + @Test + public void increment() { + testCounter.increment(); + + int result = testCounter.value(); + + assertEquals(iterations, testCounter.value()); + } + + @Test + public void decrement() { + testCounter.decrement(); + + int result = testCounter.value(); + + assertEquals(-iterations, testCounter.value()); + } + + @Test + public void value() { + int result = testCounter.value(); + + assertEquals(0, testCounter.value()); + } + } \ No newline at end of file diff --git a/src/test/java/com/mc/main/advanced/threads/concurrency/ProviderUnitTEST.java b/src/test/java/com/mc/main/advanced/threads/concurrency/ProviderUnitTEST.java index 3332920..1d30fb0 100644 --- a/src/test/java/com/mc/main/advanced/threads/concurrency/ProviderUnitTEST.java +++ b/src/test/java/com/mc/main/advanced/threads/concurrency/ProviderUnitTEST.java @@ -1,5 +1,51 @@ package com.mc.main.advanced.threads.concurrency; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + public class ProviderUnitTEST { + private static Provider testProvider; + private static TradeExchange testTradeExchange; + + @BeforeEach + public void testStart() { + testTradeExchange = new TradeExchange(); + testProvider = new Provider(testTradeExchange); + } + + @Test + public void constructor() { + assertTrue(testProvider instanceof Runnable); + assertTrue(testProvider instanceof Provider); + } + + //TODO: Implement mocking/concurrency solution + // Do not implement unit tests that rely on other objects - this is akin to a System test!!! + @Test + @Timeout(2500) + public void runTest() { + Thread testProvider, testConsumer; + try { + testConsumer = new Thread(new Consumer(testTradeExchange)); + testProvider = new Thread(new Provider(testTradeExchange)); + + testProvider.start(); + testConsumer.start(); + + testProvider.join(); + testConsumer.join(); + + assertEquals(Thread.State.TERMINATED, testConsumer.getState()); + assertEquals(Thread.State.TERMINATED, testProvider.getState()); + }catch (Exception e) { + fail(); + } + } + } \ No newline at end of file diff --git a/src/test/java/com/mc/main/advanced/threads/concurrency/TradeExchangeUnitTEST.java b/src/test/java/com/mc/main/advanced/threads/concurrency/TradeExchangeUnitTEST.java index 502dea8..7cb1907 100644 --- a/src/test/java/com/mc/main/advanced/threads/concurrency/TradeExchangeUnitTEST.java +++ b/src/test/java/com/mc/main/advanced/threads/concurrency/TradeExchangeUnitTEST.java @@ -1,5 +1,36 @@ package com.mc.main.advanced.threads.concurrency; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + public class TradeExchangeUnitTEST { + private static TradeExchange testExchange; + + @BeforeEach + public void testStart() { + testExchange = new TradeExchange(); + } + + @Test + public void constructor() { + assertTrue(testExchange instanceof TradeExchange); + } + + @Test + public void putRetrieve() { + String result, expected; + + expected = "Expected"; + testExchange.put(expected); + result = testExchange.retrieve(); + + assertEquals(expected, result); + } + + //TODO: add testing for condition where thread wait occurs. + } \ No newline at end of file From 900b695ca5420b985ab08961ac8243d3ccad949d Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Thu, 31 Mar 2022 15:09:49 +0100 Subject: [PATCH 16/23] AD: readme updated --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 202f074..f76da02 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ The topics that'll be covered are: - [ ] Annotations - [X] Java Pattern/Regular Expression - [X] Threads - - [ ] Multithreading + - [X] Multithreading - [X] Reflection --- From 56a21e1dda7e2d78e16a5575beeef7b9202ce4ba Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Mon, 4 Apr 2022 20:09:04 +0100 Subject: [PATCH 17/23] AD: annotation practice --- src/main/java/com/mc/main/Application.java | 2 + src/main/java/com/mc/main/Runner.java | 5 +- .../mc/main/advanced/AnnotationPractice.java | 35 ++++++++ .../mc/main/advanced/ConcurrencyPractice.java | 2 +- .../annotations/ExampleAnnotation.java | 68 +++++++++++++++ .../advanced/annotations/casestudy/Init.java | 12 +++ .../annotations/casestudy/JsonElement.java | 20 +++++ .../casestudy/JsonSerializable.java | 15 ++++ .../casestudy/ObjectToJsonConverter.java | 84 +++++++++++++++++++ .../annotations/casestudy/Person.java | 36 ++++++++ 10 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/mc/main/advanced/AnnotationPractice.java create mode 100644 src/main/java/com/mc/main/advanced/annotations/ExampleAnnotation.java create mode 100644 src/main/java/com/mc/main/advanced/annotations/casestudy/Init.java create mode 100644 src/main/java/com/mc/main/advanced/annotations/casestudy/JsonElement.java create mode 100644 src/main/java/com/mc/main/advanced/annotations/casestudy/JsonSerializable.java create mode 100644 src/main/java/com/mc/main/advanced/annotations/casestudy/ObjectToJsonConverter.java create mode 100644 src/main/java/com/mc/main/advanced/annotations/casestudy/Person.java diff --git a/src/main/java/com/mc/main/Application.java b/src/main/java/com/mc/main/Application.java index 415d25c..13d64ea 100644 --- a/src/main/java/com/mc/main/Application.java +++ b/src/main/java/com/mc/main/Application.java @@ -1,5 +1,6 @@ package com.mc.main; +import com.mc.main.advanced.AnnotationPractice; import com.mc.main.advanced.ConcurrencyPractice; import com.mc.main.advanced.DesignPatternPractice; import com.mc.main.advanced.ExceptionPractice; @@ -83,6 +84,7 @@ public void run() { PatternPractice.start(); ThreadPractice.start(); ConcurrencyPractice.start(); + AnnotationPractice.start(); break; case JAVA8: diff --git a/src/main/java/com/mc/main/Runner.java b/src/main/java/com/mc/main/Runner.java index 4da14d6..95d8c6c 100644 --- a/src/main/java/com/mc/main/Runner.java +++ b/src/main/java/com/mc/main/Runner.java @@ -1,6 +1,5 @@ package com.mc.main; -import com.mc.main.advanced.ConcurrencyPractice; import com.mc.main.advanced.ui.scanner.Console; import com.mc.main.util.ApplicationTime; import com.mc.main.util.StringConst; @@ -44,6 +43,10 @@ public static void main(String[] args) { } } + // ======================================== + // Main Runner instructions + // ======================================== + private static final ApplicationTime time = new ApplicationTime(); protected static void start() { diff --git a/src/main/java/com/mc/main/advanced/AnnotationPractice.java b/src/main/java/com/mc/main/advanced/AnnotationPractice.java new file mode 100644 index 0000000..ffa54b7 --- /dev/null +++ b/src/main/java/com/mc/main/advanced/AnnotationPractice.java @@ -0,0 +1,35 @@ +package com.mc.main.advanced; + +import com.mc.main.advanced.annotations.ExampleAnnotation; +import com.mc.main.advanced.annotations.casestudy.ObjectToJsonConverter; +import com.mc.main.advanced.annotations.casestudy.Person; +import com.mc.main.util.Practice; +import com.mc.main.util.ReportFormater; + +// Let's review ExampleAnnotation +@ExampleAnnotation +public class AnnotationPractice extends Practice { + + private AnnotationPractice() { + super(); + } + + public static void start() { + printHeader("Annotations"); + + String result; + Person person = new Person("caspian", "lovett", "28"); + + try { + result = ObjectToJsonConverter.convertToJson(person); + ReportFormater.reportDivided("\tPerson JSON output:", result); + } catch (Exception e) { + e.printStackTrace(); + } + + // Annotations are useful as a means to provide data about our classes + // similar to a form of abstraction, we can use annotations as a standard + // or warning. + } + +} diff --git a/src/main/java/com/mc/main/advanced/ConcurrencyPractice.java b/src/main/java/com/mc/main/advanced/ConcurrencyPractice.java index c242460..4eae327 100644 --- a/src/main/java/com/mc/main/advanced/ConcurrencyPractice.java +++ b/src/main/java/com/mc/main/advanced/ConcurrencyPractice.java @@ -112,7 +112,7 @@ private static void guardedBlock() throws InterruptedException { factory.join(); - System.out.format("%s\n\n", StringConst.DIV); + System.out.format("%s\n", StringConst.DIV); } private static void sharedCounter(Counter counter) throws InterruptedException { diff --git a/src/main/java/com/mc/main/advanced/annotations/ExampleAnnotation.java b/src/main/java/com/mc/main/advanced/annotations/ExampleAnnotation.java new file mode 100644 index 0000000..fe96dc3 --- /dev/null +++ b/src/main/java/com/mc/main/advanced/annotations/ExampleAnnotation.java @@ -0,0 +1,68 @@ +package com.mc.main.advanced.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/* + * As outlined by Oracle documentations: + * ======================================== + * Information for the compiler + * — Annotations can be used by the compiler to detect errors or suppress warnings. + * Compile-time and deployment-time processing + * — Software tools can process annotation information to generate code, XML files, and so forth. + * Runtime processing + * — Some annotations are available to be examined at runtime. + * ======================================== + * + * In certain recent examples, you might see @Override or in some cases @SuppressWarnings + * These are annotations, but in particularly these are annotations that informs the compiler about details + * about code that indicate a state or useful scenario that helps with the construction of executable code. + * + * Perhaps you want to help inform the compiler that a warning isn't actually applicable as a result of a + * false/positive or some other case where the warning is not actually a problem. + * + * Override allows us to inform the compiler that a child class is overriding parent implementation. + * + * Compile-time annotations help us produce implementation or some process that occurs prior to the execution of code + * this can build upon the application to provided resources or implementation that can be inferred from + * existing implementation. Project Lombok is an example of a particularly example of some of the capabilities + * of this annotation, where certain accessor methods can be generated as a result of the appropriate annotations. + * + * In general, Annotations are designed to be some form of Meta data - data about data. + * +*/ + +// In order to create a custom annotation we simply declare our interface with the @ symbol +// as part of the interface keyword. +// +// This will allow us to use @Example Annotation +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.TYPE) +public @interface ExampleAnnotation { + + // Annotations aren't really part of the end result of code, more like a set of instructions + // to provide information about our data to the compiler. + // + // As such we can control when this annotation is triggered, but also we can determine what it precisely + // controls information about using the @Target annotation to define it's focus based on the ElementType: + // TYPE + // Field + // Constructor + // Method + // Parameter + // etc... + + // Retention focuses more on the cycle of compilation, either "Runtime" or "Source" + // Runtime will focus on active code and providing a reference opportunity through annotations + // + // + // Source focuses on providing detailing about implementation, but wouldn't effect the code running + // it's like a comment, but with opportunity to inform the compiler about the instructions provided. + // Consider: + // - @Override + // - @SuppressWarnings + + // Let's investigate AnnotationPractice.start() +} diff --git a/src/main/java/com/mc/main/advanced/annotations/casestudy/Init.java b/src/main/java/com/mc/main/advanced/annotations/casestudy/Init.java new file mode 100644 index 0000000..2b41e78 --- /dev/null +++ b/src/main/java/com/mc/main/advanced/annotations/casestudy/Init.java @@ -0,0 +1,12 @@ +package com.mc.main.advanced.annotations.casestudy; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// This will allow us to place a flag on a method, code might use reflection +// to identify that this method is marked to infer some kind of initialisation feature. +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Init {} diff --git a/src/main/java/com/mc/main/advanced/annotations/casestudy/JsonElement.java b/src/main/java/com/mc/main/advanced/annotations/casestudy/JsonElement.java new file mode 100644 index 0000000..5bac5cb --- /dev/null +++ b/src/main/java/com/mc/main/advanced/annotations/casestudy/JsonElement.java @@ -0,0 +1,20 @@ +package com.mc.main.advanced.annotations.casestudy; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// Here we have a annotation that marks a Field, this gives us the ability to provide some meta +// data around the field in question. +// +// When we build up a Json object, we divide the object members into a "key-values" model. +// in this annotation we can declare that this annotation can store and allow the allocation +// of a key String value in the form of a method with the respective type return value. +// +// we can also use default as a means to provide a default value not provided! +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface JsonElement { + public String key() default ""; +} diff --git a/src/main/java/com/mc/main/advanced/annotations/casestudy/JsonSerializable.java b/src/main/java/com/mc/main/advanced/annotations/casestudy/JsonSerializable.java new file mode 100644 index 0000000..578146a --- /dev/null +++ b/src/main/java/com/mc/main/advanced/annotations/casestudy/JsonSerializable.java @@ -0,0 +1,15 @@ +package com.mc.main.advanced.annotations.casestudy; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// Much like the Init Annotation, the idea here is to mark a TYPE - or Class for +// reference by code; it has implications about how a class and its implied capabilities. +// +// In this case, we are suggesting that this marked class is setup to be annotated to provide a clear +// potential for translating its class members to a JSON object +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface JsonSerializable {} diff --git a/src/main/java/com/mc/main/advanced/annotations/casestudy/ObjectToJsonConverter.java b/src/main/java/com/mc/main/advanced/annotations/casestudy/ObjectToJsonConverter.java new file mode 100644 index 0000000..8cb868f --- /dev/null +++ b/src/main/java/com/mc/main/advanced/annotations/casestudy/ObjectToJsonConverter.java @@ -0,0 +1,84 @@ +package com.mc.main.advanced.annotations.casestudy; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +// Here we can take advantage of the implementation with Annotations. +public class ObjectToJsonConverter { + + // A simple method to convert our target class into a JSON string format. + public static String convertToJson(Object object) throws Exception { + try { + isSerializable(object); // check + prepareObject(object); // setup + return getJsonString(object); // creation/release + } catch (Exception e) { + throw new Exception(e.getMessage()); + } + } + + // Main method of translation using java reflection based around + // Fields annotated with "JSONElement" + private static String getJsonString(Object object) throws Exception { + Class clazz = object.getClass(); + Map jsonElementsMap = new HashMap<>(); + + // Extract each field to Map for later management + for (Field field : clazz.getDeclaredFields()) { + field.setAccessible(true); + if (field.isAnnotationPresent(JsonElement.class)) { + jsonElementsMap.put(getKey(field), (String) field.get(object)); + } + } + + String jsonString = jsonElementsMap.entrySet() + .stream() + .map(entry -> String.format("%3$s%1$s%4$s%2$s%3$s", + entry.getKey(), entry.getValue(), "\"", "\":\"")) + .collect(Collectors.joining(",")); + + return "{" + jsonString + "}"; + } + + // Let's find any Method capability that is annotated to be a initialising process + // Via reflection + private static void prepareObject(Object object) throws Exception { + Class clazz = object.getClass(); + + for (Method method : clazz.getDeclaredMethods()) { + if (method.isAnnotationPresent(Init.class)) { + method.setAccessible(true); + method.invoke(object); + } + } + + } + + // Simple check to determine if the target object is annotated for the process + // we are looking to achieve. + private static void isSerializable(Object object) throws Exception { + + if (Objects.isNull(object)) { + throw new Exception("The object to serialize is null"); + } + + Class clazz = object.getClass(); + + if (!clazz.isAnnotationPresent(JsonSerializable.class)) { + throw new Exception("The class " + + clazz.getSimpleName() + + " is not annotated with JsonSerializable"); + } + } + + // Let's check if there's a key provided to us, if not - use field name instead! + private static String getKey(Field field) { + String value = field.getAnnotation(JsonElement.class).key(); + return value.isEmpty() ? field.getName() : value; + } + +} diff --git a/src/main/java/com/mc/main/advanced/annotations/casestudy/Person.java b/src/main/java/com/mc/main/advanced/annotations/casestudy/Person.java new file mode 100644 index 0000000..353b095 --- /dev/null +++ b/src/main/java/com/mc/main/advanced/annotations/casestudy/Person.java @@ -0,0 +1,36 @@ +package com.mc.main.advanced.annotations.casestudy; + +// Here we have our annotations applied +@JsonSerializable // A mark to imply this object can be translated into a JSON String +public class Person { + + @JsonElement // a marker of a field to provide. + private String forename; + + @JsonElement + private String surname; + + @JsonElement(key = "Age") // here we are using Key as a means to provide a bespoke label + private String age; + + @Init // Marked to run as part of the initialising process + private void initNames() { + // This is a setup method, part of a process outside of this class + // all we are doing is ensuring that fore-and-surnames start with capital letters + this.forename = this.forename.substring(0, 1).toUpperCase() + + this.forename.substring(1); + this.surname = this.surname.substring(0, 1).toUpperCase() + + this.surname.substring(1); + } + + public Person() { + super(); + } + + public Person(String forename, String surname, String age) { + this.forename = forename; + this.surname = surname; + this.age = age; + } + +} From 9aca75606b7baf407d8310655d468fbea8ed27ef Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Mon, 4 Apr 2022 21:07:45 +0100 Subject: [PATCH 18/23] AD: Added annotation tests --- README.md | 10 +- .../advanced/AnnotationPracticeUnitTEST.java | 18 +++ .../ObjectToJsonConverterUnitTEST.java | 125 ++++++++++++++++++ .../annotations/casestudy/PersonUnitTEST.java | 50 +++++++ 4 files changed, 198 insertions(+), 5 deletions(-) create mode 100644 src/test/java/com/mc/main/advanced/AnnotationPracticeUnitTEST.java create mode 100644 src/test/java/com/mc/main/advanced/annotations/casestudy/ObjectToJsonConverterUnitTEST.java create mode 100644 src/test/java/com/mc/main/advanced/annotations/casestudy/PersonUnitTEST.java diff --git a/README.md b/README.md index f76da02..02722e6 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Checkout the [feature-branch model here!](https://github.com/MorickClive/Vale-Ja ### Topics to Cover: The topics that'll be covered are: -- [ ] Java Basics: +- [X] Java Basics: - [X] Variables - [X] Methods - [X] Flow & Control @@ -23,7 +23,7 @@ The topics that'll be covered are: - [X] Iteration/Loop(for, for-each, while, do-while) - [X] Strings -- [ ] Object-Oriented Programming +- [X] Object-Oriented Programming - [X] Class practices - [X] Wrapper classes - [X] Object-Oriented Programming Principles: @@ -34,7 +34,7 @@ The topics that'll be covered are: - [X] Enumeration - [X] Generics -- [ ] Advanced Java: +- [X] Advanced Java: - [X] Exceptions: try, try-catch, try-finally - [X] Scanners - [X] Design Patterns @@ -48,7 +48,7 @@ The topics that'll be covered are: - [X] Observer Pattern - [X] Iterator Pattern - [X] File In/Out - - [ ] Annotations + - [X] Annotations - [X] Java Pattern/Regular Expression - [X] Threads - [X] Multithreading @@ -66,7 +66,7 @@ The topics that'll be covered are: - [ ] Static import - [X] Enumeration - [X] AutoBoxing (Wrapper Classes, type inference) - - [ ] Annotations + - [X] Annotations - [X] Generics - [ ] JAVA 7 diff --git a/src/test/java/com/mc/main/advanced/AnnotationPracticeUnitTEST.java b/src/test/java/com/mc/main/advanced/AnnotationPracticeUnitTEST.java new file mode 100644 index 0000000..0a34467 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/AnnotationPracticeUnitTEST.java @@ -0,0 +1,18 @@ +package com.mc.main.advanced; + +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.Test; + +public class AnnotationPracticeUnitTEST { + + @Test + public void annotationStart() { + try { + AnnotationPractice.start(); + }catch (Exception e) { + fail(); + } + } + +} diff --git a/src/test/java/com/mc/main/advanced/annotations/casestudy/ObjectToJsonConverterUnitTEST.java b/src/test/java/com/mc/main/advanced/annotations/casestudy/ObjectToJsonConverterUnitTEST.java new file mode 100644 index 0000000..60aab3c --- /dev/null +++ b/src/test/java/com/mc/main/advanced/annotations/casestudy/ObjectToJsonConverterUnitTEST.java @@ -0,0 +1,125 @@ +package com.mc.main.advanced.annotations.casestudy; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ObjectToJsonConverterUnitTEST { + + private static Person testObj; + + @BeforeEach + public void testStart() { + testObj = new Person("Caspian", "Lovett", "28"); + } + + @Test + public void convertToJson() throws Exception { + String expect = "{\"forename\":\"Caspian\",\"surname\":\"Lovett\",\"Age\":\"28\"}"; + String result = ObjectToJsonConverter.convertToJson(testObj); + + assertTrue(testObj instanceof Person); + assertEquals(expect, result); + } + + @Test + public void initNames() { + Method targetMethod; + String expect = "{\"forename\":\"Caspian\",\"surname\":\"Lovett\",\"Age\":\"28\"}"; + + try { + targetMethod = ObjectToJsonConverter.class.getDeclaredMethod("getJsonString", Object.class); + targetMethod.setAccessible(true); + String result = (String) targetMethod.invoke(ObjectToJsonConverter.class, testObj); + + assertTrue(testObj instanceof Person); + assertEquals(expect, result); + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + fail(); + } + } + + @Test + public void prepareObject() { + Method targetMethod; + + try { + targetMethod = ObjectToJsonConverter.class.getDeclaredMethod("prepareObject", Object.class); + targetMethod.setAccessible(true); + targetMethod.invoke(targetMethod, testObj); + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + fail(e.getMessage()); + } + + } + + @Test + public void isSerializableFailure1() { + Method targetMethod; + + try { + targetMethod = ObjectToJsonConverter.class.getDeclaredMethod("isSerializable", Object.class); + targetMethod.setAccessible(true); + targetMethod.invoke(targetMethod, new Object()); + fail(); + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + assertTrue(true); + } + } + + @Test + public void isSerializableFailure2() { + Method targetMethod; + + try { + targetMethod = ObjectToJsonConverter.class.getDeclaredMethod("isSerializable", Object.class); + targetMethod.setAccessible(true); + targetMethod.invoke(targetMethod, null); + fail(); + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + assertTrue(true); + } + } + + @Test + public void isSerializable() { + Method targetMethod; + + try { + targetMethod = ObjectToJsonConverter.class.getDeclaredMethod("isSerializable", Object.class); + targetMethod.setAccessible(true); + targetMethod.invoke(targetMethod, testObj); + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + fail(e.getMessage()); + } + } + + @Test + public void getKey() { + Class clazz = testObj.getClass(); + Method targetMethod; + Field testField; + + String result; + String expect = "Age"; + + try { + targetMethod = ObjectToJsonConverter.class.getDeclaredMethod("getKey", Field.class); + testField = clazz.getDeclaredField("age"); + targetMethod.setAccessible(true); + result = (String) targetMethod.invoke(targetMethod, testField); + + assertEquals(expect, result); + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchFieldException e) { + fail(e.getMessage()); + } + + } +} diff --git a/src/test/java/com/mc/main/advanced/annotations/casestudy/PersonUnitTEST.java b/src/test/java/com/mc/main/advanced/annotations/casestudy/PersonUnitTEST.java new file mode 100644 index 0000000..686dbe0 --- /dev/null +++ b/src/test/java/com/mc/main/advanced/annotations/casestudy/PersonUnitTEST.java @@ -0,0 +1,50 @@ +package com.mc.main.advanced.annotations.casestudy; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.google.protobuf.Field; + +public class PersonUnitTEST { + + private static Person testObj; + + @BeforeEach + public void testStart() { + testObj = new Person("Caspian", "Lovett", "28"); + } + + @Test + public void constructor() { + testObj = new Person(); + assertTrue(testObj instanceof Person); + } + + @Test + public void constructor1() { + assertTrue(testObj instanceof Person); + } + + @Test + public void initNames() { + Class clazz = testObj.getClass(); + Method targetMethod; + + try { + targetMethod = clazz.getDeclaredMethod("initNames"); + targetMethod.setAccessible(true); + targetMethod.invoke(testObj); + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + e.printStackTrace(); + fail(e.getMessage()); + } + + } + +} From 34504222b47f778b8dbdd6c5545c67ec24f5a74f Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Thu, 7 Apr 2022 00:34:22 +0100 Subject: [PATCH 19/23] AD: added Functional Interfaces practice --- src/main/java/com/mc/main/Application.java | 2 + src/main/java/com/mc/main/Runner.java | 2 +- .../mc/main/java8/FunctInterfacePractice.java | 65 +++++++++++++++++++ .../com/mc/main/java8/streams/Printable.java | 13 ++++ .../mc/main/java8/streams/PrintableParam.java | 11 ++++ 5 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/mc/main/java8/FunctInterfacePractice.java create mode 100644 src/main/java/com/mc/main/java8/streams/Printable.java create mode 100644 src/main/java/com/mc/main/java8/streams/PrintableParam.java diff --git a/src/main/java/com/mc/main/Application.java b/src/main/java/com/mc/main/Application.java index 13d64ea..c3b4622 100644 --- a/src/main/java/com/mc/main/Application.java +++ b/src/main/java/com/mc/main/Application.java @@ -9,6 +9,7 @@ import com.mc.main.advanced.ReflectionPractice; import com.mc.main.advanced.ThreadPractice; import com.mc.main.advanced.ui.ScannerPractice; +import com.mc.main.java8.FunctInterfacePractice; import com.mc.main.oop.ClassApp; import com.mc.main.oop.WrapperClass; import com.mc.main.oop.principles.AbstractionPractice; @@ -88,6 +89,7 @@ public void run() { break; case JAVA8: + FunctInterfacePractice.start(); break; default: diff --git a/src/main/java/com/mc/main/Runner.java b/src/main/java/com/mc/main/Runner.java index 95d8c6c..f069066 100644 --- a/src/main/java/com/mc/main/Runner.java +++ b/src/main/java/com/mc/main/Runner.java @@ -55,7 +55,7 @@ protected static void start() { Application.PROCEDURAL.run(); Application.OOP.run(); Application.ADVANCED.run(); - // Application.JAVA8.run(); + Application.JAVA8.run(); } } diff --git a/src/main/java/com/mc/main/java8/FunctInterfacePractice.java b/src/main/java/com/mc/main/java8/FunctInterfacePractice.java new file mode 100644 index 0000000..8506853 --- /dev/null +++ b/src/main/java/com/mc/main/java8/FunctInterfacePractice.java @@ -0,0 +1,65 @@ +package com.mc.main.java8; + +import com.mc.main.java8.streams.Printable; +import com.mc.main.java8.streams.PrintableParam; +import com.mc.main.util.Practice; + +public class FunctInterfacePractice extends Practice { + + private FunctInterfacePractice() { + super(); + } + + // Functional interfaces is an unusual example of java syntax + // that appears more akin to functional programming. + // + // When we declare a special interface type, we can use that type to store + // method implementation by promising to fulfil the action of the interface. + public static void start() { + printHeader("Functional Interfaces"); + + // Functional interfaces allow us to pass a method as an object + // we do not need to provide accessors, return types(as it's inferred by the compiler). + // + // To inform java that our implementation is a method that is part of a functional interface + // we provide the arrow "->" keyword. + // + // We can use the return keyword, but it isn't needed if it's a void returning method + // we can have multiple lines of instructions, but the instructions must be + // enclosed in scope in order to encapsulate the instructions. + + // The method implementation as an example of our functional interface + Printable printVoid = () -> System.out.println("result"); + + // We can pass our implementation of the functional interface as an + // object, allowing that method to take our implementation and handle/invoke + // our provided implementation. + print(printVoid); + + // Let's instead see an example of a method implementation that has a parameter + PrintableParam printParam = (y) -> { + // Our implementation here handles y as though a standard java object + // type limitation is provided by the outline of the interface method. + for(int i = 0 ; i < 5; i++) { + System.out.println("result: " + y); + } + }; // even though we have a scope, we need to terminate the variable initialisation process + + print(printParam); + } + + // A method that invokes our functional interface + // by extension, invoking the implementation of x + public static void print(Printable x){ + x.print(); // perform the functional interface method call + } + + // As we can only have one abstract method in a functional interface + // we need a separate method that expects to use that interface. + // + // because the interface outlines a parameter, we know that x.print can take in "Object x" + // which we can use here to append Cat to the method implementation. + public static void print(PrintableParam x){ + x.print("Cat"); // now this method can pass some parameter into the provided implementation + } +} diff --git a/src/main/java/com/mc/main/java8/streams/Printable.java b/src/main/java/com/mc/main/java8/streams/Printable.java new file mode 100644 index 0000000..aa487b2 --- /dev/null +++ b/src/main/java/com/mc/main/java8/streams/Printable.java @@ -0,0 +1,13 @@ +package com.mc.main.java8.streams; + +// Functional Interfaces allow us to use abstraction in a way to +// outline a method definition without our implementation +// +// However functional interfaces are only comprised of one abstract method: +// a "Single Abstract Method" Interface. +@FunctionalInterface +public interface Printable { + + public void print(); + +} diff --git a/src/main/java/com/mc/main/java8/streams/PrintableParam.java b/src/main/java/com/mc/main/java8/streams/PrintableParam.java new file mode 100644 index 0000000..df1dbd5 --- /dev/null +++ b/src/main/java/com/mc/main/java8/streams/PrintableParam.java @@ -0,0 +1,11 @@ +package com.mc.main.java8.streams; + +// Here we are providing the functional interface to expect some kind of Parameter +// the implementation provided should by extension, provide some kind of implementation +// for our x parameter in the body of implementation that is requested. +@FunctionalInterface +public interface PrintableParam { + + public void print(Object x); + +} From 0b3370a17c48e8e676e117398d60ebd574f8abf3 Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Thu, 7 Apr 2022 00:37:34 +0100 Subject: [PATCH 20/23] AD: added Streams practice --- .../com/mc/main/java8/StreamsPractice.java | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 src/main/java/com/mc/main/java8/StreamsPractice.java diff --git a/src/main/java/com/mc/main/java8/StreamsPractice.java b/src/main/java/com/mc/main/java8/StreamsPractice.java new file mode 100644 index 0000000..9ca5a66 --- /dev/null +++ b/src/main/java/com/mc/main/java8/StreamsPractice.java @@ -0,0 +1,160 @@ +package com.mc.main.java8; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collector; +import java.util.stream.Collectors; + +import com.mc.main.util.Practice; +import com.mc.main.util.ReportFormater; + +public class StreamsPractice extends Practice { + + private static final int[] even = {2, 4, 6, 8, 10, 12, 14}; + private static final int[] odd = {1, 3, 5, 7, 9, 11, 13}; + private static List evenList, oddList; + + private StreamsPractice() { + super(); + } + + public static void start() { + printHeader("Streams"); + + // let's first observe the challenges faced by standard arrays + // and how we can collect the results of streaming to convert + // a basic array into a streamable collection + listsToStreams(); + + // With our even and odd Collections ready for streaming, let's observe some + // other techniques that allow us to control and collect data based + // on a functional/lambda approach. + streamTechniques(); + + //TODO: Parallel/Concurrent streams + } + + private static void listsToStreams() { + // Streaming allows us to take advantage of writing functional statements + // using functional interfaces to create lambda styled expressions + // + // In particular, streaming typically works for any array of data, applying + // a predicate(method implementation as a parameter) to apply an expression to each + // item in the array. + // + // it's very similar to foreach, but the usage of lambda expressions refine this down + // to a singular statement; rather than excessive block scope. + // In fact Streams include a process for handling each object like a foreach! + + // Let's look at how we can convert a primitive array to a wrapper class array List! + collect(); + + ReportFormater.reportHeader("Stream().forEach"); + // Now that we've translated our primitive even and odd array to an auto-boxed collection + // we can stream the list, using a simple print method on the results. + System.out.print("ForEach even: "); + evenList.stream().forEach(x -> System.out.format("%s ",x)); + + System.out.print("\nForEach odd: "); + oddList.stream().forEach(x -> System.out.format("%s ",x)); + System.out.println(); + } + + private static void collect() { + // Here we will use the Arrays object to stream our primitive array + evenList = Arrays.stream(even).boxed().collect(Collectors.toList()); + oddList = Arrays.stream(odd).boxed().collect(Collectors.toList()); + // notice we then use the boxed method in order to apply a standard operation + // provided by the IntStream object in order to wrap the primitive value into + // it's - equivalent! + + // finally we use ".collect" to store each result and use the Collectors object + // to store our entries into a new list + + // As a result we've used streams to translate int[] into List + // this is quite useful as Streams can have some anomalous interactions with primitive arrays. + // Using a form of Collection type is going to provide more direct support. + } + + private static void streamTechniques() { + // We've already observed .stream, .forEach, ".collect()" & Collectors + // let's observe some other actions we can take when streaming through collections + // whilst applying predicates. + filter(); + map(); + sorted(); + reduce(); + } + + private static void map() { + ReportFormater.reportHeader("Stream().map()"); + // Map allows us to apply some kinda of implementation via predicate + // the end result is returned allowing us the capability of applying + // a standardised operation to each element of the collection. + + List squaredEventList; + + squaredEventList= evenList.stream().map(x -> x*x).collect(Collectors.toList()); + System.out.format("EvenList squared: %s\n", squaredEventList); + + squaredEventList = oddList.stream().map(x -> x*x).collect(Collectors.toList()); + System.out.format("OddList squared: %s\n", squaredEventList); + } + + private static void filter() { + ReportFormater.reportHeader("Stream().filter()"); + // Let's use filter to apply a check againest our data, passing the data to a collection if it meets + // out requirements based on a filter! + List divByThree; + + divByThree = oddList.stream().filter(x -> x % 3 == 0).collect(Collectors.toList()); + System.out.format("Odds divisible by Three: %s\n", divByThree); + + divByThree = evenList.stream().filter(x -> x % 3 == 0).collect(Collectors.toList()); + System.out.format("Evens divisible by Three: %s\n", divByThree); + } + + private static void forEach(Collection set) { + set.stream().forEach(x -> System.out.format("%s ", x)); + } + + private static void sorted() { + ReportFormater.reportHeader("Stream().sorted()"); + // Let's inverse the evens list, then have streams sort the collection back to ascending order! + List correctedList; + + Collections.reverse(evenList); + System.out.format("EvenList invert order: %s\n", evenList); + + correctedList = evenList.stream().sorted().collect(Collectors.toList()); + System.out.format("EvenList sorted order: %s\n", correctedList); + Collections.reverse(evenList); // let's reset this list now we've shown the sorting method. + } + + private static void reduce() { + ReportFormater.reportHeader("Stream().reduce()"); + // The focus of the reduce method depends on which overload of the method you access + // the general principle is that reduce will attempt to summarise the stream into a single value + // This is done by taking the result of the last iteration and factoring it into the current iteration + // + // let's add the total of all stored even numbers: + int result; + + System.out.println(evenList); + + // .reduce(accumulator); + // ======================================== + result = evenList.stream().reduce((a, b) -> a+b).orElseThrow(); + System.out.println("Total: " + result); + + // .reduce(identity, accumulator); + // ======================================== + // The next overload is an accumulator format of reduce. + // It uses the first parameter as a base value to build on, rather than assuming our first + // entry has nothing to pass through. + result = evenList.stream().reduce(result, (a,b) -> a+b); + System.out.println("Total accumalated: " + result); + } +} From 5caeaec3f7a96933cd21c4ea8d915acdb87c2d21 Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Thu, 7 Apr 2022 01:04:13 +0100 Subject: [PATCH 21/23] AD: added unit tests for Streams and Functional Interfaces Practice --- README.md | 24 ++++++------ src/main/java/com/mc/main/Runner.java | 35 +++++++++++++---- .../com/mc/main/java8/StreamsPractice.java | 4 -- src/test/java/com/mc/main/RunnerUnitTEST.java | 6 ++- .../java8/FunctInterfacePracticeUnitTEST.java | 38 +++++++++++++++++++ .../main/java8/StreamsPracticeUnitTEST.java | 17 +++++++++ 6 files changed, 100 insertions(+), 24 deletions(-) create mode 100644 src/test/java/com/mc/main/java8/FunctInterfacePracticeUnitTEST.java create mode 100644 src/test/java/com/mc/main/java8/StreamsPracticeUnitTEST.java diff --git a/README.md b/README.md index 02722e6..e3c96f1 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,12 @@ java techniques and keywords. Checkout the [feature-branch model here!](https://github.com/MorickClive/Vale-Java-Practice/network) +Code Coverage: 96.8% + ## ACTIVE PROJECT PHASE: VALE ### Topics to Cover: -The topics that'll be covered are: - [X] Java Basics: - [X] Variables - [X] Methods @@ -58,8 +59,6 @@ The topics that'll be covered are: ## Java Version Features -### ACTIVE PROJECT PHASE: VALE - - [ ] JAVA 4/5 - [X] For-Each loops - [X] VarArgs @@ -75,15 +74,16 @@ The topics that'll be covered are: - [ ] Type Inference - [ ] JAVA 8 (Projects: VALE, PASS): - - [ ] Functional Interface - - [ ] Streams/Lambdas - - [ ] Collectors - - [ ] map - - [ ] filter - - [ ] forEach - - [ ] sorted - - [ ] collect - - [ ] reduce + - [X] Functional Interface + - [X] Streams/Lambdas + - [x] box + - [X] collect + - [X] Collectors + - [X] forEach + - [X] map + - [X] filter + - [X] sorted + - [X] reduce - [ ] Date & Time - [ ] Base-64 en/decoding - [ ] Method references diff --git a/src/main/java/com/mc/main/Runner.java b/src/main/java/com/mc/main/Runner.java index f069066..f2763f2 100644 --- a/src/main/java/com/mc/main/Runner.java +++ b/src/main/java/com/mc/main/Runner.java @@ -29,7 +29,7 @@ public static void main(String[] args) { // Continued: try { - start(); + start(args); } finally { // SCANNER close method: // Clean up active resources at end of application @@ -46,16 +46,37 @@ public static void main(String[] args) { // ======================================== // Main Runner instructions // ======================================== - + private static final ApplicationTime time = new ApplicationTime(); + private static String paramDebug = "4"; - protected static void start() { + protected static void start(String[] args) { System.out.println(StringConst.DIV + "\n"); + + // If app has no parameters, provide param value + args = args == null || args.length < 1 ? new String[] {paramDebug} : args; - Application.PROCEDURAL.run(); - Application.OOP.run(); - Application.ADVANCED.run(); - Application.JAVA8.run(); + switch(args[0]) { + case "1" : + Application.PROCEDURAL.run(); + break; + case "2" : + Application.OOP.run(); + break; + case "3" : + Application.ADVANCED.run(); + break; + case "4" : + Application.JAVA8.run(); + break; + + default: + case "-a" : + Application.PROCEDURAL.run(); + Application.OOP.run(); + Application.ADVANCED.run(); + Application.JAVA8.run(); + } } } diff --git a/src/main/java/com/mc/main/java8/StreamsPractice.java b/src/main/java/com/mc/main/java8/StreamsPractice.java index 9ca5a66..42cf89b 100644 --- a/src/main/java/com/mc/main/java8/StreamsPractice.java +++ b/src/main/java/com/mc/main/java8/StreamsPractice.java @@ -116,10 +116,6 @@ private static void filter() { System.out.format("Evens divisible by Three: %s\n", divByThree); } - private static void forEach(Collection set) { - set.stream().forEach(x -> System.out.format("%s ", x)); - } - private static void sorted() { ReportFormater.reportHeader("Stream().sorted()"); // Let's inverse the evens list, then have streams sort the collection back to ascending order! diff --git a/src/test/java/com/mc/main/RunnerUnitTEST.java b/src/test/java/com/mc/main/RunnerUnitTEST.java index 4ee6af6..8917a77 100644 --- a/src/test/java/com/mc/main/RunnerUnitTEST.java +++ b/src/test/java/com/mc/main/RunnerUnitTEST.java @@ -7,9 +7,13 @@ public class RunnerUnitTEST { // in advanced testing you might mock this behaviour or simply test // that the application runs to completion; although this isn't always possible. + //TODO: Mock associated class calls. + // Additionally, mocking would be required in order to isolate code run by association + // as we are only concerned with code that occurs in Runner.main, nothing it calls. + @Test public void startTest() { - Runner.main(null); + Runner.main(new String[]{"-a"}); } } diff --git a/src/test/java/com/mc/main/java8/FunctInterfacePracticeUnitTEST.java b/src/test/java/com/mc/main/java8/FunctInterfacePracticeUnitTEST.java new file mode 100644 index 0000000..475df96 --- /dev/null +++ b/src/test/java/com/mc/main/java8/FunctInterfacePracticeUnitTEST.java @@ -0,0 +1,38 @@ +package com.mc.main.java8; + +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.Test; + +import com.mc.main.advanced.ReflectionPractice; + +public class FunctInterfacePracticeUnitTEST { + + @Test + public void funcInterStart() { + try { + FunctInterfacePractice.start(); + }catch (Exception e) { + fail(); + } + } + + @Test + public void print() { + try { + FunctInterfacePractice.print( () -> System.out.println("test")); + }catch (Exception e) { + fail(); + } + } + + @Test + public void printParam() { + try { + FunctInterfacePractice.print( (y) -> System.out.println("test - " + y)); + }catch (Exception e) { + fail(); + } + } + +} diff --git a/src/test/java/com/mc/main/java8/StreamsPracticeUnitTEST.java b/src/test/java/com/mc/main/java8/StreamsPracticeUnitTEST.java new file mode 100644 index 0000000..0ef5e98 --- /dev/null +++ b/src/test/java/com/mc/main/java8/StreamsPracticeUnitTEST.java @@ -0,0 +1,17 @@ +package com.mc.main.java8; + +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.Test; + +public class StreamsPracticeUnitTEST { + + @Test + public void streamsStart() { + try { + StreamsPractice.start(); + }catch (Exception e) { + fail(); + } + } +} From a3e193ff6975c27d1224e27324ab59ff880cc5ec Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Thu, 7 Apr 2022 21:52:37 +0100 Subject: [PATCH 22/23] AD: added JDBC practice with full DML CRUD functionality --- .gitignore | 3 +- src/main/java/com/mc/main/Application.java | 4 + src/main/java/com/mc/main/Runner.java | 2 + .../java/com/mc/main/java8/JDBCPractice.java | 118 ++++++++++++++++++ .../main/java8/jdbc/DatabaseConnection.java | 78 ++++++++++++ 5 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/mc/main/java8/JDBCPractice.java create mode 100644 src/main/java/com/mc/main/java8/jdbc/DatabaseConnection.java diff --git a/.gitignore b/.gitignore index 0b61641..29f7204 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target/ /.settings/ -/.classpath \ No newline at end of file +/.classpath +/src/main/resources \ No newline at end of file diff --git a/src/main/java/com/mc/main/Application.java b/src/main/java/com/mc/main/Application.java index c3b4622..68c4c37 100644 --- a/src/main/java/com/mc/main/Application.java +++ b/src/main/java/com/mc/main/Application.java @@ -10,6 +10,8 @@ import com.mc.main.advanced.ThreadPractice; import com.mc.main.advanced.ui.ScannerPractice; import com.mc.main.java8.FunctInterfacePractice; +import com.mc.main.java8.JDBCPractice; +import com.mc.main.java8.StreamsPractice; import com.mc.main.oop.ClassApp; import com.mc.main.oop.WrapperClass; import com.mc.main.oop.principles.AbstractionPractice; @@ -90,6 +92,8 @@ public void run() { case JAVA8: FunctInterfacePractice.start(); + StreamsPractice.start(); + JDBCPractice.start(); break; default: diff --git a/src/main/java/com/mc/main/Runner.java b/src/main/java/com/mc/main/Runner.java index f2763f2..3f3d05e 100644 --- a/src/main/java/com/mc/main/Runner.java +++ b/src/main/java/com/mc/main/Runner.java @@ -1,6 +1,7 @@ package com.mc.main; import com.mc.main.advanced.ui.scanner.Console; +import com.mc.main.java8.jdbc.DatabaseConnection; import com.mc.main.util.ApplicationTime; import com.mc.main.util.StringConst; @@ -36,6 +37,7 @@ public static void main(String[] args) { // finally allows closure post-exception encounter - ensuring closure in most // if not all error cases Console.close(); + DatabaseConnection.close(); // Let's return to Console.input()! System.out.println("Application ENDS"); diff --git a/src/main/java/com/mc/main/java8/JDBCPractice.java b/src/main/java/com/mc/main/java8/JDBCPractice.java new file mode 100644 index 0000000..5d1cd98 --- /dev/null +++ b/src/main/java/com/mc/main/java8/JDBCPractice.java @@ -0,0 +1,118 @@ +package com.mc.main.java8; + +import java.io.File; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import com.mc.main.java8.jdbc.DatabaseConnection; +import com.mc.main.util.Practice; +import com.mc.main.util.ReportFormater; + +public class JDBCPractice extends Practice { + + private JDBCPractice() { + super(); + } + + public static void start() { + String relativePath = new File("").getAbsolutePath(); + File JDBCprops = new File(relativePath + "/src/main/resources/db.properties"); + + if (JDBCprops.exists()) { + printHeader("JDBC"); + System.out.println(); + + // In order to work with SQL databases, we need a new Library to assist us: + // mysql + // mysql-connector-java + // + // This will give us access to the Connection and DriverManager classes - + // these classes will allow us to establish a resource connection. + // + // Let's review our DatabaseConnection class to see how we can manage our connection + // Separately from our queries. + Connection connection = DatabaseConnection.getConnection(); + + // Our Connection allows us to create statements that allow us to execute + // SQL queries. + // + // When we expect a result from our query, it maybe in a table format + // the ResultSet class acts like an Iterator Pattern object + // so that we might access each row via a iteration cycle of that returned result. + Statement statement; + ResultSet rs; + + try { + // First let's create a statement object based on our active connection + // this can fail - so it requires a try block to capture exceptions. + // + // Consider that our connection might be fine - but if the SQL server or it's hardware + // platform fails, we might get exceptions from our requests to a faulty host. + statement = connection.createStatement(); + + // Observer the below, review how we can execute queries, but also + // review our data based on ResultSets that are returned to us. + + // DDL + // ======================================== + // DELETE + statement.execute("DROP TABLE IF EXISTS example"); + // CREATE + statement.execute("CREATE TABLE example(ID varchar(255), Name varchar(255))"); + + // DML + // ======================================== + ReportFormater.reportHeader("\tREAD + CREATE:"); + // CREATE + statement.execute("INSERT INTO example VALUES (\"ONE\", \"Word\")"); + statement.execute("INSERT INTO example VALUES (\"TWO\", \"Other Word\")"); + + // READ + rs = statement.executeQuery("SELECT * FROM example;"); + readExample(rs); + + // Update + System.out.println(); + ReportFormater.reportHeader("\tUPDATE:"); + statement.execute("UPDATE example SET ID = 'THREE', Name = 'New' WHERE ID = \"ONE\""); + + rs = statement.executeQuery("SELECT * FROM example"); + readExample(rs); + + // DELETE + System.out.println(); + ReportFormater.reportHeader("\tDELETE:"); + statement.execute("DELETE FROM example WHERE ID='TWO'"); + + rs = statement.executeQuery("SELECT * FROM example"); + readExample(rs); + + // Clear Table + statement.execute("DROP TABLE IF EXISTS example"); + System.out.println(); + + // Ideally we'd close our connection here, our connection has done its job and should be freed + // up for other consumers of the database. + // + // Connections can autoclose - but it's best practice to close our resources + // when we are finished with them. It promotes security, application liveliness and + // shared resource access! + + // In this application, the singular connection is release at the end of the + // Runner.main method in the same fashion as our inputstream from our Console! + + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + + private static void readExample(ResultSet rs) throws SQLException { + while (rs.next()) { + System.out.format("ID: %s, Name: %s\n", rs.getString(1), rs.getString(2)); + } + } + +} diff --git a/src/main/java/com/mc/main/java8/jdbc/DatabaseConnection.java b/src/main/java/com/mc/main/java8/jdbc/DatabaseConnection.java new file mode 100644 index 0000000..c02255d --- /dev/null +++ b/src/main/java/com/mc/main/java8/jdbc/DatabaseConnection.java @@ -0,0 +1,78 @@ +package com.mc.main.java8.jdbc; + +import java.io.IOException; +import java.io.InputStream; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Properties; + +// In order to work our database, we need some form of stable connection +// to our SQL database server. +// +// We'll use DatabaseConnection as a means to store this connection and credentials required to +// access the database +public class DatabaseConnection { + + private static Connection activeConnection; + private static String URL; + private static String username; + private static String password; + + // For this example, we'll only ever have one connection active at a time + // we won't want more than one instance able to connect at a given time. + private DatabaseConnection() { + super(); + } + + // This will be our means of acquiring a connection instance + // it'll handle setup requirements and provide us with either an existing connection + // or initialise a new connection. + public static Connection getConnection() { + if (activeConnection == null) { + initConnection(); + } + return activeConnection; + } + + // As part of creating a connection instance, we need to use a DriverManager class + // with the right address and credentials in order to establish a connection. + private static void initConnection() { + try { + readProperties(); + activeConnection = DriverManager.getConnection(URL, username, password); + } catch (SQLException | IOException e) { + e.printStackTrace(); + } + } + + // It's a bad idea to hard code sensitive data into our applications + // for this reason, the Database Connection class reads user credentials from a properties. + // + // We can use a mention of the respective resource folder to avoid uploading these resources to + // our public repositories. + public static void readProperties() throws IOException { + Properties props = new Properties(); + InputStream is = DatabaseConnection.class.getClassLoader().getResourceAsStream("db.properties"); + + props.load(is); + + URL = props.get("URL") + "?serverTimezone=UTC"; + username = (String) props.get("Username"); + password = (String) props.get("Password"); + } + + // Like any resource, we want to close our connection to ensure the database isn't waiting + // for a close signal; preventing misuse of our database connection and improving + // liveliness of our database access. + public static void close() { + try { + if (activeConnection != null) { + activeConnection.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + }; + +} From 04104e92bf9e348917ba44e5ee612c725ea2bba5 Mon Sep 17 00:00:00 2001 From: Alex Davies Date: Fri, 8 Apr 2022 18:52:39 +0100 Subject: [PATCH 23/23] AD: added code coverage test for JDBC implementation, revisit required --- README.md | 8 +- pom.xml | 23 +++++- src/main/java/com/mc/main/Application.java | 3 +- src/main/java/com/mc/main/Runner.java | 9 ++- .../java/com/mc/main/java8/JDBCPractice.java | 6 +- .../main/java8/jdbc/DatabaseConnection.java | 7 +- src/test/java/com/mc/main/RunnerUnitTEST.java | 10 +-- .../java8/FunctInterfacePracticeUnitTEST.java | 2 - .../mc/main/java8/JDBCPracticeUnitTEST.java | 79 +++++++++++++++++++ .../jdbc/DatabaseConnectionUnitTest.java | 74 +++++++++++++++++ 10 files changed, 199 insertions(+), 22 deletions(-) create mode 100644 src/test/java/com/mc/main/java8/JDBCPracticeUnitTEST.java create mode 100644 src/test/java/com/mc/main/java8/jdbc/DatabaseConnectionUnitTest.java diff --git a/README.md b/README.md index e3c96f1..210b5f5 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ java techniques and keywords. Checkout the [feature-branch model here!](https://github.com/MorickClive/Vale-Java-Practice/network) -Code Coverage: 96.8% +Code Coverage: 96.6% ## ACTIVE PROJECT PHASE: VALE @@ -59,10 +59,10 @@ Code Coverage: 96.8% ## Java Version Features -- [ ] JAVA 4/5 +- [X] JAVA 4/5 - [X] For-Each loops - [X] VarArgs - - [ ] Static import + - [X] Static import - [X] Enumeration - [X] AutoBoxing (Wrapper Classes, type inference) - [X] Annotations @@ -88,7 +88,7 @@ Code Coverage: 96.8% - [ ] Base-64 en/decoding - [ ] Method references - [ ] Default methods - - [ ] JDBC + - [X] JDBC

Java 9 (Projects:STRONGHOLD, BASTION) diff --git a/pom.xml b/pom.xml index 767093f..cf42039 100644 --- a/pom.xml +++ b/pom.xml @@ -12,13 +12,32 @@ - + mysql mysql-connector-java 8.0.28 - + + + org.junit.jupiter + junit-jupiter-engine + 5.8.1 + test + + + org.mockito + mockito-core + 2.21.0 + test + + + org.mockito + mockito-junit-jupiter + 2.23.0 + test + + org.junit.jupiter junit-jupiter diff --git a/src/main/java/com/mc/main/Application.java b/src/main/java/com/mc/main/Application.java index 68c4c37..b510b02 100644 --- a/src/main/java/com/mc/main/Application.java +++ b/src/main/java/com/mc/main/Application.java @@ -12,6 +12,7 @@ import com.mc.main.java8.FunctInterfacePractice; import com.mc.main.java8.JDBCPractice; import com.mc.main.java8.StreamsPractice; +import com.mc.main.java8.jdbc.DatabaseConnection; import com.mc.main.oop.ClassApp; import com.mc.main.oop.WrapperClass; import com.mc.main.oop.principles.AbstractionPractice; @@ -93,7 +94,7 @@ public void run() { case JAVA8: FunctInterfacePractice.start(); StreamsPractice.start(); - JDBCPractice.start(); + JDBCPractice.start(DatabaseConnection.getConnection()); break; default: diff --git a/src/main/java/com/mc/main/Runner.java b/src/main/java/com/mc/main/Runner.java index 3f3d05e..c4614f0 100644 --- a/src/main/java/com/mc/main/Runner.java +++ b/src/main/java/com/mc/main/Runner.java @@ -51,6 +51,7 @@ public static void main(String[] args) { private static final ApplicationTime time = new ApplicationTime(); private static String paramDebug = "4"; + private static boolean test = false; protected static void start(String[] args) { System.out.println(StringConst.DIV + "\n"); @@ -69,7 +70,7 @@ protected static void start(String[] args) { Application.ADVANCED.run(); break; case "4" : - Application.JAVA8.run(); + if(!test) { Application.JAVA8.run(); } break; default: @@ -77,8 +78,12 @@ protected static void start(String[] args) { Application.PROCEDURAL.run(); Application.OOP.run(); Application.ADVANCED.run(); - Application.JAVA8.run(); + if(!test) {Application.JAVA8.run();} } } + + public static void setTestMode() { + test = true; + } } diff --git a/src/main/java/com/mc/main/java8/JDBCPractice.java b/src/main/java/com/mc/main/java8/JDBCPractice.java index 5d1cd98..bca8f78 100644 --- a/src/main/java/com/mc/main/java8/JDBCPractice.java +++ b/src/main/java/com/mc/main/java8/JDBCPractice.java @@ -11,12 +11,14 @@ import com.mc.main.util.ReportFormater; public class JDBCPractice extends Practice { + + private static Connection connection; private JDBCPractice() { super(); } - public static void start() { + public static void start(Connection connection) { String relativePath = new File("").getAbsolutePath(); File JDBCprops = new File(relativePath + "/src/main/resources/db.properties"); @@ -33,7 +35,7 @@ public static void start() { // // Let's review our DatabaseConnection class to see how we can manage our connection // Separately from our queries. - Connection connection = DatabaseConnection.getConnection(); + // Our Connection allows us to create statements that allow us to execute // SQL queries. diff --git a/src/main/java/com/mc/main/java8/jdbc/DatabaseConnection.java b/src/main/java/com/mc/main/java8/jdbc/DatabaseConnection.java index c02255d..c42d849 100644 --- a/src/main/java/com/mc/main/java8/jdbc/DatabaseConnection.java +++ b/src/main/java/com/mc/main/java8/jdbc/DatabaseConnection.java @@ -19,10 +19,9 @@ public class DatabaseConnection { private static String username; private static String password; - // For this example, we'll only ever have one connection active at a time - // we won't want more than one instance able to connect at a given time. - private DatabaseConnection() { + public DatabaseConnection(Connection activeConnection) { super(); + this.activeConnection = activeConnection; } // This will be our means of acquiring a connection instance @@ -51,7 +50,7 @@ private static void initConnection() { // // We can use a mention of the respective resource folder to avoid uploading these resources to // our public repositories. - public static void readProperties() throws IOException { + private static void readProperties() throws IOException { Properties props = new Properties(); InputStream is = DatabaseConnection.class.getClassLoader().getResourceAsStream("db.properties"); diff --git a/src/test/java/com/mc/main/RunnerUnitTEST.java b/src/test/java/com/mc/main/RunnerUnitTEST.java index 8917a77..f2cf4ce 100644 --- a/src/test/java/com/mc/main/RunnerUnitTEST.java +++ b/src/test/java/com/mc/main/RunnerUnitTEST.java @@ -6,15 +6,15 @@ public class RunnerUnitTEST { // Typically app Runners don't contribute a meaningful unit test // in advanced testing you might mock this behaviour or simply test // that the application runs to completion; although this isn't always possible. - - //TODO: Mock associated class calls. - // Additionally, mocking would be required in order to isolate code run by association - // as we are only concerned with code that occurs in Runner.main, nothing it calls. + + //TODO: Implement written code test. + // mocking static methods from an enum won't do, code refactor will need review. @Test public void startTest() { + Runner.setTestMode(); Runner.main(new String[]{"-a"}); } - + } \ No newline at end of file diff --git a/src/test/java/com/mc/main/java8/FunctInterfacePracticeUnitTEST.java b/src/test/java/com/mc/main/java8/FunctInterfacePracticeUnitTEST.java index 475df96..f05598f 100644 --- a/src/test/java/com/mc/main/java8/FunctInterfacePracticeUnitTEST.java +++ b/src/test/java/com/mc/main/java8/FunctInterfacePracticeUnitTEST.java @@ -4,8 +4,6 @@ import org.junit.jupiter.api.Test; -import com.mc.main.advanced.ReflectionPractice; - public class FunctInterfacePracticeUnitTEST { @Test diff --git a/src/test/java/com/mc/main/java8/JDBCPracticeUnitTEST.java b/src/test/java/com/mc/main/java8/JDBCPracticeUnitTEST.java new file mode 100644 index 0000000..25990fb --- /dev/null +++ b/src/test/java/com/mc/main/java8/JDBCPracticeUnitTEST.java @@ -0,0 +1,79 @@ +package com.mc.main.java8; + +import static org.junit.jupiter.api.Assertions.fail; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class JDBCPracticeUnitTEST { + + @Mock + private static Connection connection; + @Mock + private static Statement statement; + @Mock + private static ResultSet rs; + + @Test + public void start() throws SQLException { + // Mocks + Mockito.when(connection.createStatement()).thenReturn(statement); + Mockito.when(statement.executeQuery(Mockito.anyString())).thenReturn(rs); + + try { + JDBCPractice.start(connection); + }catch (Exception e) { + fail(); + } + + Mockito.verify(connection, Mockito.times(1)).createStatement(); + Mockito.verify(statement, Mockito.times(3)).executeQuery(Mockito.anyString()); + } + + @Test + public void startFailure1() throws SQLException { + // Mocks + Mockito.when(connection.createStatement()).thenReturn(statement); + Mockito.when(statement.executeQuery(Mockito.anyString())).thenThrow(SQLException.class); + + try { + JDBCPractice.start(connection); + }catch (Exception e) { + fail(); + } + + Mockito.verify(connection, Mockito.times(1)).createStatement(); + Mockito.verify(statement, Mockito.times(1)).executeQuery(Mockito.anyString()); + } + + @Test + public void readExample() throws SQLException, NoSuchMethodException, SecurityException { + // Mocks + Mockito.when(rs.next()).thenReturn(true, false); + Mockito.when(rs.getString(Mockito.anyInt())).thenReturn("Mock"); + + Method testMethod = JDBCPractice.class.getDeclaredMethod("readExample", ResultSet.class); + testMethod.setAccessible(true); + + try { + testMethod.invoke(testMethod, rs); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + fail(); + } + Mockito.verify(rs, Mockito.times(2)).next(); + Mockito.verify(rs, Mockito.times(2)).getString(Mockito.anyInt()); + } + + +} diff --git a/src/test/java/com/mc/main/java8/jdbc/DatabaseConnectionUnitTest.java b/src/test/java/com/mc/main/java8/jdbc/DatabaseConnectionUnitTest.java new file mode 100644 index 0000000..ea2e395 --- /dev/null +++ b/src/test/java/com/mc/main/java8/jdbc/DatabaseConnectionUnitTest.java @@ -0,0 +1,74 @@ +package com.mc.main.java8.jdbc; + +import static org.junit.jupiter.api.Assertions.fail; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.Connection; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class DatabaseConnectionUnitTest { + + @Mock + private static Connection testConnection; + + private static DatabaseConnection testDBConnection; + + @BeforeEach + public void startTest() { + testDBConnection = new DatabaseConnection(testConnection); + } + + @Test + public void getConnection() { + Connection connection = testDBConnection.getConnection(); + } + + @Test + public void initConnection() { + // This method won't access a database, this is to be expected as we aren't spinning up + // a test database as part of the call. + //TODO: add test environment for testing purposes. + Method testMethod; + + try { + testMethod = DatabaseConnection.class.getDeclaredMethod("initConnection"); + testMethod.setAccessible(true); + + testMethod.invoke(testMethod, null); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { + fail(); + } + } + + @Test + public void readProperties() { + Method testMethod; + + try { + testMethod = DatabaseConnection.class.getDeclaredMethod("readProperties"); + testMethod.setAccessible(true); + + testMethod.invoke(testMethod, null); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { + fail(); + } + } + + @Test + public void close() { + testDBConnection.close(); + } + + @AfterEach + public void cleanUp() { + testDBConnection.close(); + } +}