Skip to content

Added java assignment for java berlin clock, with build fixes and exception handling #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
### Gradle files ###
.gradle/
build/
!gradle-wrapper.jar
!gradle-wrapper.properties

### IntelliJ IDEA files ###
.idea/
*.iml
*.iws
*.ipr

### OS-specific files ###
.DS_Store
Thumbs.db

### Log files ###
*.log

### Temporary files ###
*.swp
*.swo
*.bak

### Gradle Wrapper ###
gradle-wrapper.jar
gradle-wrapper.properties

### Compiled class files ###
*.class

### JetBrains Rider ###
.idea/.idea_modules/

### IntelliJ project files ###
out/
22 changes: 12 additions & 10 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -7,13 +7,12 @@ apply plugin: 'eclipse'
version = '1.0-SNAPSHOT'
group = 'org.suggs.interviews.berlinclock'

task wrapper(type: Wrapper){
wrapper {
description = 'Generates gradlew scripts for NIX and win envs'
gradleVersion = '2.0'
gradleVersion = '7.3'
}

repositories {
jcenter()
mavenCentral()
}

@@ -22,15 +21,18 @@ idea.module {
}

dependencies {
compile 'org.slf4j:slf4j-api:1.7.5',
implementation 'org.slf4j:slf4j-api:1.7.5',
'commons-lang:commons-lang:2.6'

runtime 'org.slf4j:slf4j-log4j12:1.7.5',
implementation 'org.slf4j:slf4j-log4j12:1.7.5',
'log4j:log4j:1.2.17'

testCompile 'junit:junit:4.11',
'org.mockito:mockito-core:1.9.5',
'org.assertj:assertj-core:1.6.1',
'commons-io:commons-io:2.4',
'org.jbehave:jbehave-core:3.8'
testImplementation 'org.mockito:mockito-core:5.15.2',
'org.assertj:assertj-core:3.27.3',
'commons-io:commons-io:2.18.0',
'org.jbehave:jbehave-core:5.2.0'
// Jupiter api and engine dependencies
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.3'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.3'

}
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
12 changes: 6 additions & 6 deletions instructions/build.gradle
Original file line number Diff line number Diff line change
@@ -7,13 +7,13 @@ apply plugin: 'eclipse'
version = '1.0-SNAPSHOT'
group = 'org.suggs.interviews.example'

task wrapper(type: Wrapper){
wrapper {
description = 'Generates gradlew scripts for NIX and win envs'
gradleVersion = '2.0'
gradleVersion = '7.3'
}

repositories {
jcenter()
mavenCentral()
mavenLocal()
}

@@ -22,10 +22,10 @@ idea.module {
}

dependencies {
compile 'org.slf4j:slf4j-api:1.7.5'
implementation 'org.slf4j:slf4j-api:1.7.5'

runtime 'org.slf4j:slf4j-log4j12:1.7.5',
implementation 'org.slf4j:slf4j-log4j12:1.7.5',
'log4j:log4j:1.2.17'

testCompile 'junit:junit:4.11'
testImplementation 'junit:junit:4.11'
}
2 changes: 1 addition & 1 deletion instructions/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
2 changes: 1 addition & 1 deletion instructions/src/test/resources/log4j.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">

<log4j:configuration>

89 changes: 89 additions & 0 deletions src/main/java/com/ubs/opsit/interviews/BerlinClock.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.ubs.opsit.interviews;

import java.util.Objects;

/**
* @author Pankaj
*/
public class BerlinClock implements TimeConverter {
@Override
public String convertTime(String time) {
if (time == null || time.isEmpty()) {
throw new IllegalArgumentException("Invalid time format");
}

// Handle invalid time format
String[] values = time.split(":");
if (values.length != 3) {
throw new IllegalArgumentException("Invalid time format");
}

try {
int hours = Integer.parseInt(values[0]);
int minutes = Integer.parseInt(values[1]);
int seconds = Integer.parseInt(values[2]);

// Validate hours, minutes, and seconds range
if (hours < 0 || hours > 24 || minutes < 0 || minutes > 59 || seconds < 0 || seconds > 59) {
throw new IllegalArgumentException("Invalid time format");
}

// If hours == 24, check if minutes and seconds are 00
if (hours == 24 && (minutes != 0 || seconds != 0)) {
throw new IllegalArgumentException("Invalid time format");
}

// Construct the Berlin Clock string
return getLampOnOff(seconds) + " " + getHours(hours) + " " + getMinutes(minutes);

} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid time format");
}
}


/**
* Every 2 seconds lamp 1st row blinks on/off
* @param seconds
* @return
*/
protected String getLampOnOff(int seconds) {
return seconds % 2 == 0 ? LampSymbol.Y.name() : LampSymbol.O.name();
}

protected String getHours(int hours) {
int numberTopHourLamps = hours / 5;
int numberBottomHourLamps = hours % 5;

return getLampRow(4, numberTopHourLamps, LampSymbol.R) + " " + getLampRow(4, numberBottomHourLamps, LampSymbol.R);
}

protected String getMinutes(int minutes) {
int numberTopMinutesLamps = minutes / 5;
int numberBottomMinutesLamps = minutes % 5;

StringBuilder sb = new StringBuilder();

for (int i = 1; i <= 11; i++) {
sb.append(i <= numberTopMinutesLamps ? getMinuteLampColour(i) : LampSymbol.O.name());
}

sb.append(" ");

sb.append(getLampRow(4, numberBottomMinutesLamps, LampSymbol.Y));

return sb.toString();
}

private String getLampRow(int totalNumberLamps, int numberLampsOn, LampSymbol lampSymbol) {
StringBuilder sb = new StringBuilder(totalNumberLamps);
for (int i = 0; i < totalNumberLamps; i++) {
sb.append(i < numberLampsOn ? lampSymbol : LampSymbol.O.name());
}
return sb.toString();
}

private String getMinuteLampColour(int index) {
return index % 3 == 0 ? LampSymbol.R.name() : LampSymbol.Y.name();
}
}
13 changes: 13 additions & 0 deletions src/main/java/com/ubs/opsit/interviews/BerlinClockMain.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.ubs.opsit.interviews;

public class BerlinClockMain {

public static void main(String[] args) {
BerlinClock clock = new BerlinClock();
//clock.convertTime("");
//clock.convertTime("23.59.59");
String result = clock.convertTime("23:59:59");
System.out.println(result);

}
}
10 changes: 10 additions & 0 deletions src/main/java/com/ubs/opsit/interviews/LampSymbol.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.ubs.opsit.interviews;

/**
* @author Pankaj
*/
public enum LampSymbol {
O,
Y,
R;
}
25 changes: 20 additions & 5 deletions src/test/java/com/ubs/opsit/interviews/BerlinClockFixture.java
Original file line number Diff line number Diff line change
@@ -2,25 +2,25 @@

import org.jbehave.core.annotations.Then;
import org.jbehave.core.annotations.When;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import static com.ubs.opsit.interviews.support.BehaviouralTestEmbedder.aBehaviouralTestRunner;
import static org.assertj.core.api.Assertions.assertThat;

/**
* Acceptance test class that uses the JBehave (Gerkin) syntax for writing stories. You should not need to
* edit this class to complete the exercise, this is your definition of done.
*/
public class BerlinClockFixture {

private TimeConverter berlinClock;
private TimeConverter berlinClock = new BerlinClock(); // Initialize BerlinClock
private String theTime;

@Test
public void berlinClockAcceptanceTests() throws Exception {
aBehaviouralTestRunner()
.usingStepsFrom(this)
.withStory("berlin-clock.story")
.withStory("berlin-clock.story") // The story file that contains scenarios
.run();
}

@@ -31,6 +31,21 @@ public void whenTheTimeIs(String time) {

@Then("the clock should look like $")
public void thenTheClockShouldLookLike(String theExpectedBerlinClockOutput) {
assertThat(berlinClock.convertTime(theTime)).isEqualTo(theExpectedBerlinClockOutput);
try {
String result = berlinClock.convertTime(theTime);
Assertions.assertEquals(theExpectedBerlinClockOutput, result);
} catch (IllegalArgumentException e) {
Assertions.assertEquals("Invalid time format", e.getMessage());
}
}

@Then("the clock should throw an error $")
public void thenTheClockShouldThrowAnError(String expectedError) {
try {
berlinClock.convertTime(theTime);
Assertions.fail("Expected error not thrown");
} catch (IllegalArgumentException e) {
Assertions.assertEquals(expectedError, e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -9,13 +9,13 @@
import org.jbehave.core.steps.InjectableStepsFactory;
import org.jbehave.core.steps.InstanceStepsFactory;
import org.jbehave.core.steps.ParameterConverters;
import org.junit.jupiter.api.Assertions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.SimpleDateFormat;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.jbehave.core.io.CodeLocations.codeLocationFromClass;
import static org.jbehave.core.reporters.Format.CONSOLE;
import static org.jbehave.core.reporters.Format.HTML;
@@ -42,7 +42,7 @@ public static BehaviouralTestEmbedder aBehaviouralTestRunner() {
}

@Override
public void run() throws Exception {
public void run() {
List<String> paths = createStoryPaths();
if (paths == null || paths.isEmpty()) {
throw new IllegalStateException("No story paths found for state machine");
@@ -53,7 +53,7 @@ public void run() throws Exception {

@Override
public InjectableStepsFactory stepsFactory() {
assertThat(stepsFactory).isNotNull();
Assertions.assertNotNull(stepsFactory);
return stepsFactory;
}

@@ -74,7 +74,7 @@ public BehaviouralTestEmbedder withStory(String aWildcardStoryFilename) {
}

public BehaviouralTestEmbedder usingStepsFrom(Object... stepsSource) {
assertThat(stepsFactory).isNull();
Assertions.assertNull(stepsFactory);
stepsFactory = new InstanceStepsFactory(configuration(), stepsSource);
return this;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.ubs.opsit.interviews.support;

import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.apache.commons.io.filefilter.RegexFileFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@@ -23,27 +23,27 @@ public final class ClasspathStoryFinder {
private static final Logger LOG = LoggerFactory.getLogger(ClasspathStoryFinder.class);

public static List<String> findFilenamesThatMatch(String aFilenameWithWildcards) {
List<String> filenames = new ArrayList<String>();
List<String> filenames = new ArrayList<>();
for (File file : findFilesThatMatch(aFilenameWithWildcards)) {
filenames.add(file.toURI().toString());
}
return filenames;
}

private static Collection<File> findFilesThatMatch(String aFilenameWithWildcards) {
WildcardFileFilter regexFileFilter = new WildcardFileFilter(aFilenameWithWildcards);
RegexFileFilter regexFileFilter = new RegexFileFilter(aFilenameWithWildcards);
List<File> rootDirsToSearchFrom = getRootDirs();
LOG.info("Searching for stories called [{}] in [{}]", aFilenameWithWildcards, rootDirsToSearchFrom);

List<File> ret = new ArrayList<File>() ;
List<File> ret = new ArrayList<>() ;
for (File f : rootDirsToSearchFrom) {
ret.addAll(listFiles(f, regexFileFilter, DirectoryFileFilter.DIRECTORY)) ;
}
return ret ;
}

private static List<File> getRootDirs() {
List<File> ret = new ArrayList<File>() ;
List<File> ret = new ArrayList<>() ;
try {
Enumeration<URL> roots = ClasspathStoryFinder.class.getClassLoader().getResources("") ;
while(roots.hasMoreElements()) {
2 changes: 1 addition & 1 deletion src/test/resources/log4j.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">

<log4j:configuration>

28 changes: 28 additions & 0 deletions src/test/resources/stories/berlin-clock.story
Original file line number Diff line number Diff line change
@@ -44,5 +44,33 @@ RRRR
OOOOOOOOOOO
OOOO

Scenario: Null time
When the time is null
Then the clock should throw an error "Invalid time format"

Scenario: Empty time value
When the time is ""
Then the clock should throw an error "Invalid time format"

Scenario: Invalid time format (with dots instead of colons)
When the time is 23.34.34
Then the clock should throw an error "Invalid time format"

Scenario: Invalid hour value (greater than 24)
When the time is 25:30:45
Then the clock should throw an error "Invalid time format"

Scenario: Invalid minute value (greater than 59)
When the time is 12:60:30
Then the clock should throw an error "Invalid time format"

Scenario: Invalid second value (greater than 59)
When the time is 12:30:60
Then the clock should throw an error "Invalid time format"

Scenario: Invalid time format (non-numeric value)
When the time is "abc:def:ghi"
Then the clock should throw an error "Invalid time format"