diff --git a/.gitignore b/.gitignore index 34a2c68b..5392375c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,19 @@ *.class -bin/* -.metadata */bin/* +.metadata +bin/* +lib/build +lib/out koans/data koans/data* +.classpath +.project +.settings +koans/app/data +lib/file-compiler/bin +**/file_hashes.dat +.gradle +*.iml +*.ipr +*.iws +.idea diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..c0eb7f08 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,17 @@ +sudo: required + +language: ruby + +services: + - docker + +before_install: + - docker build -t java/koans docker + +script: + - docker run java/koans /bin/bash -c "git clone https://github.com/matyb/java-koans && gradle -p java-koans/lib buildApp" + +cache: + directories: + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ diff --git a/README b/README deleted file mode 100755 index e3f646d9..00000000 --- a/README +++ /dev/null @@ -1,10 +0,0 @@ -Running Instructions: -1. Download and unarchive the contents of the most recent java-koans in development from: - https://github.com/matyb/java-koans/archives/master -2. Open a terminal and cd to the 'koans/src' directory from within the archive -3. Run run.bat or run.sh (whichever is applicable for your OS) - -Developing a Koan: -1. Follow any of the existing koans as an example to create a new class w/ koan methods (indicated by the @Koan annotation) -2. Define the order and metadata associated with each koan in the PathToEnlightment.xml -3. If necessary - use dynamic content in your lesson, examples are located in XmlVariableInjector class (and Test) and the AboutKoans.java file diff --git a/README.md b/README.md new file mode 100644 index 00000000..a0519e70 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# Java Koans + +[![Build Status](https://travis-ci.org/matyb/java-koans.png?branch=master)](https://travis-ci.org/matyb/java-koans) + +Running Instructions: +===================== +* Download and unarchive the contents of the most recent java-koans in development from: +https://github.com/matyb/java-koans/archive/master.zip +* Open a terminal and cd to the directory you unarchived: +```cd ``` +* Within it you'll find: + * *koans*: this directory contains the application and its lessons, it is all that is needed to advance through the koans themselves and **it can be distributed independently** + * *lib*: this directory contains the code the koans engine is comprised of and built with + * *gradle*: wrapper for build library used to build koans source, setup project files in eclipse/idea, run tests, etc. you probably don't need to touch anything in here +* Change directory to the koans directory: ```cd koans``` +* If you are using windows enter: ```run.bat``` or ```./run.sh``` if you are using Mac or Linux + +Developing a Koan: +================== +* Follow any of the existing koans as an example to create a new class with koan methods (indicated by the @Koan annotation, they're public and specify no arguments) +* Define the order the koan suite (if it's new) will run in the koans/app/config/PathToEnlightenment.xml file +* [Override the lesson text](https://github.com/matyb/java-koans/blob/master/koans/app/config/i18n/messages_en.properties#L1) when it fails (default is expected 'X' found 'Y') +* Optionally you may use dynamic content in your lesson, examples are located in the XmlVariableInjector class (and Test) and the AboutKoans.java file + +Something's wrong: +================== +* If the koans app is constantly timing out compiling a koan, your computer may be too slow to compile the koan classes with the default timeout value. Update the compile_timeout_in_ms property in koans/app/config/config.properties with a larger value and try again. diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 00000000..f7c564aa --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,18 @@ +FROM centos:7 +MAINTAINER Mat Bentley + +ENV JAVA_VERSION 1.8.0 +ENV GRADLE_VERSION 3.4.1 + +# install wget, git, curl, jdk, which +RUN yum remove -y java &&\ + yum install -y wget git curl unzip java-$JAVA_VERSION-openjdk-devel which + +# install gradle +RUN wget https://services.gradle.org/distributions/gradle-$GRADLE_VERSION-bin.zip &&\ + mkdir -p /etc/alternatives/gradle &&\ + unzip -d /etc/alternatives/gradle gradle-$GRADLE_VERSION-bin.zip &&\ + ln -s /etc/alternatives/gradle/gradle-$GRADLE_VERSION /opt/gradle + +ENV PATH $PATH:/opt/gradle/bin +RUN JAVA_HOME=$(readlink $(readlink `which java`) | gawk '$0=gensub(/\/jre\/bin\/java/,"",1)') diff --git a/docker/spec/Dockerfile_spec.rb b/docker/spec/Dockerfile_spec.rb new file mode 100644 index 00000000..85bc02b8 --- /dev/null +++ b/docker/spec/Dockerfile_spec.rb @@ -0,0 +1,30 @@ +#! /usr/bin/ruby + +require "serverspec" +require "docker" + +describe "Dockerfile" do + before(:all) do + image = Docker::Image.build_from_dir('.') + + set :os, family: :redhat + set :backend, :docker + set :docker_image, image.id + end + + it "has jdk8 installed" do + expect(command("javac -version").stderr).to include(" 1.8.") + end + + it "has gradle installed" do + expect(command("gradle -version").stdout).to include("Build time") + end + + it "can build app" do + output = command("git clone https://github.com/matyb/java-koans && gradle -p java-koans/lib buildApp").stdout + expect(output).to include(":test\n") + expect(output).to include("BUILD SUCCESSFUL") + end + +end + diff --git a/koans-lib/.classpath b/koans-lib/.classpath deleted file mode 100755 index fb501163..00000000 --- a/koans-lib/.classpath +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/koans-lib/.project b/koans-lib/.project deleted file mode 100755 index 119dcdc9..00000000 --- a/koans-lib/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - koans-lib - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/koans-lib/.settings/org.eclipse.jdt.core.prefs b/koans-lib/.settings/org.eclipse.jdt.core.prefs deleted file mode 100755 index 6ed0deb7..00000000 --- a/koans-lib/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,12 +0,0 @@ -#Wed Apr 06 21:04:13 PDT 2011 -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.5 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.5 diff --git a/koans-lib/MANIFEST.MF b/koans-lib/MANIFEST.MF deleted file mode 100755 index 8ab9ca42..00000000 --- a/koans-lib/MANIFEST.MF +++ /dev/null @@ -1,4 +0,0 @@ -Manifest-Version: 1.0 -Class-Path: . ../bin/temp.jar -Main-Class: com.sandwich.koan.runner.KoanSuiteRunner - diff --git a/koans-lib/src/com/sandwich/koan/cmdline/CommandLineArgumentBuilder.java b/koans-lib/src/com/sandwich/koan/cmdline/CommandLineArgumentBuilder.java deleted file mode 100755 index 7b2b19b0..00000000 --- a/koans-lib/src/com/sandwich/koan/cmdline/CommandLineArgumentBuilder.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.sandwich.koan.cmdline; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; - -import com.sandwich.koan.constant.ArgumentType; -import com.sandwich.koan.constant.KoanConstants; -import com.sandwich.koan.util.ApplicationUtils; -import com.sandwich.util.ConsoleUtils; -import com.sandwich.util.io.DynamicClassLoader; - -public class CommandLineArgumentBuilder extends LinkedHashMap { - - private static final long serialVersionUID = 7635285665311420603L; - - public CommandLineArgumentBuilder(String...args){ - for(int index = 0; index < args.length;){ - String stringArg = args[index]; - if(stringArg == null || stringArg.trim().length() == 0){ - index++; - continue; - } - // is incremented in test of ternary - String stringArgPlusOne = args.length <= ++index ? null : args[index]; - stringArgPlusOne = stringArgPlusOne == null || stringArgPlusOne.trim().length() == 0 - ? null : stringArgPlusOne; - ArgumentType argumentType = ArgumentType.findTypeByString(stringArg); - ArgumentType argumentTypePlusOne = ArgumentType.findTypeByString(stringArgPlusOne); - if(argumentType != null){ // matches an anticipated argument string - // so does next value, must be an argument too, reevaluate w/ index incremented only once above - if(argumentTypePlusOne instanceof ArgumentType){ - put(argumentType, new CommandLineArgument(argumentType, null)); - // intentionally increment past next argument (only one) and - // evaluate in next iteration with its following argument - // - in other words, argumentType will be argumentTypePlusOne in next iteration - continue; - } - // ok 2nd argument wasn't a recongized argument type - go - // ahead and see if it's a class, if not, it must be a - // method - or bogus - else if(stringArgPlusOne != null){ - guessAtMethodAndClass(this, stringArgPlusOne); - } - else{ - put(argumentType, new CommandLineArgument(argumentType, null)); - } - index++; - continue; - }else if(stringArg != null){ - // no flag, but a string - likely class or class and method - guessAtMethodAndClass(this, stringArg); - // do not increment again, bump stringArgPlusOne into stringArg from prior increment - continue; - } - } - applyAssumedStartupBehaviors(); - } - - private static void guessAtMethodAndClass( - Map commandLineArguments, - String potentialClassOrMethod) { - boolean hasMethod = commandLineArguments.containsKey(ArgumentType.METHOD_ARG); - boolean hasClass = commandLineArguments.containsKey(ArgumentType.CLASS_ARG); - try{ - if(potentialClassOrMethod != null){ - if(!hasClass){ - commandLineArguments.put(ArgumentType.CLASS_ARG, - new CommandLineArgument(ArgumentType.CLASS_ARG, - new DynamicClassLoader().loadClass(potentialClassOrMethod).getName())); - }else if(!hasMethod){ - commandLineArguments.put(ArgumentType.METHOD_ARG, - new CommandLineArgument(ArgumentType.METHOD_ARG, potentialClassOrMethod)); - } - } - }catch(Exception cnfe2){ - if(!hasMethod){ - commandLineArguments.put(ArgumentType.METHOD_ARG, - new CommandLineArgument(ArgumentType.METHOD_ARG, potentialClassOrMethod)); - }else{ - throw new IllegalArgumentException(potentialClassOrMethod - + " does not match an expected argument, nor value."); - } - } - } - - void applyAssumedStartupBehaviors() { - if(ApplicationUtils.isFirstTimeAppHasBeenRun()){ - ArgumentType.BACKUP.run(null); - ConsoleUtils.clearConsole(); - } - if(isEmpty() || !containsKey(ArgumentType.RUN_KOANS) && ( - containsKey(ArgumentType.CLASS_ARG) || - containsKey(ArgumentType.DEBUG))){ - if(KoanConstants.DEBUG){ - System.out.println("Planting default run target."); - for(Entry argEntry : entrySet()){ - System.out.println("Key: '"+argEntry.getKey()+"'"); - System.out.println("Value: '"+argEntry.getValue()+"'"); - } - } - put(ArgumentType.RUN_KOANS, new CommandLineArgument(ArgumentType.RUN_KOANS, null, true)); - } - } -} diff --git a/koans-lib/src/com/sandwich/koan/cmdline/behavior/Backup.java b/koans-lib/src/com/sandwich/koan/cmdline/behavior/Backup.java deleted file mode 100755 index a741d250..00000000 --- a/koans-lib/src/com/sandwich/koan/cmdline/behavior/Backup.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.sandwich.koan.cmdline.behavior; - -import java.io.IOException; - -import com.sandwich.util.io.FileUtils; - -public class Backup extends KoanFileCopying{ - - @Override - protected void copy(String backupSrcDirectory, String appSrcDirectory) - throws IOException { - FileUtils.copy(appSrcDirectory, backupSrcDirectory); - } - -} diff --git a/koans-lib/src/com/sandwich/koan/cmdline/behavior/ClassArg.java b/koans-lib/src/com/sandwich/koan/cmdline/behavior/ClassArg.java deleted file mode 100755 index 2067e31c..00000000 --- a/koans-lib/src/com/sandwich/koan/cmdline/behavior/ClassArg.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.sandwich.koan.cmdline.behavior; - -import com.sandwich.koan.path.PathToEnlightenment; - -public class ClassArg extends AbstractArgumentBehavior { - - public void run(String koanSuiteClassName) { - if(koanSuiteClassName != null && koanSuiteClassName.trim().length() != 0){ - PathToEnlightenment.filterBySuite(koanSuiteClassName); - } - } - -} diff --git a/koans-lib/src/com/sandwich/koan/cmdline/behavior/Debug.java b/koans-lib/src/com/sandwich/koan/cmdline/behavior/Debug.java deleted file mode 100755 index 162fce0e..00000000 --- a/koans-lib/src/com/sandwich/koan/cmdline/behavior/Debug.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.sandwich.koan.cmdline.behavior; - -import com.sandwich.koan.constant.KoanConstants; - -public class Debug extends AbstractArgumentBehavior{ - - public void run(String arg){ - KoanConstants.DEBUG = KoanConstants.DEBUG || - "true".equalsIgnoreCase(arg) || - arg == null || - arg.trim().length() == 0; - } - -} diff --git a/koans-lib/src/com/sandwich/koan/cmdline/behavior/DefaultNoArg.java b/koans-lib/src/com/sandwich/koan/cmdline/behavior/DefaultNoArg.java deleted file mode 100755 index ffea71df..00000000 --- a/koans-lib/src/com/sandwich/koan/cmdline/behavior/DefaultNoArg.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.sandwich.koan.cmdline.behavior; - -public class DefaultNoArg extends AbstractArgumentBehavior { - - public void run(String value){ - - } - -} diff --git a/koans-lib/src/com/sandwich/koan/cmdline/behavior/MethodArg.java b/koans-lib/src/com/sandwich/koan/cmdline/behavior/MethodArg.java deleted file mode 100755 index f9bf2dc7..00000000 --- a/koans-lib/src/com/sandwich/koan/cmdline/behavior/MethodArg.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.sandwich.koan.cmdline.behavior; - -import com.sandwich.koan.path.PathToEnlightenment; - -public class MethodArg extends AbstractArgumentBehavior { - - public void run(String koanName) { - if(koanName != null && koanName.trim().length() != 0){ - PathToEnlightenment.filterByKoan(koanName); - } - } - -} diff --git a/koans-lib/src/com/sandwich/koan/cmdline/behavior/NotImplemented.java b/koans-lib/src/com/sandwich/koan/cmdline/behavior/NotImplemented.java deleted file mode 100755 index 65497ff9..00000000 --- a/koans-lib/src/com/sandwich/koan/cmdline/behavior/NotImplemented.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.sandwich.koan.cmdline.behavior; - - -public class NotImplemented extends AbstractArgumentBehavior{ - - public void run(String value) { - throw new UnsupportedOperationException(); - } - -} diff --git a/koans-lib/src/com/sandwich/koan/cmdline/behavior/Test.java b/koans-lib/src/com/sandwich/koan/cmdline/behavior/Test.java deleted file mode 100755 index 8061a982..00000000 --- a/koans-lib/src/com/sandwich/koan/cmdline/behavior/Test.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.sandwich.koan.cmdline.behavior; - -import java.io.IOException; - -import com.sandwich.util.io.FileCompiler; -import com.sandwich.util.io.directories.DirectoryManager; -import com.sandwich.util.io.directories.Production; -import com.sandwich.util.io.directories.UnitTest; - -public class Test extends AbstractArgumentBehavior{ - - public void run(String value) throws IOException { - try{ - DirectoryManager.setDirectorySet(new UnitTest()); - FileCompiler.compile(DirectoryManager.getSourceDir(), DirectoryManager.getBinDir()); - }finally{ - DirectoryManager.setDirectorySet(new Production()); - } - } - -} diff --git a/koans-lib/src/com/sandwich/koan/constant/KoanConstants.java b/koans-lib/src/com/sandwich/koan/constant/KoanConstants.java deleted file mode 100755 index d15ba1da..00000000 --- a/koans-lib/src/com/sandwich/koan/constant/KoanConstants.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.sandwich.koan.constant; - -public abstract class KoanConstants { - - private KoanConstants(){} - - public static boolean DEBUG = false; -// public static boolean DEBUG = true; - public static final char EXIT_CHARACTER = 'Q'; - public static final boolean ENABLE_ENCOURAGEMENT= false; - - public static final String __ = "REPLACE ME"; - public static final String PATH_XML_NAME = "PathToEnlightment.xml"; -// public static final String PATH_XML = "src/"; // TODO: Adapt so dev and deployed can use same path - - public static final String DESCRIPTION = "description"; - public static final String ARGUMENTS = "args"; - - public static final String EOL = System.getProperty("line.separator"); - public static final String EOLS = "[\n\r"+EOL+"]"; - - public static final String PERIOD = "."; - public static final String EXPECTATION_LEFT_ARG= "has expectation as wrong argument!"; - public static final String EXPECTED_LEFT = "expected:<"; - public static final String EXPECTED_RIGHT = ">"; - public static final String LINE_NO_START = ".java:"; - public static final String LINE_NO_END = ")"; - - public static final String DEFAULT_KOAN_DESC = "TODO: Add a description of what the koan is intended to teach the pupil"; - public static final String LEVEL = "Level: "; - public static final String APP_NAME = "Java Koans"; - public static final String ALL_SUCCEEDED = "Way to go! You've completed all of the koans! Feel like writing any?"; - public static final String WHATS_WRONG = "What went wrong:"; - public static final String CONQUERED = "You have conquered"; - public static final String OUT_OF = "out of"; - public static final String KOAN = "koan"; - public static final String ENCOURAGEMENT = "Keep going, you will persevere!"; - public static final String PASSING_SUITES = "Passing Suites:"; - public static final String FAILING_SUITES = "Remaining Suites:"; - public static final String INVESTIGATE_IN_THE = "Ponder what's going wrong in the"; - public static final String PROGRESS = "Progress:"; - public static final String COMPLETE_CHAR = "X"; - public static final String INCOMPLETE_CHAR = "-"; - - public static final int PROGRESS_BAR_WIDTH = 50; - public static final String PROGRESS_BAR_START = "["; - public static final String PROGRESS_BAR_END = "]"; - - public static final String XML_PARAMETER_START = "${"; - public static final String XML_PARAMETER_END = "}"; - -} diff --git a/koans-lib/src/com/sandwich/koan/constant/messages.properties b/koans-lib/src/com/sandwich/koan/constant/messages.properties deleted file mode 100644 index a0661d34..00000000 --- a/koans-lib/src/com/sandwich/koan/constant/messages.properties +++ /dev/null @@ -1,42 +0,0 @@ -ArgumentType.duplicated_arg_error_part1=command line arg: -ArgumentType.duplicated_arg_error_part2=\ is duplicated. - -Backup.description=Backup all the koans in the src/ for easy restoration later (useful for developing koans). -Backup.args= -backup, backup, b -Backup.error=An issue was encountered saving a backup copy. Check that the directory exists and try again. -Backup.success=Koans were backed up successfully - -ClassArg.args= -class, class, c -ClassArg.description=Switch is optional, app tries to find a class definition for any unrecognized string - which becomes a method argument if class is not found. If class lookup succeeds - an instance of the class will become the only koansuite to run. Permits users/developers to focus on one suite at a time. -ClassArg.error= -ClassArg.success= - -Debug.args= -debug, debug, d -Debug.description=Enable debug state in the app. -Debug.error= -Debug.success= - -Help.args= -help, help, h, ? -Help.description=Help. Displays stuff to, er, help you. -Help.error= -Help.success= - -MethodArg.args= -method, method, m -MethodArg.description=Switch is optional, results from failing to find a class definition by an unrecognized string if switch is omitted. -MethodArg.error= -MethodArg.success= - -Reset.args= -reset, reset, r, restore, -restore -Reset.description=Restore all the koans in the src/ folder to their original (or last backed up) state. -Reset.error=There was an unanticipated error encountered restoring the koan files. You're best bet is to start with a fresh copy from your downloads. -Reset.success=Koans restored successfully - -RunKoans.args= -RunKoans.description=Default target. No switch - this runs if no switch is defined, or if a valid class is found as an argument. -RunKoans.error= -RunKoans.success= - -Test.args= -test, test, t -Test.description=Run tests. System returns number of failing testcases - not to exceed 255 to retain DOS compatibility for BAT files. -Test.error= -Test.success \ No newline at end of file diff --git a/koans-lib/src/com/sandwich/koan/result/KoanBatchResult.java b/koans-lib/src/com/sandwich/koan/result/KoanBatchResult.java deleted file mode 100644 index 8681161d..00000000 --- a/koans-lib/src/com/sandwich/koan/result/KoanBatchResult.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.sandwich.koan.result; - -public class KoanBatchResult { - -} diff --git a/koans-lib/src/com/sandwich/koan/runner/AppLauncher.java b/koans-lib/src/com/sandwich/koan/runner/AppLauncher.java deleted file mode 100755 index 21b07633..00000000 --- a/koans-lib/src/com/sandwich/koan/runner/AppLauncher.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.sandwich.koan.runner; - -import java.util.Map; - -import com.sandwich.koan.cmdline.CommandLineArgument; -import com.sandwich.koan.cmdline.CommandLineArgumentBuilder; -import com.sandwich.koan.cmdline.CommandLineArgumentRunner; -import com.sandwich.koan.constant.ArgumentType; -import com.sandwich.koan.constant.KoanConstants; -import com.sandwich.util.io.FileMonitorFactory; -import com.sandwich.util.io.KoanFileCompileAndRunListener; -import com.sandwich.util.io.directories.DirectoryManager; - -public class AppLauncher { - - public static void main(final String... args) throws Throwable { - Map argsMap = new CommandLineArgumentBuilder(args); - new CommandLineArgumentRunner(argsMap).run(); - if(KoanConstants.DEBUG){ - StringBuilder argsBuilder = new StringBuilder(); - int argNumber = 0; - for(String arg : args){ - argsBuilder.append("Argument number "+String.valueOf(++argNumber)+": '"+arg+"'"); - } - System.out.println(argsBuilder.toString()); - } - if(argsMap.containsKey(ArgumentType.RUN_KOANS)){ - FileMonitorFactory.getInstance(DirectoryManager.getProdMainDir()) - .addFileSavedListener(new KoanFileCompileAndRunListener(argsMap)); - } - } -} diff --git a/koans-lib/src/com/sandwich/koan/ui/SuitePresenter.java b/koans-lib/src/com/sandwich/koan/ui/SuitePresenter.java deleted file mode 100755 index da076327..00000000 --- a/koans-lib/src/com/sandwich/koan/ui/SuitePresenter.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.sandwich.koan.ui; - -import com.sandwich.koan.result.KoanSuiteResult; - -public interface SuitePresenter { - - public void displayResult(KoanSuiteResult result); - -} diff --git a/koans-lib/src/com/sandwich/util/ConsoleUtils.java b/koans-lib/src/com/sandwich/util/ConsoleUtils.java deleted file mode 100644 index 4ae9cecf..00000000 --- a/koans-lib/src/com/sandwich/util/ConsoleUtils.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.sandwich.util; - -public class ConsoleUtils { - - private static final int NUMBER_OF_LINES_TO_CLEAR_CONSOLE = 80; - - public static void clearConsole(){ - for(int i = 0; i < NUMBER_OF_LINES_TO_CLEAR_CONSOLE; i++){ - System.out.println(); - } - } - -} diff --git a/koans-lib/src/com/sandwich/util/Counter.java b/koans-lib/src/com/sandwich/util/Counter.java deleted file mode 100755 index 5f15d901..00000000 --- a/koans-lib/src/com/sandwich/util/Counter.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.sandwich.util; - -public class Counter { - - private long count; - - public Counter(){ - this(0); - } - - public Counter(long count){ - this.count = count; - } - - public void count(){ - count++; - }; - - public long getCount(){ - return count; - } - - @Override - public String toString() { - return "Counter [count=" + count + "]"; - } - -} diff --git a/koans-lib/src/com/sandwich/util/KoanComparator.java b/koans-lib/src/com/sandwich/util/KoanComparator.java deleted file mode 100755 index 06bd7fbe..00000000 --- a/koans-lib/src/com/sandwich/util/KoanComparator.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.sandwich.util; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.logging.Logger; - -import com.sandwich.koan.KoanMethod; -import com.sandwich.util.io.FileUtils; - -public class KoanComparator implements Comparator { - List orderedKeywords; - Set methodNamesCompared = new HashSet(); - - public KoanComparator(String...koans){ - this(Arrays.asList(koans)); - } - - public KoanComparator(Collection koans){ - this.orderedKeywords = new ArrayList(koans); - } - - public int compare(KoanMethod arg0, KoanMethod arg1) { - Class declaringClass0 = arg0.getMethod().getDeclaringClass(); - Class declaringClass1 = arg1.getMethod().getDeclaringClass(); - if(declaringClass0 != declaringClass1){ - Logger.getAnonymousLogger().severe("no idea how to handle comparing the classes: " + declaringClass0 + " and: "+declaringClass1); - return 0; - } - String contentsOfOriginalJavaFile = FileUtils.getContentsOfOriginalJavaFile(declaringClass0.getName()); - Integer index0 = Integer.valueOf( contentsOfOriginalJavaFile.indexOf(arg0.getMethod().getName())); - Integer index1 = Integer.valueOf( contentsOfOriginalJavaFile.indexOf(arg1.getMethod().getName())); - return index0.compareTo(index1); - } - -} \ No newline at end of file diff --git a/koans-lib/src/com/sandwich/util/Strings.java b/koans-lib/src/com/sandwich/util/Strings.java deleted file mode 100644 index 24d53727..00000000 --- a/koans-lib/src/com/sandwich/util/Strings.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.sandwich.util; -import static com.sandwich.koan.constant.KoanConstants.PERIOD; - -import java.util.MissingResourceException; -import java.util.ResourceBundle; - -public class Strings { - - //$NON-NLS - private static final ResourceBundle MESSAGES_BUNDLE = ResourceBundle.getBundle("com.sandwich.koan.constant.messages"); - - private Strings() { - } - - public static String getMessage(String key) { - try { - return MESSAGES_BUNDLE.getString(key); - } catch (MissingResourceException e) { - return '!' + key + '!'; - } - } - - public static String getMessage(Class clazz, String key){ - return Strings.getMessage(new StringBuilder(clazz.getSimpleName()).append(PERIOD).append(key).toString()); - } - - public static String[] getMessages(Class clazz, String key) { - String[] tmp = getMessage(clazz, key).split(","); - String[] trimmed = new String[tmp.length]; - for(int i = 0; i < tmp.length; i++){ - trimmed[i] = tmp[i].trim(); - } - return trimmed; - } -} diff --git a/koans-lib/src/com/sandwich/util/io/DynamicClassLoader.java b/koans-lib/src/com/sandwich/util/io/DynamicClassLoader.java deleted file mode 100644 index ffc85c88..00000000 --- a/koans-lib/src/com/sandwich/util/io/DynamicClassLoader.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.sandwich.util.io; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -import com.sandwich.koan.constant.KoanConstants; -import com.sandwich.util.io.directories.DirectoryManager; - -public class DynamicClassLoader extends ClassLoader { - - private static Map> classesByLocation = new HashMap>(); - private static Map, URL> locationByClass = new HashMap, URL>(); - private final FileMonitor fileMonitor = FileMonitorFactory.getInstance( - DirectoryManager.getProdMainDir()); - - public DynamicClassLoader(){ - this(ClassLoader.getSystemClassLoader()); - } - - public DynamicClassLoader(ClassLoader parent) { - super(parent); - } - - public static void remove(URL url){ - String urlToString = url.toString().replace(FileCompiler.CLASS_SUFFIX, "").replace(FileCompiler.JAVA_SUFFIX, ""); - for(Entry> entry : classesByLocation.entrySet()){ - if(entry.getKey().toString().contains(urlToString)){ - locationByClass.remove(entry.getValue()); - entry.setValue(null); - } - } - } - - public static void remove(Class clas){ - for(Entry, URL> entry : locationByClass.entrySet()){ - if(entry.getKey().getName().contains(clas.getName())){ - classesByLocation.remove(entry.getValue()); - entry.setValue(null); - } - } - } - - public Class loadClass(String className){ - String fileName = DirectoryManager.getBinDir() - + DirectoryManager.FILESYSTEM_SEPARATOR - + className.replace(KoanConstants.PERIOD, DirectoryManager.FILESYSTEM_SEPARATOR) - + FileCompiler.CLASS_SUFFIX; - File classFile = new File(fileName); - try { - // file may have never been compiled, go ahead and compile it now - File sourceFile = FileUtils.classToSource(classFile); - if(classFile.exists()){ - String absolutePath = classFile.getAbsolutePath(); - boolean isAnonymous = absolutePath.contains("$"); - if(fileMonitor.isFileModifiedSinceLastPoll(sourceFile.getAbsolutePath(), sourceFile.lastModified())){ - if(!isAnonymous){ - compile(className, fileName, sourceFile); - } - } - return loadClass(classFile.toURI().toURL(), className); - } - try{ - return super.loadClass(className); - }catch(ClassNotFoundException x){ - compile(className, fileName, sourceFile); - classFile = new File(fileName); - return loadClass(classFile.toURI().toURL(), className); - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private void compile(String className, String fileName, File sourceFile) - throws IOException { - FileCompiler.compile(sourceFile, - new File(DirectoryManager.getBinDir()), - DirectoryManager.getProjectLibraryDir() + DirectoryManager.FILESYSTEM_SEPARATOR + "koans.jar"); - fileMonitor.updateFileSaveTime(sourceFile); - } - - public Class loadClass(URL url, String className){ - Class clazz = classesByLocation.get(url); - if(clazz != null){ - return clazz; - } - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - try { - URLConnection connection = url.openConnection(); - InputStream input = connection.getInputStream(); - int data = input.read(); - while(data != -1){ - buffer.write(data); - data = input.read(); - } - input.close(); - } catch (IOException e) { - e.printStackTrace(); - } - - byte[] classData = buffer.toByteArray(); - clazz = defineClass(className, classData, 0, classData.length); - classesByLocation.put(url, clazz); - locationByClass.put(clazz, url); - return clazz; - } -} diff --git a/koans-lib/src/com/sandwich/util/io/FileAction.java b/koans-lib/src/com/sandwich/util/io/FileAction.java deleted file mode 100644 index 1ea98cc5..00000000 --- a/koans-lib/src/com/sandwich/util/io/FileAction.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.sandwich.util.io; - -import java.io.File; -import java.io.IOException; - -public interface FileAction { - - void sourceToDestination(File src, File dest) throws IOException; - - File makeDestination(File dest, String fileInDirectory); -} diff --git a/koans-lib/src/com/sandwich/util/io/FileCompiler.java b/koans-lib/src/com/sandwich/util/io/FileCompiler.java deleted file mode 100644 index 6527f96c..00000000 --- a/koans-lib/src/com/sandwich/util/io/FileCompiler.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.sandwich.util.io; - -import java.io.File; -import java.io.IOException; - -public class FileCompiler { - - public static final String JAVA_SUFFIX = ".java"; - public static final String CLASS_SUFFIX = ".class"; - - public static void compile(String src, String bin) throws IOException { - compile(new File(src), new File(bin)); - } - - public static void compile(File src, File bin, final String...classpath) throws IOException { - final File destinationDirectory = bin; - if(!destinationDirectory.exists()){ - if(!destinationDirectory.mkdir()){ - System.out.println("Was unable to create: "+destinationDirectory); - System.exit(-231); - } - } - FileUtils.forEachFile(src, bin, new FileCompilerAction(destinationDirectory, classpath)); - } - -} diff --git a/koans-lib/src/com/sandwich/util/io/FileCompilerAction.java b/koans-lib/src/com/sandwich/util/io/FileCompilerAction.java deleted file mode 100755 index 8de4d1fd..00000000 --- a/koans-lib/src/com/sandwich/util/io/FileCompilerAction.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.sandwich.util.io; - -import java.io.File; -import java.io.IOException; -import java.util.Arrays; - -import com.sandwich.koan.constant.KoanConstants; - -class FileCompilerAction implements FileAction { - - private final String destinationPath; - private final String[] classPaths; - - public FileCompilerAction(File destinationPath, String...classPaths){ - this.destinationPath = destinationPath.getAbsolutePath(); - this.classPaths = classPaths; - } - - public void sourceToDestination(File src, File bin) throws IOException { - // javac -d ..\bin -classpath ..\lib\koans.jar beginner\*.java - String fileName = src.getName(); - if (fileName.length() > 4 - && fileName.toLowerCase().endsWith(FileCompiler.JAVA_SUFFIX)) { - String[] command = constructJavaCompilationCommand(src); - if (KoanConstants.DEBUG) { - System.out.println("executing command: \"" + command - + "\" to compile the sourcefile: " + src + "."); - } - Process p = Runtime.getRuntime().exec(command); - try { - if (KoanConstants.DEBUG) { - System.out.println("compiling file: " + src.getAbsolutePath()); - } - if (p.waitFor() != 0) { - compilationFailed(src, command, p, null); - } - } catch (Exception x) { - x.printStackTrace(); - compilationFailed(src, command, p, x); - } - } - } - - private String[] constructJavaCompilationCommand(File src) { - return copy(new String[]{"javac", "-d", destinationPath}, getClasspath(), new String[]{src.getAbsolutePath()}); - } - - private String[] copy(String[]...strings) { - String[] copies = new String[getTotalSize(strings)]; - int i = 0; - for(String[] strings2 : strings){ - for(String string : strings2){ - copies[i++] = string; - } - } - return copies; - } - - private int getTotalSize(String[][] strings) { - int i = 0; - for(String[] strings2 : strings){ - i += strings2.length; - } - return i; - } - - private void compilationFailed(File src, String[] command, Process p, Throwable x) { - System.out.println("\n*****************************************************************"); - System.out.println(Arrays.toString(command)); - System.out.println(src.getAbsolutePath() + " does not compile. exit status was: " + p.exitValue()); - System.out.println("*****************************************************************\n"); - } - - private String[] getClasspath() { - String[] classpaths = new String[classPaths.length + 1]; - if (classPaths.length > 0) { - classpaths[0] = "-classpath"; - }else{ - return new String[]{}; - } - for(int i = 1; i <= classPaths.length; i++){ - classpaths[i] = classPaths[i - 1]; - } - return classpaths; - } - - public File makeDestination(File dest, String fileInDirectory) { - String fileName = dest.getName(); - fileName = fileName.length() > 4 - && fileName.toLowerCase().contains(FileCompiler.JAVA_SUFFIX) ? fileName - .replace(FileCompiler.JAVA_SUFFIX, FileCompiler.CLASS_SUFFIX) : fileName; - dest = new File(dest, fileInDirectory); - System.out.println("file: " + dest.getAbsolutePath()); - return dest; - } - -} diff --git a/koans-lib/src/com/sandwich/util/io/FileMonitorFactory.java b/koans-lib/src/com/sandwich/util/io/FileMonitorFactory.java deleted file mode 100644 index e40d7b9a..00000000 --- a/koans-lib/src/com/sandwich/util/io/FileMonitorFactory.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.sandwich.util.io; - -import java.io.IOException; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; - -import com.sandwich.koan.constant.KoanConstants; - -public class FileMonitorFactory { - - private static Map monitors = new LinkedHashMap(); - public static final int SLEEP_TIME_IN_MS = 500; - private static boolean keepCheckingDirectory = true; - - // poll for file modifications - static{ - new Thread(new Runnable(){ - public void run() { - while(keepCheckingDirectory){ - try { - Thread.sleep(SLEEP_TIME_IN_MS); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - for(Entry filePathAndMonitor : monitors.entrySet()){ - FileMonitor monitor = filePathAndMonitor.getValue(); - if(monitor != null){ - monitor.notifyListeners(); - } - } - } - } - }).start(); - } - - // poll for keyboard input to stop the polling - static{ - new Thread(new Runnable(){ - public void run() { - while(keepCheckingDirectory){ - char c = ' '; - try { - c = (char)System.in.read(); - } catch (IOException e) { - e.printStackTrace(); - } - if(Character.toUpperCase(c) == Character.valueOf(KoanConstants.EXIT_CHARACTER)){ - keepCheckingDirectory = false; - for(FileMonitor monitor : monitors.values()){ - monitor.close(); - } - monitors.clear(); - } - } - } - }).start(); - } - - public static FileMonitor getInstance(String fileSystemPath) { - FileMonitor monitor = monitors.get(fileSystemPath); - if(monitor == null){ - monitor = new FileMonitor(fileSystemPath); - monitors.put(fileSystemPath, monitor); - } - return monitor; - } - - public static void removeInstance(FileMonitor monitor){ - monitor.close(); - monitors.remove(monitor.getFilesystemPath()); - } - - public static void removeInstance(String absolutePath) { - monitors.remove(absolutePath); - } - -} diff --git a/koans-lib/src/com/sandwich/util/io/FileUtils.java b/koans-lib/src/com/sandwich/util/io/FileUtils.java deleted file mode 100755 index f4255a32..00000000 --- a/koans-lib/src/com/sandwich/util/io/FileUtils.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.sandwich.util.io; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import com.sandwich.koan.constant.KoanConstants; -import com.sandwich.util.io.directories.DirectoryManager; - -public class FileUtils { - - private static final String DOLLAR_SIGN = "$"; - public static String BASE_DIR; static{ File dir = new File(ClassLoader.getSystemResource(".").getFile()); - if(dir.exists()){ - dir = dir.getParentFile(); - if(dir.exists()){ - dir = dir.getParentFile(); // go up 2 levels from koans/src or koans-tests/src - } - } - BASE_DIR = dir.getAbsolutePath(); - } - - public static void copy(String fileName0, String fileName1) throws IOException { - copy(new File(fileName0), new File(fileName1)); - } - - public static void copy(final File file0, final File file1) throws IOException{ - forEachFile(file0, file1, new FileAction(){ - public void sourceToDestination(File src, File dest) throws IOException { - InputStream in = new FileInputStream(src); - OutputStream out = new FileOutputStream(dest); - - // Transfer bytes from in to out - byte[] buf = new byte[1024]; - int len; - - while ((len = in.read(buf)) > 0) { - out.write(buf, 0, len); - } - - in.close(); - out.close(); - } - public File makeDestination(File dest, String fileInDirectory) { - if(!dest.exists()){ - dest.mkdirs(); - } - return new File(dest, fileInDirectory); - } - }); - } - - public static void forEachFile(File src, File dest, FileAction fileAction) throws IOException { - if((src == null || !src.exists()) && dest == null){ - throw new IllegalArgumentException("Both path's must actually exist"); - } - if (src.isDirectory()) { - if (!dest.exists()) { - dest.mkdir(); - } - String files[] = src.list(); - for (int i = 0; i < files.length; i++) { - forEachFile(new File(src, files[i]), fileAction.makeDestination(dest, files[i]), fileAction); - } - } else { - if (!src.exists()) { - throw new IOException("File or directory does not exist: "+src); - } else { - fileAction.sourceToDestination(src, dest); - } - } - } - - public static String readFileAsString(File file) { - byte[] buffer = new byte[(int) file.length()]; - BufferedInputStream f = null; - try { - f = new BufferedInputStream(new FileInputStream(file)); - f.read(buffer); - } catch (IOException e) { - throw new RuntimeException(e); - } finally { - if (f != null) try { f.close(); } catch (IOException ignored) { } - } - return new String(buffer); - } - - public static String getContentsOfOriginalJavaFile(String className) { - File sourceFile = new File( DirectoryManager.getSourceDir() - + DirectoryManager.FILESYSTEM_SEPARATOR - + classNameToJavaFileName(className)); - if(!sourceFile.exists()){ - throw new RuntimeException(new FileNotFoundException(sourceFile.getAbsolutePath()+" does not exist")); - } - return readFileAsString(sourceFile); - } - - private static String classNameToJavaFileName(String className) { - className = className.replace(KoanConstants.PERIOD, DirectoryManager.FILESYSTEM_SEPARATOR); - if(className.contains(DOLLAR_SIGN)){ - className = className.substring(0, className.indexOf(DOLLAR_SIGN)); - } - return className + FileCompiler.JAVA_SUFFIX; - } - - public static File sourceToClass(File file) { - return new File(file.getAbsolutePath() - .replace(DirectoryManager.getSourceDir(), DirectoryManager.getBinDir()) - .replace(FileCompiler.JAVA_SUFFIX, FileCompiler.CLASS_SUFFIX)); - } - - public static File classToSource(File file) { - return classToSource(file.getAbsolutePath()); - } - - public static File classToClassFile(Class clazz) { - String className = clazz.getName(); - String path = className.replace(".", System.getProperty("file.separator")) + FileCompiler.CLASS_SUFFIX; - return new File(ClassLoader.getSystemResource(path).toString().substring(5)); - } - - public static File classToSource(String absolutePath) { - return new File(absolutePath - .replace(DirectoryManager.getBinDir(), DirectoryManager.getSourceDir()) - .replace(FileCompiler.CLASS_SUFFIX, FileCompiler.JAVA_SUFFIX)); - } -} diff --git a/koans-lib/src/com/sandwich/util/io/KoanFileCompileAndRunListener.java b/koans-lib/src/com/sandwich/util/io/KoanFileCompileAndRunListener.java deleted file mode 100644 index 6ef591e9..00000000 --- a/koans-lib/src/com/sandwich/util/io/KoanFileCompileAndRunListener.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.sandwich.util.io; - -import java.io.File; -import java.io.IOException; -import java.util.Collections; -import java.util.Map; - -import com.sandwich.koan.cmdline.CommandLineArgumentRunner; -import com.sandwich.koan.cmdline.CommandLineArgument; -import com.sandwich.koan.constant.ArgumentType; -import com.sandwich.koan.constant.KoanConstants; -import com.sandwich.util.ConsoleUtils; -import com.sandwich.util.io.directories.DirectoryManager; - -public class KoanFileCompileAndRunListener implements FileListener { - - private Map args = Collections.emptyMap(); - - public KoanFileCompileAndRunListener(Map args) throws IOException{ - this.args = args; - } - - public void fileSaved(File file) { - String absolutePath = file.getAbsolutePath(); - if(absolutePath.toLowerCase().endsWith(FileCompiler.JAVA_SUFFIX)){ - System.out.println(KoanConstants.EOL+"loading: "+absolutePath); - try { - FileCompiler.compile(file, - new File(DirectoryManager.getBinDir()), - DirectoryManager.injectFileSystemSeparators(DirectoryManager.getProjectLibraryDir(), "koans.jar")); - DynamicClassLoader.remove(FileUtils.sourceToClass(file).toURI().toURL()); - ConsoleUtils.clearConsole(); - new CommandLineArgumentRunner(args).run(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - } - - public void newFile(File file) { - - } - - public void fileDeleted(File file) { - - } -} diff --git a/koans-lib/src/com/sandwich/util/io/directories/DirectorySet.java b/koans-lib/src/com/sandwich/util/io/directories/DirectorySet.java deleted file mode 100755 index 493781b7..00000000 --- a/koans-lib/src/com/sandwich/util/io/directories/DirectorySet.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.sandwich.util.io.directories; - -import java.io.File; - -abstract class DirectorySet { - - private static final String BIN_DIR = "bin"; - private static final String LIB_DIR = "lib"; - private static final String DATA_DIR = "data"; - private static final String BASE_DIR = createBaseDir(); - - abstract String getSourceDir(); - abstract String getProjectDir(); - - public String getBaseDir(){ - return BASE_DIR; - } - - public String getBinaryDir(){ - return BIN_DIR; - } - - public String getLibrariesDir(){ - return LIB_DIR; - } - - public String getDataDir(){ - return DATA_DIR; - } - - private static String createBaseDir() { - File dir = new File(ClassLoader.getSystemResource(".").getFile()); - dir = new File(dir.getAbsolutePath().replace("%20", " ")); - if (dir.exists()) { - dir = dir.getParentFile(); - if (dir.exists()) { - dir = dir.getParentFile(); // go up 2 levels from koans/src or - // koans-tests/src - } - } - return dir.getAbsolutePath(); - } -} diff --git a/koans-lib/src/com/sandwich/util/io/directories/UnitTest.java b/koans-lib/src/com/sandwich/util/io/directories/UnitTest.java deleted file mode 100644 index 9f38e0ad..00000000 --- a/koans-lib/src/com/sandwich/util/io/directories/UnitTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.sandwich.util.io.directories; - - -public class UnitTest extends DirectorySet { - - public String getSourceDir() { - return "test"; - } - - public String getProjectDir() { - return "koans-tests"; - } - -} diff --git a/koans-tests/.classpath b/koans-tests/.classpath deleted file mode 100755 index 6bae21bc..00000000 --- a/koans-tests/.classpath +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/koans-tests/.project b/koans-tests/.project deleted file mode 100755 index 4d6ea963..00000000 --- a/koans-tests/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - koans-tests - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/koans-tests/.settings/org.eclipse.jdt.core.prefs b/koans-tests/.settings/org.eclipse.jdt.core.prefs deleted file mode 100755 index 7b345363..00000000 --- a/koans-tests/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,12 +0,0 @@ -#Wed Apr 06 21:08:03 PDT 2011 -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.5 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.5 diff --git a/koans-tests/data/file_hashes.dat b/koans-tests/data/file_hashes.dat deleted file mode 100644 index ff3347b5..00000000 Binary files a/koans-tests/data/file_hashes.dat and /dev/null differ diff --git a/koans-tests/data/src/cglib-nodep-2.2.jar b/koans-tests/data/src/cglib-nodep-2.2.jar deleted file mode 100644 index ed07cb50..00000000 Binary files a/koans-tests/data/src/cglib-nodep-2.2.jar and /dev/null differ diff --git a/koans-tests/data/src/com/sandwich/koan/cmdline/CommandLineArgumentBuilderTest.java b/koans-tests/data/src/com/sandwich/koan/cmdline/CommandLineArgumentBuilderTest.java deleted file mode 100644 index 5098f8da..00000000 --- a/koans-tests/data/src/com/sandwich/koan/cmdline/CommandLineArgumentBuilderTest.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.sandwich.koan.cmdline; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.Map; -import java.util.Map.Entry; - -import org.junit.Test; - -import com.sandwich.koan.constant.ArgumentType; -import com.sandwich.util.SimpleEntry; - - -public class CommandLineArgumentBuilderTest { - - @Test - public void testNoArguments() throws Exception { - assertEquals(ArgumentType.RUN_KOANS, new CommandLineArgumentBuilder().entrySet().iterator().next().getKey()); - } - - @Test - public void testUnanticipatedArgument_yieldsMethodArg_constructedImplicitly() throws Exception { - String value = - "if string isn't a known command line arg (ArgumentType) or class - assume its a method"; - Entry anticipatedResult = - new SimpleEntry(ArgumentType.METHOD_ARG, - new CommandLineArgument(ArgumentType.METHOD_ARG, value)); - Map commandLineArgs = - new CommandLineArgumentBuilder(value); - assertEquals(1, commandLineArgs.size()); - assertEquals(anticipatedResult, commandLineArgs.entrySet().iterator().next()); - } - - @Test - public void testMethodArg_constructedExplicitly() throws Exception { - String value = "someMethodName"; - Entry anticipatedResult = - new SimpleEntry(ArgumentType.METHOD_ARG, - new CommandLineArgument(ArgumentType.METHOD_ARG, value)); - Map commandLineArgs = new CommandLineArgumentBuilder( - ArgumentType.METHOD_ARG.args().iterator().next(), - value); - assertEquals(1, commandLineArgs.size()); - assertEquals(anticipatedResult, commandLineArgs.entrySet().iterator().next()); - } - - @Test - public void testClassArg_constructedImplicitly() throws Exception { - String value = Object.class.getName(); - Map commandLineArgs = new CommandLineArgumentBuilder(value); - assertEquals(2, commandLineArgs.size()); - assertTrue(commandLineArgs.containsKey(ArgumentType.CLASS_ARG)); - assertTrue(commandLineArgs.containsKey(ArgumentType.RUN_KOANS)); - } - - @Test - public void testClassArg_constructedExplicitly() throws Exception { - String value = Object.class.getName(); - Map commandLineArgs = new CommandLineArgumentBuilder( - ArgumentType.CLASS_ARG.args().iterator().next(), value); - assertEquals(2, commandLineArgs.size()); - assertTrue(commandLineArgs.containsKey(ArgumentType.CLASS_ARG)); - assertTrue(commandLineArgs.containsKey(ArgumentType.RUN_KOANS)); - } - - @Test - public void testMultipleMethodOrUnknownArgs_throwsException() throws Exception { - String value = "someMethodName"; - try{ - new CommandLineArgumentBuilder(ArgumentType.METHOD_ARG.args().iterator().next(), value, value); - fail(); - }catch(IllegalArgumentException x){ - - } - } - -} diff --git a/koans-tests/data/src/com/sandwich/koan/path/CommandLineTestCase.java b/koans-tests/data/src/com/sandwich/koan/path/CommandLineTestCase.java deleted file mode 100644 index 78b3b184..00000000 --- a/koans-tests/data/src/com/sandwich/koan/path/CommandLineTestCase.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.sandwich.koan.path; -import static org.junit.Assert.assertEquals; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.junit.After; -import org.junit.Before; - -import com.sandwich.koan.Koan; -import com.sandwich.koan.KoanIncompleteException; -import com.sandwich.koan.TestUtils; -import com.sandwich.koan.TestUtils.ArgRunner; -import com.sandwich.koan.constant.ArgumentType; -import com.sandwich.koan.path.PathToEnlightenment.Path; -import com.sandwich.koan.path.xmltransformation.FakeXmlToPathTransformer; -import com.sandwich.koan.path.xmltransformation.KoanElementAttributes; -import com.sandwich.koan.runner.RunKoans; -import com.sandwich.util.io.DynamicClassLoader; -import com.sandwich.util.io.directories.DirectoryManager; -import com.sandwich.util.io.directories.Production; -import com.sandwich.util.io.directories.UnitTest; - -public abstract class CommandLineTestCase { - - private PrintStream console; - private ByteArrayOutputStream bytes; - - @Before - public void setUp() { - DirectoryManager.setDirectorySet(new UnitTest()); - bytes = new ByteArrayOutputStream(); - console = System.out; - TestUtils.setValue("behavior", new RunKoans(), ArgumentType.RUN_KOANS); - PathToEnlightenment.xmlToPathTransformer = new FakeXmlToPathTransformer(); - PathToEnlightenment.theWay = PathToEnlightenment.createPath(); - System.setOut(new PrintStream(bytes)); - } - - @After - public void tearDown() { - DirectoryManager.setDirectorySet(new Production()); - setRealPath(); - System.setOut(console); - } - - protected void setRealPath(){ - PathToEnlightenment.xmlToPathTransformer = null; - PathToEnlightenment.theWay = PathToEnlightenment.createPath(); - } - - protected Path stubAllKoans(String packageName, List path){ - Path oldKoans = PathToEnlightenment.getPathToEnlightment(); - Map> tempSuitesAndMethods = - new LinkedHashMap>(); - DynamicClassLoader loader = new DynamicClassLoader(); - for(String suite : path){ - Map methodsByName = new LinkedHashMap(); - for(Method m : loader.loadClass(suite).getMethods()){ - if(m.getAnnotation(Koan.class) != null){ - methodsByName.put(m.getName(), new KoanElementAttributes("", m.getName(), "", m.getDeclaringClass().getName())); - } - } - tempSuitesAndMethods.put(suite, methodsByName); - } - Map>> stubbedPath = - new LinkedHashMap>>(); - stubbedPath.put(packageName, tempSuitesAndMethods); - PathToEnlightenment.theWay = new Path(null,stubbedPath); - return oldKoans; - } - - public Path stubAllKoans(List path){ - List classes = new ArrayList(); - for(Object o : path){ - String className; - if(o instanceof Class){ - className = ((Class)o).getName(); - }else{ - className = o.getClass().getName(); - } - classes.add(className); - } - return stubAllKoans("Test", classes); - } - - public void clearSysout(){ - bytes = new ByteArrayOutputStream(); - System.setOut(new PrintStream(bytes)); - } - - public void assertSystemOutEquals(String expectation){ - expectation = expectation == null ? "" : expectation; - if(!expectation.equals(bytes.toString())){ - throw new KoanIncompleteException("expected: <"+expectation+"> but found: <"+bytes.toString()+">"); - } - } - - public void assertSystemOutContains(String expectation){ - assertSystemOutContains(true, expectation); - } - - protected void assertSystemOutDoesntContain(String expectation){ - assertSystemOutContains(false, expectation); - } - - private void assertSystemOutContains(boolean assertContains, String expectation) { - String consoleOutput = bytes.toString(); - boolean containsTheSubstring = consoleOutput.contains(expectation); - if(assertContains && !containsTheSubstring || !assertContains && containsTheSubstring){ - throw new KoanIncompleteException(new StringBuilder( - "<").append( - expectation).append( - "> ").append( - (assertContains ? "wasn't" : "was")).append( - " found in: " ).append( - "<").append( - consoleOutput).append( - ">").toString()); - } - } - - public void assertSystemOutLineEquals(final int lineNumber, final String lineText){ - assertSystemOutLineEquals(lineNumber, lineText, false); - } - - public void assertSystemOutLineEquals(final int lineNumber, final String lineText, - final boolean trimLinesString) { - final int[] onLine = new int[]{0}; - final boolean[] found = new boolean[]{false}; - TestUtils.forEachLine(bytes.toString(), new ArgRunner(){ - public void run(String s){ - if(onLine[0] == lineNumber){ - if(trimLinesString){ - s = s.trim(); - } - assertEquals(lineText, s); - found[0] = true; - } - onLine[0]++; - } - }); - if(!found[0]){ - throw new KoanIncompleteException(lineText+" was expected, but not found in: "+bytes.toString()); - } - } -} diff --git a/koans-tests/data/src/com/sandwich/koan/path/DefaultKoanDescriptionTest.java b/koans-tests/data/src/com/sandwich/koan/path/DefaultKoanDescriptionTest.java deleted file mode 100644 index 71d6d876..00000000 --- a/koans-tests/data/src/com/sandwich/koan/path/DefaultKoanDescriptionTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.sandwich.koan.path; - -import java.util.Map; -import java.util.Map.Entry; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.sandwich.koan.Koan; -import com.sandwich.koan.KoanMethod; -import com.sandwich.koan.constant.KoanConstants; -import com.sandwich.koan.path.xmltransformation.KoanElementAttributes; - -public class DefaultKoanDescriptionTest extends CommandLineTestCase { - - @Before - public void setUp(){ - super.setUp(); - } - - @After - public void tearDown(){ - super.tearDown(); - } - - @Test - public void defaultKoanDescriptions() throws Exception { - StringBuilder exceptionStringBuilder = new StringBuilder(KoanConstants.EOL); - for (Entry> suiteAndKoans : - PathToEnlightenment.getPathToEnlightment().getKoanMethodsBySuiteByPackage().next().getValue().entrySet()) { - for(Entry koanEntry : suiteAndKoans.getValue().entrySet()){ - KoanMethod koan = KoanMethod.getInstance(koanEntry.getValue()); - Koan annotation = koan.getMethod().getAnnotation(Koan.class); - if (annotation != null && KoanConstants.DEFAULT_KOAN_DESC.equals(koan.getLesson())) { - exceptionStringBuilder.append(suiteAndKoans.getKey().getClass().getName()).append('.') - .append(koan.getMethod().getName()).append(KoanConstants.EOL); - } - } - } - String exceptionString = exceptionStringBuilder.toString(); - if(exceptionString.trim().length() != 0){ - throw new RuntimeException(new StringBuilder(KoanConstants.EOL).append( - "Following still have default Koan description:").append(exceptionString).toString()); - } - } - -} diff --git a/koans-tests/data/src/com/sandwich/koan/runner/AppReadinessForDeploymentTest.java b/koans-tests/data/src/com/sandwich/koan/runner/AppReadinessForDeploymentTest.java deleted file mode 100644 index fc06ead5..00000000 --- a/koans-tests/data/src/com/sandwich/koan/runner/AppReadinessForDeploymentTest.java +++ /dev/null @@ -1,199 +0,0 @@ -package com.sandwich.koan.runner; - -import static com.sandwich.koan.constant.KoanConstants.EXPECTATION_LEFT_ARG; -import static com.sandwich.koan.constant.KoanConstants.__; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; - -import java.util.Arrays; -import java.util.Map; -import java.util.Map.Entry; -import java.util.logging.Handler; -import java.util.logging.LogRecord; -import java.util.logging.Logger; - -import org.junit.Test; - -import com.sandwich.koan.Koan; -import com.sandwich.koan.KoanMethod; -import com.sandwich.koan.cmdline.CommandLineArgumentBuilder; -import com.sandwich.koan.cmdline.CommandLineArgumentRunner; -import com.sandwich.koan.constant.KoanConstants; -import com.sandwich.koan.path.CommandLineTestCase; -import com.sandwich.koan.path.PathToEnlightenment; -import com.sandwich.koan.path.xmltransformation.KoanElementAttributes; -import com.sandwich.koan.result.KoanSuiteResult; -import com.sandwich.koan.suite.BlowUpOnLineEleven; -import com.sandwich.koan.suite.BlowUpOnLineTen; -import com.sandwich.koan.suite.OneFailingKoan; -import com.sandwich.koan.suite.OnePassingKoan; -import com.sandwich.koan.ui.SuitePresenter; -import com.sandwich.util.io.directories.DirectoryManager; -import com.sandwich.util.io.directories.Production; - -/** - * Anything that absoutely has to happen before bundling client jar - to be sure: - * - all koans fail by default - * - necessary aspects of app presentation are preserved - * - progression through koans (the sequence of koans) is consistent - */ -public class AppReadinessForDeploymentTest extends CommandLineTestCase { - - @Test - public void testMainMethodWithClassNameArg_qualifiedWithPkgName() throws Throwable { - stubAllKoans(Arrays.asList(new OnePassingKoan())); - new CommandLineArgumentRunner().run(); - assertSystemOutContains(KoanConstants.PASSING_SUITES+" "+OnePassingKoan.class.getSimpleName()); - } - - @Test - public void testMainMethodWithClassNameArg_classSimpleName() throws Throwable { - stubAllKoans(Arrays.asList(new OnePassingKoan())); - new CommandLineArgumentRunner().run(); - assertSystemOutContains(KoanConstants.PASSING_SUITES+" "+OnePassingKoan.class.getSimpleName()); - } - - @Test - public void testMainMethodWithClassNameArg_classNameAndMethod() throws Throwable { - stubAllKoans(Arrays.asList(new TwoFailingKoans())); - String failingKoanMethodName = TwoFailingKoans.class.getMethod("koanTwo").getName(); - new CommandLineArgumentRunner().run(); - assertSystemOutContains(failingKoanMethodName); - assertSystemOutContains("0/2"); - } - - public static class TwoFailingKoans extends OneFailingKoan { - @Koan - public void koanTwo(){assertEquals(true, false);} - } - - @Test - public void testGetKoans() throws Exception { - stubAllKoans(Arrays.asList(new OnePassingKoan())); - Map> koans = PathToEnlightenment.getPathToEnlightment().iterator().next().getValue(); - assertEquals(1, koans.size()); - Entry> entry = koans.entrySet().iterator().next(); - assertEquals(OnePassingKoan.class.getName(), entry.getKey()); - assertEquals( OnePassingKoan.class.getDeclaredMethod("koan").toString(), - KoanMethod.getInstance(entry.getValue().get("koan")).getMethod().toString()); - } - - @Test /** Ensures that koans are ready for packaging & distribution */ - public void testKoanSuiteRunner_firstKoanFail() throws Exception { - setRealPath(); - final KoanSuiteResult[] result = new KoanSuiteResult[]{null}; - final SuitePresenter presenter = new SuitePresenter(){ - public void displayResult(KoanSuiteResult actualAppResult) { - // don't display, capture them so we can analyze and ensure first failure is reported - result[0] = actualAppResult; - } - }; - doAsIfInProd(new Runnable(){ - public void run(){ - new RunKoans(presenter, PathToEnlightenment.getPathToEnlightment()).run(null); - } - }); - String firstSuiteClassRan = PathToEnlightenment.getPathToEnlightment() - .iterator().next().getValue().entrySet().iterator().next().getKey(); - assertEquals(result[0].getFailingCase(), firstSuiteClassRan.substring(firstSuiteClassRan.lastIndexOf(".") + 1)); - } - - @Test /** Ensures that koans are ready for packaging & distribution */ - public void testKoanSuiteRunner_allKoansFail() throws Exception { - setRealPath(); - final KoanSuiteResult[] result = new KoanSuiteResult[]{null}; - final SuitePresenter presenter = new SuitePresenter(){ - public void displayResult(KoanSuiteResult actualAppResult) { - // don't display, capture them so we can analyze and ensure first failure is reported - result[0] = actualAppResult; - } - }; - doAsIfInProd(new Runnable(){ - public void run(){ - new RunKoans(presenter, PathToEnlightenment.getPathToEnlightment()).run(null); - } - }); - String message = "Not all koans need solving! Each should ship in a failing state."; - assertEquals(message, 0, result[0].getNumberPassing()); - // make sure test was actually useful (ie something actually failed) - assertNotNull(result[0].getFailingCase()); - } - - private void doAsIfInProd(Runnable runnable) { - DirectoryManager.setDirectorySet(new Production()); - runnable.run(); - } - - @Test - public void testLineExceptionIsThrownAtIsHintedAt() throws Exception { - stubAllKoans(Arrays.asList(new BlowUpOnLineTen())); - new CommandLineArgumentRunner().run(); - assertSystemOutContains("Line 10"); - assertSystemOutDoesntContain("Line 11"); - } - - @Test - public void testLineExceptionIsThrownAtIsHintedAtEvenIfThrownFromSuperClass() throws Exception { - stubAllKoans(Arrays.asList(new BlowUpOnLineEleven())); - new CommandLineArgumentRunner().run(); - assertSystemOutContains("Line 11"); - assertSystemOutDoesntContain("Line 10"); - } - - @Test - public void testWarningFromPlacingExpecationOnWrongSide() throws Throwable { - final String[] message = new String[1]; - stubAllKoans(Arrays.asList(new WrongExpectationOrderKoan())); - Logger.getLogger(CommandLineArgumentRunner.class.getSimpleName()).addHandler( - new Handler() { - @Override - public void close() throws SecurityException { - } - - @Override - public void flush() { - } - - @Override - public void publish(LogRecord arg0) { - message[0] = arg0.getMessage(); - } - }); - new CommandLineArgumentRunner(new CommandLineArgumentBuilder()).run(); - assertEquals( - new StringBuilder( - WrongExpectationOrderKoan.class.getSimpleName()) - .append(".expectationOnLeft ") - .append(EXPECTATION_LEFT_ARG).toString(), message[0]); - } - - @Test - public void testNoWarningFromPlacingExpecationOnRightSide() - throws Throwable { - stubAllKoans(Arrays.asList(new OnePassingKoan())); - Logger.getLogger(CommandLineArgumentRunner.class.getSimpleName()).addHandler( - new Handler() { - @Override - public void close() throws SecurityException { - } - - @Override - public void flush() { - } - - @Override - public void publish(LogRecord arg0) { - fail("No logging necessary when koan passes, otherwise - logging is new, adjust accordingly."); - } - }); - new CommandLineArgumentRunner().run(); - } - - public static class WrongExpectationOrderKoan { - @Koan - public void expectationOnLeft() { - com.sandwich.util.Assert.assertEquals(__, false); - } - } -} diff --git a/koans-tests/data/src/com/sandwich/koan/runner/ConsolePresenterTest.java b/koans-tests/data/src/com/sandwich/koan/runner/ConsolePresenterTest.java deleted file mode 100644 index 748f9818..00000000 --- a/koans-tests/data/src/com/sandwich/koan/runner/ConsolePresenterTest.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.sandwich.koan.runner; - -import static com.sandwich.koan.constant.KoanConstants.ALL_SUCCEEDED; -import static com.sandwich.koan.constant.KoanConstants.COMPLETE_CHAR; -import static com.sandwich.koan.constant.KoanConstants.CONQUERED; -import static com.sandwich.koan.constant.KoanConstants.ENCOURAGEMENT; -import static com.sandwich.koan.constant.KoanConstants.EOL; -import static com.sandwich.koan.constant.KoanConstants.FAILING_SUITES; -import static com.sandwich.koan.constant.KoanConstants.INCOMPLETE_CHAR; -import static com.sandwich.koan.constant.KoanConstants.INVESTIGATE_IN_THE; -import static com.sandwich.koan.constant.KoanConstants.KOAN; -import static com.sandwich.koan.constant.KoanConstants.OUT_OF; -import static com.sandwich.koan.constant.KoanConstants.PASSING_SUITES; -import static com.sandwich.koan.constant.KoanConstants.PROGRESS; -import static com.sandwich.koan.constant.KoanConstants.PROGRESS_BAR_START; -import static com.sandwich.koan.constant.KoanConstants.PROGRESS_BAR_WIDTH; -import static com.sandwich.koan.constant.KoanConstants.WHATS_WRONG; - -import java.util.Arrays; - -import org.junit.Test; - -import com.sandwich.koan.cmdline.CommandLineArgumentRunner; -import com.sandwich.koan.cmdline.CommandLineArgumentBuilder; -import com.sandwich.koan.constant.KoanConstants; -import com.sandwich.koan.path.CommandLineTestCase; -import com.sandwich.koan.suite.OneFailingKoan; -import com.sandwich.koan.suite.OneFailingKoanDifferentName; -import com.sandwich.koan.suite.OnePassingKoan; -import com.sandwich.koan.suite.OnePassingKoanDifferentName; - -public class ConsolePresenterTest extends CommandLineTestCase { - - @Test - public void hintPresentation() throws Throwable { - stubAllKoans(Arrays.asList(new OneFailingKoanDifferentName())); - new CommandLineArgumentRunner(new CommandLineArgumentBuilder()).run(); - assertSystemOutContains(new StringBuilder( - INVESTIGATE_IN_THE).append( - " ").append( - OneFailingKoanDifferentName.class.getSimpleName()).append( - " class's ").append( - OneFailingKoanDifferentName.class.getDeclaredMethod("koanMethod").getName()).append( - " method.").toString()); - assertSystemOutContains("Line 11 may offer a clue as to how you may progress, now make haste!"); - } - - @Test // uncomment enableEncouragement @ the top of ConsolePresenter class - public void encouragement() throws Throwable { - if(KoanConstants.ENABLE_ENCOURAGEMENT){ - stubAllKoans(Arrays.asList(new Class[] { - OneFailingKoan.class })); - new CommandLineArgumentRunner().run(); - assertSystemOutContains(new StringBuilder( - CONQUERED).append( - " 0 ").append( - OUT_OF).append( - " 1 ").append( - KOAN).append( - "! ").append( - ENCOURAGEMENT).toString()); - assertSystemOutDoesntContain(ALL_SUCCEEDED); - } - } - - @Test - public void testOneHundredPercentSuccessReward() throws Throwable { - stubAllKoans(Arrays.asList(new Class[] { - OnePassingKoan.class })); - new CommandLineArgumentRunner().run(); - assertSystemOutContains(ALL_SUCCEEDED); - assertSystemOutDoesntContain(CONQUERED); - assertSystemOutDoesntContain(ENCOURAGEMENT); - } - - @Test - public void passingSuites() throws Throwable { - stubAllKoans(Arrays.asList(new OnePassingKoan(), new OnePassingKoanDifferentName())); - new CommandLineArgumentRunner().run(); - assertSystemOutContains(PASSING_SUITES+" OnePassingKoan, OnePassingKoanDifferentName"); - } - - @Test - public void failingSuites() throws Throwable { - stubAllKoans(Arrays.asList(new OneFailingKoan(), new OneFailingKoanDifferentName())); - new CommandLineArgumentRunner().run(); - assertSystemOutContains(FAILING_SUITES+" OneFailingKoan, OneFailingKoanDifferentName"); - } - - @Test - public void failingAndPassingSuites() throws Throwable { - stubAllKoans(Arrays.asList(new OnePassingKoan(), new OneFailingKoan())); - new CommandLineArgumentRunner().run(); - assertSystemOutContains(PASSING_SUITES+" OnePassingKoan"+EOL+ - FAILING_SUITES+" OneFailingKoan"); - } - - @Test - public void progressAllPassing() throws Throwable { - stubAllKoans(Arrays.asList(new OnePassingKoan())); - new CommandLineArgumentRunner().run(); - StringBuilder sb = new StringBuilder(PROGRESS).append(" ") - .append(PROGRESS_BAR_START); - for(int i = 0; i < PROGRESS_BAR_WIDTH; i++){ // 100% success - sb.append(COMPLETE_CHAR); - } - sb.append("] 1/1").append(EOL); - assertSystemOutContains(sb.toString()); - } - - @Test - public void progressAllFailing() throws Throwable { - stubAllKoans(Arrays.asList(new OneFailingKoan(), new OneFailingKoanDifferentName())); - new CommandLineArgumentRunner().run(); - StringBuilder sb = new StringBuilder( - PROGRESS).append(" ").append( - PROGRESS_BAR_START); - for(int i = 0; i < PROGRESS_BAR_WIDTH; i++){ // 100% failed - sb.append(INCOMPLETE_CHAR); - } - sb.append("] 0/2").append(EOL); - assertSystemOutContains(sb.toString()); - } - - @Test - public void progressFiftyFifty() throws Throwable { - stubAllKoans(Arrays.asList(new OnePassingKoan(), new OneFailingKoan())); - new CommandLineArgumentRunner().run(); - StringBuilder sb = new StringBuilder( - PROGRESS).append(" ").append( - PROGRESS_BAR_START); - for(int i = 0; i < PROGRESS_BAR_WIDTH / 2; i++){ // 50% succeeded - sb.append(COMPLETE_CHAR); - } - for(int i = 0; i < PROGRESS_BAR_WIDTH / 2; i++){ // 50% failed - sb.append(INCOMPLETE_CHAR); - } - sb.append("] 1/2").append(EOL); - assertSystemOutContains(sb.toString()); - } - - @Test - public void whatWentWrongExplanation() throws Throwable { - stubAllKoans(Arrays.asList(new OneFailingKoan())); - new CommandLineArgumentRunner().run(); - assertSystemOutContains(new StringBuilder( - WHATS_WRONG).append( - EOL).append( - "expected: but was:").toString()); - } - -} diff --git a/koans-tests/data/src/com/sandwich/koan/runner/KoanSuiteRunnerTest.java b/koans-tests/data/src/com/sandwich/koan/runner/KoanSuiteRunnerTest.java deleted file mode 100644 index aa106442..00000000 --- a/koans-tests/data/src/com/sandwich/koan/runner/KoanSuiteRunnerTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.sandwich.koan.runner; - - -public class KoanSuiteRunnerTest { - -// @Test -// public void testRunSortsAndInvokesByComparableImplInArgumentType() throws Exception { -// // test depends on this - this is to ensure rest of test is true -// { -//// assertEquals(-1, ArgumentType.TEST.compareTo(ArgumentType.CLASS_ARG)); -// } -// Map args = new LinkedHashMap(); -// final boolean[] called = {false, false, false}; -// args.put(ArgumentType.TEST, new CommandLineArgument(ArgumentType.TEST, null){ -// @Override public void run(){ -// assertFalse(called[0]); -// assertFalse(called[1]); -// assertFalse(called[2]); -// called[0] = true; -// } -// }); -// args.put(ArgumentType.CLASS_ARG, new CommandLineArgument(ArgumentType.CLASS_ARG, null){ -// @Override public void run(){ -// assertTrue(called[0]); -// assertFalse(called[1]); -// assertFalse(called[2]); -// called[1] = true; -// } -// }); -// args.put(ArgumentType.RUN_KOANS, new CommandLineArgument(ArgumentType.RUN_KOANS, null){ -// @Override public void run(){ -// assertTrue(called[0]); -// assertTrue(called[1]); -// assertFalse(called[2]); -// called[2] = true; -// } -// }); -// new CommandLineArgumentRunner(args).run(); -// assertTrue(called[0]); -// assertTrue(called[1]); -// assertTrue(called[2]); -// } - -} diff --git a/koans-tests/data/src/com/sandwich/koan/runner/ui/AbstractSuitePresenterTest.java b/koans-tests/data/src/com/sandwich/koan/runner/ui/AbstractSuitePresenterTest.java deleted file mode 100644 index 1e22df3c..00000000 --- a/koans-tests/data/src/com/sandwich/koan/runner/ui/AbstractSuitePresenterTest.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.sandwich.koan.runner.ui; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import java.util.Arrays; - -import org.junit.Test; - -import com.sandwich.koan.KoanMethod; -import com.sandwich.koan.result.KoanMethodResult; -import com.sandwich.koan.result.KoanSuiteResult; -import com.sandwich.koan.result.KoanSuiteResult.KoanResultBuilder; -import com.sandwich.koan.suite.OneFailingKoan; -import com.sandwich.koan.ui.AbstractSuitePresenter; - -public class AbstractSuitePresenterTest { - - @Test - public void testForwardingOneHundredPercentSuccess() throws Exception { - final int state[] = new int[]{0}; - AbstractSuitePresenter presenter = new AbstractSuitePresenter() { - public void displayAllSuccess(KoanSuiteResult result) { - assertEquals(0, state[0]); - state[0] = 1; - } - public void displayChart(KoanSuiteResult result) { - assertEquals(1, state[0]); - state[0] = 2; - } - public void displayPassingFailing(KoanSuiteResult result) { - assertEquals(2, state[0]); - state[0] = 3; - } - public void displayHeader(KoanSuiteResult result) { - assertEquals(3, state[0]); - state[0] = 4; - } - public void displayOneOrMoreFailure(KoanSuiteResult result) { - fail(); - } - }; - - KoanSuiteResult kr = new KoanResultBuilder().build(); - presenter.displayResult(kr); - assertEquals(4, state[0]); - } - - @Test - public void testForwardingOneOrMoreFails() throws Exception { - final int state[] = new int[]{0}; - AbstractSuitePresenter presenter = new AbstractSuitePresenter() { - public void displayOneOrMoreFailure(KoanSuiteResult result) { - assertEquals(0, state[0]); - state[0] = 1; - } - public void displayChart(KoanSuiteResult result) { - assertEquals(1, state[0]); - state[0] = 2; - } - public void displayPassingFailing(KoanSuiteResult result) { - assertEquals(2, state[0]); - state[0] = 3; - } - public void displayHeader(KoanSuiteResult result) { - assertEquals(3, state[0]); - state[0] = 4; - } - public void displayAllSuccess(KoanSuiteResult result) { - fail(); - } - }; - - KoanSuiteResult kr = new KoanResultBuilder().remainingCases(Arrays.asList(OneFailingKoan.class.getSimpleName()) - ).methodResult(new KoanMethodResult(KoanMethod.getInstance("", OneFailingKoan.class.getDeclaredMethods()[0]), - "", "")).build(); - presenter.displayResult(kr); - assertEquals(4, state[0]); - } -} diff --git a/koans-tests/data/src/com/sandwich/koan/suite/BlowUpOnLineTen.java b/koans-tests/data/src/com/sandwich/koan/suite/BlowUpOnLineTen.java deleted file mode 100644 index 331e54d3..00000000 --- a/koans-tests/data/src/com/sandwich/koan/suite/BlowUpOnLineTen.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.sandwich.koan.suite; - -import com.sandwich.koan.Koan; - -public class BlowUpOnLineTen extends BlowUpOnLineEleven { - // gotta blow up on line 10 thus the two spaces - // - @Koan - public void blowUpOnLineTen(){ - super.blowUpOnLineEleven(); - } -} diff --git a/koans-tests/data/src/com/sandwich/util/KoanComparatorTest.java b/koans-tests/data/src/com/sandwich/util/KoanComparatorTest.java deleted file mode 100644 index 78473088..00000000 --- a/koans-tests/data/src/com/sandwich/util/KoanComparatorTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.sandwich.util; - -import static org.junit.Assert.assertSame; - -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.Test; - -import com.sandwich.koan.Koan; -import com.sandwich.koan.KoanMethod; -import com.sandwich.koan.path.CommandLineTestCase; - -public class KoanComparatorTest extends CommandLineTestCase { - - @Test - public void testThatKomparatorBombsWhenNotFound() throws Exception { - Method m = new Object(){ - @SuppressWarnings("unused") @Koan public void someMethod(){} - }.getClass().getDeclaredMethod("someMethod"); - KoanComparator comparator = new KoanComparator("meh"); - try{ - comparator.compare(KoanMethod.getInstance("2",m), KoanMethod.getInstance("1",m)); - }catch(RuntimeException fileNotFound){} - } - - @Test - public void testComparatorRanksByOrder() throws Exception { - Class clazz = new Object(){ - @SuppressWarnings("unused") @Koan public void someMethodOne(){} - @SuppressWarnings("unused") @Koan public void someMethodTwo(){} - }.getClass(); - KoanMethod m1 = KoanMethod.getInstance("",clazz.getDeclaredMethod("someMethodOne")); - KoanMethod m2 = KoanMethod.getInstance("",clazz.getDeclaredMethod("someMethodTwo")); - List methods = Arrays.asList(m2,m1); - Collections.sort(methods, new KoanComparator("someMethodOne","someMethodTwo")); - assertSame(m1,methods.get(0)); - assertSame(m2,methods.get(1)); - } -} - diff --git a/koans-tests/data/src/com/sandwich/util/io/DirectoryManagerTest.java b/koans-tests/data/src/com/sandwich/util/io/DirectoryManagerTest.java deleted file mode 100644 index d97f5ef6..00000000 --- a/koans-tests/data/src/com/sandwich/util/io/DirectoryManagerTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.sandwich.util.io; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -import com.sandwich.koan.path.CommandLineTestCase; -import com.sandwich.util.io.directories.DirectoryManager; - - -public class DirectoryManagerTest extends CommandLineTestCase { - - @Test - public void testFileSeparatorInjection_happyPath() throws Exception { - assertEquals("/home/wilford/liberty", DirectoryManager.injectFileSystemSeparators( - "home", "wilford", "liberty")); - } - - @Test - public void testFileSeparatorInjection_separatorsAtBeginning() throws Exception { - assertEquals("/home/wilford/liberty", DirectoryManager.injectFileSystemSeparators( - "/home", "/wilford", "/liberty")); - } - - @Test - public void testFileSeparatorInjection_separatorsAtEnding() throws Exception { - assertEquals("/home/wilford/liberty", DirectoryManager.injectFileSystemSeparators( - "home/", "wilford/", "liberty/")); - } - - @Test - public void testFileSeparatorInjection_separatorsAtBeginningAndEnding() throws Exception { - assertEquals("/home/wilford/liberty", DirectoryManager.injectFileSystemSeparators( - "home/", "wilford/", "/liberty/")); - } - - @Test - public void testFileSeparatorInjection_separatorsInMiddle() throws Exception { - assertEquals("/home/wilford/liberty", DirectoryManager.injectFileSystemSeparators( - "home", "wilford/liberty")); - } -} diff --git a/koans-tests/data/src/com/sandwich/util/io/FileMonitorTest.java b/koans-tests/data/src/com/sandwich/util/io/FileMonitorTest.java deleted file mode 100644 index 2f3d10e9..00000000 --- a/koans-tests/data/src/com/sandwich/util/io/FileMonitorTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.sandwich.util.io; - -import org.junit.After; -import org.junit.Before; - -import com.sandwich.koan.constant.KoanConstants; - - -public class FileMonitorTest { - - String SAMPLE_DIR = FileUtils.makeAbsoluteRelativeTo(KoanConstants.PROJ_TESTS_FOLDER); - FileMonitor monitor; - - @Before - public void createInstance() throws Exception{ - monitor = FileMonitorFactory.getInstance(SAMPLE_DIR); - } - - @After - public void destroyInstance(){ - monitor.close(); - } - -} diff --git a/koans-tests/data/src/easymock-3.0.jar b/koans-tests/data/src/easymock-3.0.jar deleted file mode 100644 index f6d7a3f9..00000000 Binary files a/koans-tests/data/src/easymock-3.0.jar and /dev/null differ diff --git a/koans-tests/test/cglib-nodep-2.2.jar b/koans-tests/test/cglib-nodep-2.2.jar deleted file mode 100755 index ed07cb50..00000000 Binary files a/koans-tests/test/cglib-nodep-2.2.jar and /dev/null differ diff --git a/koans-tests/test/com/sandwich/koan/KoansResultTest.java b/koans-tests/test/com/sandwich/koan/KoansResultTest.java deleted file mode 100755 index b2011ebd..00000000 --- a/koans-tests/test/com/sandwich/koan/KoansResultTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.sandwich.koan; - -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; - -import org.junit.Test; - -import com.sandwich.koan.result.KoanMethodResult; -import com.sandwich.koan.result.KoanSuiteResult; -import com.sandwich.koan.result.KoanSuiteResult.KoanResultBuilder; -import com.sandwich.koan.suite.OneFailingKoan; - -public class KoansResultTest { - - @Test - public void testToString() throws Exception { - KoanResultBuilder builder = new KoanResultBuilder() - .remainingCases(Arrays.asList(OneFailingKoan.class.getSimpleName())) - .methodResult(new KoanMethodResult(KoanMethod.getInstance("", OneFailingKoan.class.getDeclaredMethods()[0]), "msg", "2")) - .level("1") - .numberPassing(3); - KoanSuiteResult result = builder.build(); - String string = result.toString(); - assertTrue(string.contains("1")); - assertTrue(string.contains("2")); - assertTrue(string.contains("3")); - assertTrue(string.contains(OneFailingKoan.class.getSimpleName())); - assertTrue(string.contains(OneFailingKoan.class.getDeclaredMethods()[0].getName())); - assertTrue(string.contains("msg")); - } - -} diff --git a/koans-tests/test/com/sandwich/koan/TestUtils.java b/koans-tests/test/com/sandwich/koan/TestUtils.java deleted file mode 100755 index 9cbc999b..00000000 --- a/koans-tests/test/com/sandwich/koan/TestUtils.java +++ /dev/null @@ -1,315 +0,0 @@ -package com.sandwich.koan; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.PrintStream; -import java.io.Serializable; -import java.lang.Thread.State; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Random; - -import com.sandwich.koan.constant.KoanConstants; - -public class TestUtils { - - private static Random random = new Random(System.currentTimeMillis()); - public static final String EOL = System.getProperty("line.separator"); - public static final int MAX_UNIQUE_CHARS = 1000; - - public static long sizeInBytes(Serializable... objects) { - byte[] ba = null; - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(baos); - oos.writeObject(objects); - oos.close(); - ba = baos.toByteArray(); - baos.close(); - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } - return ba.length; - } - - public static void chewUpVM(){ - chewUpVM(100); - } - - public static void chewUpVM(long forApproxHowLongInMs){ - long start = System.currentTimeMillis(); - while(System.currentTimeMillis() - start > forApproxHowLongInMs){new Object();} - } - - public static String getRandomText(int i) { - return getRandomText(i,false); - } - - public static String getRandomText(int i, boolean enforceUnique) { - if(enforceUnique && i > MAX_UNIQUE_CHARS){ - throw new RuntimeException("call getRandomText w/ a value under 1000 if u wish to enforce unique"); - } - StringBuilder sb = new StringBuilder(); - int j = 0; - while(j < i){ - char nextChar = (char)random.nextInt(); - if(enforceUnique && sb.indexOf(String.valueOf(nextChar)) != -1){ - continue; - } - sb.append(nextChar); - j++; - } - return sb.toString(); - } - - public static long time(Runnable runnable) { - long start = System.currentTimeMillis(); - runnable.run(); - return System.currentTimeMillis() - start; - } - - public static void forEachLine(String text, ArgRunner runnable){ - String[] strings = text.split(KoanConstants.EOLS, -1); - for(String line : strings){ - runnable.run(line); - } - } - - @SuppressWarnings("unchecked") - public static T serialize(T t) { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(baos); - oos.writeObject(t); - oos.close(); - final byte[] ba = baos.toByteArray(); - baos.close(); - ByteArrayInputStream bais = new ByteArrayInputStream(ba); - ObjectInputStream ois = new ObjectInputStream(bais); - Object returnValue = ois.readObject(); - ois.close(); - return (T) returnValue; - } catch (IOException e) { - throw new RuntimeException(e); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - - @SuppressWarnings("unchecked") - public static T getValue(String fieldName, Object target) { - Class clazz = target instanceof Class ? (Class)target : target.getClass(); - Field field = getField(clazz, fieldName); - try { - boolean wasAccessible = field.isAccessible(); - field.setAccessible(true); - Object value = field.get(target); - field.setAccessible(wasAccessible); - return (T)value; - } catch (IllegalArgumentException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - public static void setValue(String fieldName, Object value, Object target) { - Class clazz = target instanceof Class ? (Class)target : target.getClass(); - Field field = getField(clazz, fieldName); - try { - boolean wasAccessible = field.isAccessible(); - field.setAccessible(true); - field.set(target, value); - field.setAccessible(wasAccessible); - return; - } catch (IllegalArgumentException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - public static Field getField(Class clazz, String fieldName){ - String className = clazz.getName(); - do{ - for(Field field : clazz.getDeclaredFields()){ - if(field.getName().equals(fieldName)){ - return field; - } - } - clazz = (Class)clazz.getSuperclass(); - }while(clazz != null); - throw new IllegalArgumentException(fieldName+" was not found in class: "+className); - } - - /** - * Used to simulate multiple threads accessing the same method whilst busy. - * Will fail in insufficiently concurrent scenarios. - * - * @param assertion - * @param rs - * @throws InterruptedException - */ - public static void doSimultaneouslyAndRepetitively(TwoObjectAssertion assertion, final Runnable... rs) throws InterruptedException{ - assertTrue("Whats the point of testing for synchronization w/ less than two threads?", rs.length > 1); - final int[] c = new int[]{0}; - int startRuns = rs.length + 9; - final Thread[] threads = new Thread[rs.length]; - int totalRuns = 0; - for(int i = 0; i < rs.length; i++){ - final int it = i; - final int startRunsTemp = startRuns; - totalRuns += startRunsTemp; - startRuns--; - threads[i] = new Thread(new Runnable(){ - public void run() { - Runnable runnable = rs[it]; - for(int j = 0; j < startRunsTemp; j++){ - runnable.run(); - c[0]++; - } - } - }); - threads[i].start(); - } - allowThreadsToFinish(threads); - assertion.assertOn("Threads died before they could finish.", totalRuns, c[0]); - } - - public static void doSimultaneouslyAndRepetitively(TwoObjectAssertion assertion, - Class anticipatedExceptionClass, - final Runnable... rs) throws InterruptedException{ - PrintStream temp = System.err; - try { - ByteArrayOutputStream sysErr = new ByteArrayOutputStream(); - System.setErr(new PrintStream(sysErr)); - doSimultaneouslyAndRepetitively(assertion, rs); - String errors = sysErr.toString(); - int i = rs.length; - assertFalse(errors.contains("Thread-"+(i+1))); - for(;i > 0; i--){ - assertTrue(errors.contains("Thread-"+i+"\" "+anticipatedExceptionClass.getName())); - } - } finally { - System.setErr(new PrintStream(temp)); - } - } - - /** - * Will lock Thread accessible when called from Thread via repetitive sleep - * calls until the passed in threads are terminated. Generally used to keep - * the test exec thread alive while the other threads are still working. - * - * @param threads - * @throws InterruptedException - */ - public static void allowThreadsToFinish(final Thread[] threads) - throws InterruptedException { - boolean threadsDone = false; - while(!threadsDone){ - threadsDone = true; - for(Thread thread : threads){ - if(thread.getState() != State.TERMINATED){ - Thread.sleep(10); - threadsDone = false; - } - } - } - } - - public static void assertEqualsContractEnforcement(Object obj0, Object obj1, Object obj2){ - assertFalse(obj0.equals(null)); - assertFalse(obj1.equals(null)); - assertFalse(obj2.equals(null)); - - assertEquals(obj0, obj0); - assertEquals(obj1, obj1); - assertEquals(obj2, obj2); - - assertEquals(obj0, obj1); - assertEquals(obj1, obj0); - - assertEquals(obj1, obj2); - assertEquals(obj2, obj1); - - assertEquals(obj2, obj0); - assertEquals(obj0, obj2); - } - - public static void assertHashCodeContractEnforcement(Object obj0, Object obj1, Object obj2){ - assertEquals(obj0.hashCode(), obj0.hashCode()); - assertEquals(obj1.hashCode(), obj1.hashCode()); - assertEquals(obj2.hashCode(), obj2.hashCode()); - - assertEquals(obj0.hashCode(), obj1.hashCode()); - assertEquals(obj1.hashCode(), obj0.hashCode()); - - assertEquals(obj1.hashCode(), obj2.hashCode()); - assertEquals(obj2.hashCode(), obj1.hashCode()); - - assertEquals(obj2.hashCode(), obj0.hashCode()); - assertEquals(obj0.hashCode(), obj2.hashCode()); - } - - public static void assertHashCodeEqualsContracts(Object obj0, Object obj1, Object obj2){ - assertEqualsContractEnforcement(obj0, obj1, obj2); - assertHashCodeContractEnforcement(obj0, obj1, obj2); - } - - public static Object invokePrivate( - String name, - final Object obj, - Object[] objects) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { - Method method = null; - Class objClass = obj instanceof Class ? (Class)obj : obj.getClass(); - while(objClass != null && method == null){ - for(Method m : objClass.getDeclaredMethods()){ - if (name.equals(m.getName()) && - (objects == null && m.getParameterTypes().length == 0) || - (objects != null && objects.length == m.getParameterTypes().length)){ - method = m; - break; - } - } - objClass = objClass.getSuperclass(); - } - if(method == null){ - throw new IllegalArgumentException("method with name "+name+" was not found, check class definition."); - } - final boolean wasAccessible = method.isAccessible(); - try{ - method.setAccessible(true); - return method.invoke(obj, objects); - }finally{ - if(wasAccessible != method.isAccessible()){ - method.setAccessible(wasAccessible); - } - } - } - - public static interface ArgRunner { - public void run(T t); - } - - public static interface TwoObjectAssertion { - /** - * Message to display when assertion fails. Followed by 2 objects to - * assertOn. Useful for reusing in object composition tests or a - * chain of command pattern based assertion chain. - * - * @param msg - * @param o0 - * @param o1 - */ - void assertOn(String msg, Object o0, Object o1); - } - -} diff --git a/koans-tests/test/com/sandwich/koan/TestUtilsTest.java b/koans-tests/test/com/sandwich/koan/TestUtilsTest.java deleted file mode 100755 index a5a66b8c..00000000 --- a/koans-tests/test/com/sandwich/koan/TestUtilsTest.java +++ /dev/null @@ -1,436 +0,0 @@ -package com.sandwich.koan; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; - -import org.easymock.EasyMock; -import org.junit.Test; - -import com.sandwich.koan.TestUtils.ArgRunner; -import com.sandwich.koan.TestUtils.TwoObjectAssertion; - -public class TestUtilsTest { - - @Test - public void testEqualsContractEnforcement_integerIdentity_happyPath() throws Exception { - Integer one = 1; - TestUtils.assertEqualsContractEnforcement(one, one, one); - } - - @Test - public void testEqualsContractEnforcement_integerObject_happyPath() throws Exception { - TestUtils.assertEqualsContractEnforcement(new Integer(1), new Integer(1), new Integer(1)); - } - - @Test(expected=AssertionError.class) - public void testEqualsContractEnforcement_integer_exceptionPath0() throws Exception { - TestUtils.assertEqualsContractEnforcement(new Integer(2), new Integer(1), new Integer(1)); - } - - @Test(expected=AssertionError.class) - public void testEqualsContractEnforcement_integer_exceptionPath1() throws Exception { - TestUtils.assertEqualsContractEnforcement(new Integer(1), new Integer(2), new Integer(1)); - } - - @Test(expected=AssertionError.class) - public void testEqualsContractEnforcement_integer_exceptionPath() throws Exception { - TestUtils.assertEqualsContractEnforcement(new Integer(1), new Integer(1), new Integer(2)); - } - - @Test - public void testHashCodeContractEnforcement_integerIdentity_happyPath() throws Exception { - Integer one = 1; - TestUtils.assertHashCodeContractEnforcement(one, one, one); - } - - @Test - public void testHashCodeContractEnforcement_integerObject_happyPath() throws Exception { - TestUtils.assertHashCodeContractEnforcement(new Integer(1), new Integer(1), new Integer(1)); - } - - @Test(expected=AssertionError.class) - public void testHashCodeContractEnforcement_integer_exceptionPath0() throws Exception { - TestUtils.assertHashCodeContractEnforcement(new Integer(2), new Integer(1), new Integer(1)); - } - - @Test(expected=AssertionError.class) - public void testHashCodeContractEnforcement_integer_exceptionPath1() throws Exception { - TestUtils.assertHashCodeContractEnforcement(new Integer(1), new Integer(2), new Integer(1)); - } - - @Test(expected=AssertionError.class) - public void testHashCodeContractEnforcement_integer_exceptionPath() throws Exception { - TestUtils.assertHashCodeContractEnforcement(new Integer(1), new Integer(1), new Integer(2)); - } - - @Test - public void testHashCodeContractEnforcement_testObj_happyPath() throws Exception { - TestUtils.assertHashCodeContractEnforcement(new ContractEnforcementBase(), - new ContractEnforcementBase(), - new ContractEnforcementBase()); - } - - @Test(expected=AssertionError.class) - public void testHashCodeContractEnforcement_testObj_exceptionPath0() throws Exception { - TestUtils.assertHashCodeContractEnforcement(new ContractEnforcementSubclass(), - new ContractEnforcementBase(), - new ContractEnforcementBase()); - } - - @Test(expected=AssertionError.class) - public void testHashCodeContractEnforcement_testObj_exceptionPath1() throws Exception { - TestUtils.assertHashCodeContractEnforcement(new ContractEnforcementBase(), - new ContractEnforcementSubclass(), - new ContractEnforcementBase()); - } - - @Test(expected=AssertionError.class) - public void testHashCodeContractEnforcement_testObj_exceptionPath2() throws Exception { - TestUtils.assertHashCodeContractEnforcement(new ContractEnforcementBase(), - new ContractEnforcementBase(), - new ContractEnforcementSubclass()); - } - - @Test - public void testEqualsContractEnforcement_testObj_happyPath() throws Exception { - TestUtils.assertEqualsContractEnforcement(new ContractEnforcementBase(), - new ContractEnforcementBase(), - new ContractEnforcementBase()); - } - - @Test(expected=AssertionError.class) - public void testEqualsContractEnforcement_testObj_exceptionPath0() throws Exception { - TestUtils.assertEqualsContractEnforcement(new ContractEnforcementSubclass(), - new ContractEnforcementBase(), - new ContractEnforcementBase()); - } - - @Test(expected=AssertionError.class) - public void testEqualsContractEnforcement_testObj_exceptionPath1() throws Exception { - TestUtils.assertEqualsContractEnforcement(new ContractEnforcementBase(), - new ContractEnforcementSubclass(), - new ContractEnforcementBase()); - } - - @Test(expected=AssertionError.class) - public void testEqualsContractEnforcement_testObj_exceptionPath2() throws Exception { - TestUtils.assertEqualsContractEnforcement(new ContractEnforcementBase(), - new ContractEnforcementBase(), - new ContractEnforcementSubclass()); - } - - static class ContractEnforcementBase { - int i = 1; - @Override - public boolean equals(Object o){ - return o instanceof ContractEnforcementBase && i == ((ContractEnforcementBase)o).i; - } - @Override - public int hashCode(){ - return 1; - } - } - - static class ContractEnforcementSubclass extends ContractEnforcementBase { - int j = 2; - @Override - public boolean equals(Object o){ - return o instanceof ContractEnforcementSubclass - && i == ((ContractEnforcementSubclass)o).i - && j == ((ContractEnforcementSubclass)o).j; - } - @Override - public int hashCode(){ - return 2; - } - } - - @Test(expected=AssertionError.class, timeout=1000) - public void testEqualsConcurrency_concurrentAccessFails() throws Exception { - final PrintStream temp = System.err; - try { - ByteArrayOutputStream sysErr = new ByteArrayOutputStream(); - System.setErr(new PrintStream(sysErr)); - TestUtils.doSimultaneouslyAndRepetitively(new TwoObjectAssertion() { - public void assertOn(String msg, Object o0, Object o1) { - assertEquals(msg, o0, o1); - } - }, IllegalMonitorStateException.class, - new Runnable() { - public void run() { - waste(10); - } - }, new Runnable() { - public void run() { - waste(11); - } - }, new Runnable() { - public void run() { - waste(3); - } - }); - String errors = sysErr.toString(); - assertTrue(errors.contains("Thread-1\" java.lang.IllegalMonitorStateException")); - assertTrue(errors.contains("Thread-2\" java.lang.IllegalMonitorStateException")); - assertTrue(errors.contains("Thread-3\" java.lang.IllegalMonitorStateException")); - assertFalse(errors.contains("Thread-4")); - } finally { - System.setErr(new PrintStream(temp)); - } - } - - @Test(expected=java.lang.AssertionError.class, timeout=500) - public void testEqualsConcurrency_concurrentAccessFails_assertIllegalMonitorStateException() throws Exception { - TestUtils.doSimultaneouslyAndRepetitively(new TwoObjectAssertion() { - public void assertOn(String msg, Object o0, Object o1) { - assertEquals(msg, o0, o1); - } - }, - IllegalMonitorStateException.class, - new Runnable() { - public void run() { - waste(10); - } - }, - new Runnable() { - public void run() { - waste(11); - } - }, - new Runnable() { - public void run() { - waste(3); - } - }); - } - - @Test - public void testEqualsConcurrency() throws Exception { - TestUtils.doSimultaneouslyAndRepetitively(new TwoObjectAssertion() { - public void assertOn(String msg, Object o0, Object o1) { - assertEquals(msg, o0, o1); - } - }, new Runnable(){ - public void run() { - wasteSynchronized(10); - } - }, new Runnable(){ - public void run() { - wasteSynchronized(11); - } - }, new Runnable(){ - public void run() { - wasteSynchronized(3); - } - }); - } - - @Test - public void testEqualsConcurrency_II() throws Exception { - TestUtils.doSimultaneouslyAndRepetitively(new TwoObjectAssertion() { - public void assertOn(String msg, Object o0, Object o1) { - assertEquals(msg, o0, o1); - } - }, new Runnable(){ - public void run() { - wasteSynchronized(10); - } - }, new Runnable(){ - public void run() { - wasteSynchronized(11); - } - }, new Runnable(){ - public void run() { - wasteSynchronized(4); - } - }, new Runnable(){ - public void run() { - wasteSynchronized(6); - } - }); - } - - private void waste(int i) { - try { - wait(i); - } catch (InterruptedException e) { - fail(e.getMessage()); - } - } - - private int wasteSynchronized(int i) { synchronized(this){ - try { - wait(i); - } catch (InterruptedException e) { - fail(e.getMessage()); - } - return i; - }} - - @Test - public void testForEachLine_threeNewLines() throws Exception { - @SuppressWarnings("unchecked") - ArgRunner runner = EasyMock.createStrictMock(ArgRunner.class); - - runner.run(""); - EasyMock.expectLastCall(); - - runner.run(""); - EasyMock.expectLastCall(); - - runner.run(""); - EasyMock.expectLastCall(); - - runner.run(""); - EasyMock.expectLastCall(); - - EasyMock.replay(runner); - - TestUtils.forEachLine("\n\n\n", runner); - EasyMock.verify(runner); - } - - @Test - public void testForEachLine_spaceNewLineNewLine(){ - @SuppressWarnings("unchecked") - ArgRunner runner = EasyMock.createStrictMock(ArgRunner.class); - - runner.run(" "); - EasyMock.expectLastCall(); - - runner.run(""); - EasyMock.expectLastCall(); - - runner.run(""); - EasyMock.expectLastCall(); - - EasyMock.replay(runner); - - TestUtils.forEachLine(" \n\n", runner); - EasyMock.verify(runner); - } - - @Test - public void testForEachLine_newLineNewLineSpace(){ - @SuppressWarnings("unchecked") - ArgRunner runner = EasyMock.createStrictMock(ArgRunner.class); - - runner.run(""); - EasyMock.expectLastCall(); - - runner.run(""); - EasyMock.expectLastCall(); - - runner.run(" "); - EasyMock.expectLastCall(); - - EasyMock.replay(runner); - - TestUtils.forEachLine("\n\n ", runner); - EasyMock.verify(runner); - } - - @Test - public void testMixingBackslashRAndBackslashNNewLines() throws Exception { - @SuppressWarnings("unchecked") - ArgRunner runner = EasyMock.createStrictMock(ArgRunner.class); - - runner.run(""); - EasyMock.expectLastCall(); - - runner.run(" "); - EasyMock.expectLastCall(); - - runner.run(" "); - EasyMock.expectLastCall(); - - EasyMock.replay(runner); - - TestUtils.forEachLine("\r \n ", runner); // can mix and match \r and \n - EasyMock.verify(runner); - } - - @Test - public void testMixingBackslashNAndBackslashRNewLines() throws Exception { - @SuppressWarnings("unchecked") - ArgRunner runner = EasyMock.createStrictMock(ArgRunner.class); - - runner.run(""); - EasyMock.expectLastCall(); - - runner.run(" "); - EasyMock.expectLastCall(); - - runner.run(" "); - EasyMock.expectLastCall(); - - EasyMock.replay(runner); - - TestUtils.forEachLine("\n \r ", runner); // can mix and match \r and \n - EasyMock.verify(runner); - } - - @Test - public void testForEachLine_emptyString() throws Exception { - @SuppressWarnings("unchecked") - ArgRunner runner = EasyMock.createStrictMock(ArgRunner.class); - runner.run(""); - EasyMock.expectLastCall(); - - EasyMock.replay(runner); - - TestUtils.forEachLine("", runner); - EasyMock.verify(runner); - } - - @Test - public void testForEachLine_nothingThreeNewLinesSeperatedBy1Space() throws Exception { - @SuppressWarnings("unchecked") - ArgRunner runner = EasyMock.createStrictMock(ArgRunner.class); - - runner.run(""); - EasyMock.expectLastCall(); - - runner.run(" "); - EasyMock.expectLastCall(); - - runner.run(" "); - EasyMock.expectLastCall(); - - runner.run(""); - EasyMock.expectLastCall(); - - EasyMock.replay(runner); - - TestUtils.forEachLine("\n \n \n", runner); - EasyMock.verify(runner); - } - - @Test - public void testForEachLine_nothingThreeNewLinesSeperatedBy1SpaceThen2Spaces() throws Exception { - @SuppressWarnings("unchecked") - ArgRunner runner = EasyMock.createStrictMock(ArgRunner.class); - - runner.run(""); - EasyMock.expectLastCall(); - - runner.run(" "); - EasyMock.expectLastCall(); - - runner.run(" "); - EasyMock.expectLastCall(); - - runner.run(""); - EasyMock.expectLastCall(); - - EasyMock.replay(runner); - - TestUtils.forEachLine("\n \n \n", runner); - EasyMock.verify(runner); - } -} diff --git a/koans-tests/test/com/sandwich/koan/constant/ArgumentTypeTest.java b/koans-tests/test/com/sandwich/koan/constant/ArgumentTypeTest.java deleted file mode 100755 index 48ec575a..00000000 --- a/koans-tests/test/com/sandwich/koan/constant/ArgumentTypeTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.sandwich.koan.constant; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.Test; - -import com.sandwich.koan.path.CommandLineTestCase; - -public class ArgumentTypeTest extends CommandLineTestCase { - - @Test - public void testClassPrecedesMethod() throws Exception { - assertTrue(ArgumentType.CLASS_ARG.compareTo(ArgumentType.METHOD_ARG) == -1); - // further drive point home - method inserted at index 0, class index 1 - List classVsMethod = Arrays.asList(ArgumentType.METHOD_ARG, ArgumentType.CLASS_ARG); - assertEquals(0, classVsMethod.indexOf(ArgumentType.METHOD_ARG)); - assertEquals(1, classVsMethod.indexOf(ArgumentType.CLASS_ARG)); - Collections.sort(classVsMethod); - // now - because of comparable impl was applied, class precedes method - this is necessary - // @ see KaonSuiteRunner.run() - assertEquals(1, classVsMethod.indexOf(ArgumentType.METHOD_ARG)); - assertEquals(0, classVsMethod.indexOf(ArgumentType.CLASS_ARG)); - } - -} diff --git a/koans-tests/test/com/sandwich/koan/path/CommandLineTestCase.java b/koans-tests/test/com/sandwich/koan/path/CommandLineTestCase.java deleted file mode 100755 index 78b3b184..00000000 --- a/koans-tests/test/com/sandwich/koan/path/CommandLineTestCase.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.sandwich.koan.path; -import static org.junit.Assert.assertEquals; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.junit.After; -import org.junit.Before; - -import com.sandwich.koan.Koan; -import com.sandwich.koan.KoanIncompleteException; -import com.sandwich.koan.TestUtils; -import com.sandwich.koan.TestUtils.ArgRunner; -import com.sandwich.koan.constant.ArgumentType; -import com.sandwich.koan.path.PathToEnlightenment.Path; -import com.sandwich.koan.path.xmltransformation.FakeXmlToPathTransformer; -import com.sandwich.koan.path.xmltransformation.KoanElementAttributes; -import com.sandwich.koan.runner.RunKoans; -import com.sandwich.util.io.DynamicClassLoader; -import com.sandwich.util.io.directories.DirectoryManager; -import com.sandwich.util.io.directories.Production; -import com.sandwich.util.io.directories.UnitTest; - -public abstract class CommandLineTestCase { - - private PrintStream console; - private ByteArrayOutputStream bytes; - - @Before - public void setUp() { - DirectoryManager.setDirectorySet(new UnitTest()); - bytes = new ByteArrayOutputStream(); - console = System.out; - TestUtils.setValue("behavior", new RunKoans(), ArgumentType.RUN_KOANS); - PathToEnlightenment.xmlToPathTransformer = new FakeXmlToPathTransformer(); - PathToEnlightenment.theWay = PathToEnlightenment.createPath(); - System.setOut(new PrintStream(bytes)); - } - - @After - public void tearDown() { - DirectoryManager.setDirectorySet(new Production()); - setRealPath(); - System.setOut(console); - } - - protected void setRealPath(){ - PathToEnlightenment.xmlToPathTransformer = null; - PathToEnlightenment.theWay = PathToEnlightenment.createPath(); - } - - protected Path stubAllKoans(String packageName, List path){ - Path oldKoans = PathToEnlightenment.getPathToEnlightment(); - Map> tempSuitesAndMethods = - new LinkedHashMap>(); - DynamicClassLoader loader = new DynamicClassLoader(); - for(String suite : path){ - Map methodsByName = new LinkedHashMap(); - for(Method m : loader.loadClass(suite).getMethods()){ - if(m.getAnnotation(Koan.class) != null){ - methodsByName.put(m.getName(), new KoanElementAttributes("", m.getName(), "", m.getDeclaringClass().getName())); - } - } - tempSuitesAndMethods.put(suite, methodsByName); - } - Map>> stubbedPath = - new LinkedHashMap>>(); - stubbedPath.put(packageName, tempSuitesAndMethods); - PathToEnlightenment.theWay = new Path(null,stubbedPath); - return oldKoans; - } - - public Path stubAllKoans(List path){ - List classes = new ArrayList(); - for(Object o : path){ - String className; - if(o instanceof Class){ - className = ((Class)o).getName(); - }else{ - className = o.getClass().getName(); - } - classes.add(className); - } - return stubAllKoans("Test", classes); - } - - public void clearSysout(){ - bytes = new ByteArrayOutputStream(); - System.setOut(new PrintStream(bytes)); - } - - public void assertSystemOutEquals(String expectation){ - expectation = expectation == null ? "" : expectation; - if(!expectation.equals(bytes.toString())){ - throw new KoanIncompleteException("expected: <"+expectation+"> but found: <"+bytes.toString()+">"); - } - } - - public void assertSystemOutContains(String expectation){ - assertSystemOutContains(true, expectation); - } - - protected void assertSystemOutDoesntContain(String expectation){ - assertSystemOutContains(false, expectation); - } - - private void assertSystemOutContains(boolean assertContains, String expectation) { - String consoleOutput = bytes.toString(); - boolean containsTheSubstring = consoleOutput.contains(expectation); - if(assertContains && !containsTheSubstring || !assertContains && containsTheSubstring){ - throw new KoanIncompleteException(new StringBuilder( - "<").append( - expectation).append( - "> ").append( - (assertContains ? "wasn't" : "was")).append( - " found in: " ).append( - "<").append( - consoleOutput).append( - ">").toString()); - } - } - - public void assertSystemOutLineEquals(final int lineNumber, final String lineText){ - assertSystemOutLineEquals(lineNumber, lineText, false); - } - - public void assertSystemOutLineEquals(final int lineNumber, final String lineText, - final boolean trimLinesString) { - final int[] onLine = new int[]{0}; - final boolean[] found = new boolean[]{false}; - TestUtils.forEachLine(bytes.toString(), new ArgRunner(){ - public void run(String s){ - if(onLine[0] == lineNumber){ - if(trimLinesString){ - s = s.trim(); - } - assertEquals(lineText, s); - found[0] = true; - } - onLine[0]++; - } - }); - if(!found[0]){ - throw new KoanIncompleteException(lineText+" was expected, but not found in: "+bytes.toString()); - } - } -} diff --git a/koans-tests/test/com/sandwich/koan/path/DefaultKoanDescriptionTest.java b/koans-tests/test/com/sandwich/koan/path/DefaultKoanDescriptionTest.java deleted file mode 100755 index 71d6d876..00000000 --- a/koans-tests/test/com/sandwich/koan/path/DefaultKoanDescriptionTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.sandwich.koan.path; - -import java.util.Map; -import java.util.Map.Entry; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.sandwich.koan.Koan; -import com.sandwich.koan.KoanMethod; -import com.sandwich.koan.constant.KoanConstants; -import com.sandwich.koan.path.xmltransformation.KoanElementAttributes; - -public class DefaultKoanDescriptionTest extends CommandLineTestCase { - - @Before - public void setUp(){ - super.setUp(); - } - - @After - public void tearDown(){ - super.tearDown(); - } - - @Test - public void defaultKoanDescriptions() throws Exception { - StringBuilder exceptionStringBuilder = new StringBuilder(KoanConstants.EOL); - for (Entry> suiteAndKoans : - PathToEnlightenment.getPathToEnlightment().getKoanMethodsBySuiteByPackage().next().getValue().entrySet()) { - for(Entry koanEntry : suiteAndKoans.getValue().entrySet()){ - KoanMethod koan = KoanMethod.getInstance(koanEntry.getValue()); - Koan annotation = koan.getMethod().getAnnotation(Koan.class); - if (annotation != null && KoanConstants.DEFAULT_KOAN_DESC.equals(koan.getLesson())) { - exceptionStringBuilder.append(suiteAndKoans.getKey().getClass().getName()).append('.') - .append(koan.getMethod().getName()).append(KoanConstants.EOL); - } - } - } - String exceptionString = exceptionStringBuilder.toString(); - if(exceptionString.trim().length() != 0){ - throw new RuntimeException(new StringBuilder(KoanConstants.EOL).append( - "Following still have default Koan description:").append(exceptionString).toString()); - } - } - -} diff --git a/koans-tests/test/com/sandwich/koan/path/XmlVariableInjectorTest.java b/koans-tests/test/com/sandwich/koan/path/XmlVariableInjectorTest.java deleted file mode 100755 index 0b12af7f..00000000 --- a/koans-tests/test/com/sandwich/koan/path/XmlVariableInjectorTest.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.sandwich.koan.path; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import org.junit.Test; - -import com.sandwich.koan.path.xmltransformation.XmlVariableInjector; -import com.sandwich.koan.suite.OneFailingKoan; - -public class XmlVariableInjectorTest { - - @Test - public void construction_nullMethod() throws Exception { - try{ - new XmlVariableInjector("", null); - fail("why construct w/ null method?"); - }catch(IllegalArgumentException t){ - // this is ok - we want this! - } - } - -// @Test -// public void construction_nullLesson() throws Exception { -// try{ -// new XmlVariableInjector(null, Object.class.getDeclaredMethod("equals", Object.class)); -// fail("why construct w/ null lesson?"); -// }catch(IllegalArgumentException t){ -// // this is ok - we want this! -// } -// } - - @Test - public void injectInputVariables_filePath() throws Exception { - String lesson = "meh ${file_path}"; - String result = new XmlVariableInjector(lesson, OneFailingKoan.class.getDeclaredMethod("koanMethod")) - .injectLessonVariables(); - String firstPkgName = "com"; - // just inspect anything beyond the root of the project - result = result.substring(result.indexOf(firstPkgName)+firstPkgName.length(), result.length()); - assertTrue(result.indexOf(firstPkgName) < result.indexOf("sandwich")); - assertTrue(result.indexOf("sandwich") < result.indexOf("koan")); - assertTrue(result.indexOf("koan") < result.indexOf("suite")); - } - - @Test - public void injectInputVariables_fileName() throws Exception { - String lesson = "meh ${file_name}"; - String result = new XmlVariableInjector(lesson, OneFailingKoan.class.getDeclaredMethod("koanMethod")) - .injectLessonVariables(); - assertEquals("meh OneFailingKoan", result); - } - - @Test - public void injectInputVariables_methodName() throws Exception { - String lesson = "meh ${method_name}"; - String result = new XmlVariableInjector(lesson, OneFailingKoan.class.getDeclaredMethod("koanMethod")) - .injectLessonVariables(); - assertEquals("meh koanMethod", result); - } - - @Test - public void nothingToInject() throws Exception { - String lesson = " meh ea asdwdw s "; - assertSame(lesson, new XmlVariableInjector(lesson, - OneFailingKoan.class.getDeclaredMethod("koanMethod")).injectLessonVariables()); - } -} diff --git a/koans-tests/test/com/sandwich/koan/path/xmltransformation/FakeXmlToPathTransformer.java b/koans-tests/test/com/sandwich/koan/path/xmltransformation/FakeXmlToPathTransformer.java deleted file mode 100644 index 63f84368..00000000 --- a/koans-tests/test/com/sandwich/koan/path/xmltransformation/FakeXmlToPathTransformer.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.sandwich.koan.path.xmltransformation; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -import com.sandwich.koan.path.PathToEnlightenment.Path; - -public class FakeXmlToPathTransformer extends XmlToPathTransformerImpl { - - private Map> methodsBySuite; - - @SuppressWarnings("unchecked") - public FakeXmlToPathTransformer() { - this(Collections.EMPTY_MAP); - } - - public FakeXmlToPathTransformer(Map> methodsBySuite){ - this.methodsBySuite = methodsBySuite; - } - - public Map> getMethodsBySuite() { - return methodsBySuite; - } - - public void setMethodsBySuite(Map> methodsBySuite) { - this.methodsBySuite = methodsBySuite; - } - - @Override - public Path transform(){ - Map>> koans = - new LinkedHashMap>>(); - koans.put("test", new LinkedHashMap>(methodsBySuite)); - return new Path(null,koans); - } - -} diff --git a/koans-tests/test/com/sandwich/koan/runner/AppLauncherTest.java b/koans-tests/test/com/sandwich/koan/runner/AppLauncherTest.java deleted file mode 100644 index a3559be1..00000000 --- a/koans-tests/test/com/sandwich/koan/runner/AppLauncherTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.sandwich.koan.runner; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import org.junit.Test; - -import com.sandwich.koan.cmdline.CommandLineArgument; -import com.sandwich.koan.cmdline.CommandLineArgumentBuilder; -import com.sandwich.koan.constant.ArgumentType; - -public class AppLauncherTest { - - @Test - public void testNecessityOfAddingRunKoansCommandLineArgument_addsIfNoArgsPresent(){ //default target - Map args = new CommandLineArgumentBuilder(); - assertArgsContains(true, args, ArgumentType.RUN_KOANS); - } - - @Test - public void testNecessityOfAddingRunKoansCommandLineArgument_ifClassArgIsPresent(){ - Map args = new CommandLineArgumentBuilder(Object.class.getName()); - assertArgsContains(true, args, ArgumentType.RUN_KOANS, ArgumentType.CLASS_ARG); - } - - @Test - public void testNecessityOfAddingRunKoansCommandLineArgument_doesntIfClassArgIsntPresent(){ - List types = new ArrayList(Arrays.asList(ArgumentType.values())); - assertTrue(types.remove(ArgumentType.CLASS_ARG)); - assertTrue(types.remove(ArgumentType.DEBUG)); - assertTrue(types.remove(ArgumentType.RUN_KOANS)); - for(ArgumentType type : types){ - Map args = new CommandLineArgumentBuilder(type.args().iterator().next()); - assertArgsContains(false, args, ArgumentType.RUN_KOANS); - assertArgsContains(true, args, type); - } - } - - private static void assertArgsContains( - boolean shouldContain, Map args, ArgumentType...types) { - if(shouldContain){ - assertEquals("expected arguments of a certain length, but found those built were of a differing size", - types.length, args.size()); - } - for(ArgumentType type : types){ - assertEquals("the arguments built should" - + (shouldContain ? "" : "n't") - + " contain the type: "+type, shouldContain, args.containsKey(type)); - } - } -} diff --git a/koans-tests/test/com/sandwich/koan/runner/CommandLineTestCaseTest.java b/koans-tests/test/com/sandwich/koan/runner/CommandLineTestCaseTest.java deleted file mode 100755 index ada9a778..00000000 --- a/koans-tests/test/com/sandwich/koan/runner/CommandLineTestCaseTest.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.sandwich.koan.runner; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.fail; - -import java.io.PrintStream; -import java.util.Collections; -import java.util.List; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.sandwich.koan.path.CommandLineTestCase; -import com.sandwich.koan.path.PathToEnlightenment; -import com.sandwich.koan.path.PathToEnlightenment.Path; - -public class CommandLineTestCaseTest { - - CommandLineTestCase testCase; - - @Before - public void setUp(){ - testCase = new CommandLineTestCase(){}; - } - - @After - public void tearDown(){ - testCase.tearDown(); // really important - will remove regular System.out if commented! - testCase = null; - } - - @Test - public void testThatConsoleIsInitializedBySetUp() throws Exception { - PrintStream originalOut = System.out; - testCase.setUp(); - assertNotSame(originalOut, System.out); - } - - @Test - public void testThatConsoleIsCleanAfterSetUp() throws Exception { - testCase.setUp(); - testCase.assertSystemOutLineEquals(0, ""); - } - - @Test - public void testThatConsoleIsAttachedToSystem() throws Exception { - testCase.setUp(); - System.out.print("hello \n world!"); - testCase.assertSystemOutLineEquals(0, "hello "); - testCase.assertSystemOutLineEquals(1, " world!"); - } - - @Test - public void testThatAssertSystemOutLineEquals_withTrimStringArg() throws Exception { - testCase.setUp(); - System.out.println(" hello \n world! "); - testCase.assertSystemOutLineEquals(0, "hello", true); - testCase.assertSystemOutLineEquals(1, "world!", true); - } - - @Test - public void testThatTearDownDetachesDummiedConsoleFromSystem(){ - PrintStream originalConsole = System.out; - testCase.setUp(); - PrintStream fakeConsole = System.out; - assertNotSame(fakeConsole, originalConsole); - testCase.tearDown(); - assertSame(originalConsole, System.out); - } - - @Test - public void testThatStubAllKoansStubsAllKoansReference() throws Exception { - Path oldKoans = PathToEnlightenment.getPathToEnlightment(); - List newKoans = Collections.emptyList(); - testCase.stubAllKoans(newKoans); - assertNotSame(oldKoans, PathToEnlightenment.getPathToEnlightment()); - // number of suites - assertEquals(0, PathToEnlightenment.getPathToEnlightment() - .iterator().next().getValue().size()); - } - - @Test - public void testTestCaseRestoresAllKoansReference() throws Exception { - Path oldKoans = PathToEnlightenment.getPathToEnlightment(); - List newKoans = Collections.emptyList(); - testCase.stubAllKoans(newKoans); - assertNotSame(oldKoans, PathToEnlightenment.getPathToEnlightment()); - testCase.tearDown(); - // creates all new instance - assertNotSame(oldKoans, PathToEnlightenment.getPathToEnlightment()); - assertEquals(oldKoans, PathToEnlightenment.getPathToEnlightment()); - } - - @Test - public void testClearSysout() throws Exception { - testCase.setUp(); - System.out.print("!"); - testCase.assertSystemOutLineEquals(0, "!"); - testCase.clearSysout(); - testCase.assertSystemOutLineEquals(0, ""); - } - - @Test - public void testSystemOutEquals_freshStart(){ - testCase.setUp(); - testCase.assertSystemOutEquals(""); - } - - @Test - public void testSystemOutEquals_aString() throws Exception { - String helloWorld = "Hello World!"; - testCase.setUp(); - testCase.assertSystemOutEquals(""); - System.out.print(helloWorld); - testCase.assertSystemOutEquals(helloWorld); - } - - @Test - public void testSystemOutContains_happyPath() throws Exception { - testCase.setUp(); - String zeroThruOne = "01"; - System.out.print(zeroThruOne); - for(char c : zeroThruOne.toCharArray()){ - testCase.assertSystemOutContains(String.valueOf(c)); - } - } - - @Test - public void testSystemOutContains_failurePath() throws Exception { - testCase.setUp(); - String zeroThruOne = "01"; - System.out.print(zeroThruOne); - try{ - testCase.assertSystemOutContains(String.valueOf("a")); - fail(); - }catch(AssertionError ex){ - assertEquals(" wasn't found in: <01>", ex.getMessage()); - } - } -} diff --git a/koans-tests/test/com/sandwich/koan/runner/KoanSuiteRunnerTest.java b/koans-tests/test/com/sandwich/koan/runner/KoanSuiteRunnerTest.java deleted file mode 100755 index aa106442..00000000 --- a/koans-tests/test/com/sandwich/koan/runner/KoanSuiteRunnerTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.sandwich.koan.runner; - - -public class KoanSuiteRunnerTest { - -// @Test -// public void testRunSortsAndInvokesByComparableImplInArgumentType() throws Exception { -// // test depends on this - this is to ensure rest of test is true -// { -//// assertEquals(-1, ArgumentType.TEST.compareTo(ArgumentType.CLASS_ARG)); -// } -// Map args = new LinkedHashMap(); -// final boolean[] called = {false, false, false}; -// args.put(ArgumentType.TEST, new CommandLineArgument(ArgumentType.TEST, null){ -// @Override public void run(){ -// assertFalse(called[0]); -// assertFalse(called[1]); -// assertFalse(called[2]); -// called[0] = true; -// } -// }); -// args.put(ArgumentType.CLASS_ARG, new CommandLineArgument(ArgumentType.CLASS_ARG, null){ -// @Override public void run(){ -// assertTrue(called[0]); -// assertFalse(called[1]); -// assertFalse(called[2]); -// called[1] = true; -// } -// }); -// args.put(ArgumentType.RUN_KOANS, new CommandLineArgument(ArgumentType.RUN_KOANS, null){ -// @Override public void run(){ -// assertTrue(called[0]); -// assertTrue(called[1]); -// assertFalse(called[2]); -// called[2] = true; -// } -// }); -// new CommandLineArgumentRunner(args).run(); -// assertTrue(called[0]); -// assertTrue(called[1]); -// assertTrue(called[2]); -// } - -} diff --git a/koans-tests/test/com/sandwich/koan/suite/BlowUpOnLineTen.java b/koans-tests/test/com/sandwich/koan/suite/BlowUpOnLineTen.java deleted file mode 100755 index 331e54d3..00000000 --- a/koans-tests/test/com/sandwich/koan/suite/BlowUpOnLineTen.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.sandwich.koan.suite; - -import com.sandwich.koan.Koan; - -public class BlowUpOnLineTen extends BlowUpOnLineEleven { - // gotta blow up on line 10 thus the two spaces - // - @Koan - public void blowUpOnLineTen(){ - super.blowUpOnLineEleven(); - } -} diff --git a/koans-tests/test/com/sandwich/koan/suite/OneFailingKoanDifferentName.java b/koans-tests/test/com/sandwich/koan/suite/OneFailingKoanDifferentName.java deleted file mode 100755 index 28da00ca..00000000 --- a/koans-tests/test/com/sandwich/koan/suite/OneFailingKoanDifferentName.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.sandwich.koan.suite; - -import static org.junit.Assert.assertEquals; - -import com.sandwich.koan.Koan; - -public class OneFailingKoanDifferentName extends OneFailingKoan { - @Koan() - @Override - public void koanMethod() { - assertEquals(true, false); - } -} diff --git a/koans-tests/test/com/sandwich/koan/suite/OnePassingKoan.java b/koans-tests/test/com/sandwich/koan/suite/OnePassingKoan.java deleted file mode 100755 index 2db4f932..00000000 --- a/koans-tests/test/com/sandwich/koan/suite/OnePassingKoan.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.sandwich.koan.suite; - -import com.sandwich.koan.Koan; - -public class OnePassingKoan { - boolean[] invoked; - public OnePassingKoan(){ - invoked = new boolean[]{false}; - } - @Koan - public void koan() { - invoked[0] = true; - } -} diff --git a/koans-tests/test/com/sandwich/koan/suite/OnePassingKoanDifferentName.java b/koans-tests/test/com/sandwich/koan/suite/OnePassingKoanDifferentName.java deleted file mode 100755 index 5a76b430..00000000 --- a/koans-tests/test/com/sandwich/koan/suite/OnePassingKoanDifferentName.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.sandwich.koan.suite; - -public class OnePassingKoanDifferentName extends OnePassingKoan { - -} diff --git a/koans-tests/test/com/sandwich/util/io/DirectoryManagerTest.java b/koans-tests/test/com/sandwich/util/io/DirectoryManagerTest.java deleted file mode 100644 index d97f5ef6..00000000 --- a/koans-tests/test/com/sandwich/util/io/DirectoryManagerTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.sandwich.util.io; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -import com.sandwich.koan.path.CommandLineTestCase; -import com.sandwich.util.io.directories.DirectoryManager; - - -public class DirectoryManagerTest extends CommandLineTestCase { - - @Test - public void testFileSeparatorInjection_happyPath() throws Exception { - assertEquals("/home/wilford/liberty", DirectoryManager.injectFileSystemSeparators( - "home", "wilford", "liberty")); - } - - @Test - public void testFileSeparatorInjection_separatorsAtBeginning() throws Exception { - assertEquals("/home/wilford/liberty", DirectoryManager.injectFileSystemSeparators( - "/home", "/wilford", "/liberty")); - } - - @Test - public void testFileSeparatorInjection_separatorsAtEnding() throws Exception { - assertEquals("/home/wilford/liberty", DirectoryManager.injectFileSystemSeparators( - "home/", "wilford/", "liberty/")); - } - - @Test - public void testFileSeparatorInjection_separatorsAtBeginningAndEnding() throws Exception { - assertEquals("/home/wilford/liberty", DirectoryManager.injectFileSystemSeparators( - "home/", "wilford/", "/liberty/")); - } - - @Test - public void testFileSeparatorInjection_separatorsInMiddle() throws Exception { - assertEquals("/home/wilford/liberty", DirectoryManager.injectFileSystemSeparators( - "home", "wilford/liberty")); - } -} diff --git a/koans-tests/test/com/sandwich/util/io/FileMonitorTest.java b/koans-tests/test/com/sandwich/util/io/FileMonitorTest.java deleted file mode 100644 index 618dd92f..00000000 --- a/koans-tests/test/com/sandwich/util/io/FileMonitorTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.sandwich.util.io; - -import org.junit.After; -import org.junit.Before; - -import com.sandwich.util.io.directories.DirectoryManager; - - -public class FileMonitorTest { - - FileMonitor monitor; - - @Before - public void createInstance() throws Exception{ - monitor = FileMonitorFactory.getInstance(DirectoryManager.getMainDir()); - } - - @After - public void destroyInstance(){ - monitor.close(); - } - -} diff --git a/koans-tests/test/easymock-3.0.jar b/koans-tests/test/easymock-3.0.jar deleted file mode 100755 index f6d7a3f9..00000000 Binary files a/koans-tests/test/easymock-3.0.jar and /dev/null differ diff --git a/koans/.classpath b/koans/.classpath deleted file mode 100755 index e0a4bd49..00000000 --- a/koans/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/koans/.project b/koans/.project deleted file mode 100755 index 6e97172c..00000000 --- a/koans/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - koans - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/koans/.settings/org.eclipse.jdt.core.prefs b/koans/.settings/org.eclipse.jdt.core.prefs deleted file mode 100755 index f14674c1..00000000 --- a/koans/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,12 +0,0 @@ -#Wed Apr 06 20:59:09 PDT 2011 -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.5 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.5 diff --git a/koans/README b/koans/README deleted file mode 100755 index b07a4536..00000000 --- a/koans/README +++ /dev/null @@ -1,16 +0,0 @@ -The purpose of this project is to emulate the awesomeness of the Ruby Koans, and apply it to the Java language. - -Why Java? Why not? - -We've all seen plenty of bad Java code, and written it ourselves. Hopefully these koans can help people eager to learn Java; to write clean, maintainable code. - -Running Instructions: -1. Download and unarchive the contents of the most recent java-koans in development from: - https://github.com/matyb/java-koans/archives/master -2. Open a terminal and cd to the 'koans/src' directory from within the archive -3. run run.bat or run.sh (whichever is applicable for your OS) - -Developing a Koan: -1. Follow any of the existing koans as an example to create a new class w/ koan methods (indicated by the @Koan annotation) -2. Define the order and metadata associated with each koan in the PathToEnlightment.xml -3. If necessary - use dynamic content in your lesson, examples are located in XmlVariableInjector class (and Test) and the AboutKoans.java file diff --git a/koans/README.md b/koans/README.md new file mode 100755 index 00000000..044f12ce --- /dev/null +++ b/koans/README.md @@ -0,0 +1,11 @@ +## Quick Start +* Download and unarchive the contents of the most recent java-koans in development from: +https://github.com/matyb/java-koans/archive/master.zip +* Open a console (terminal) and cd to the directory you unarchived: + ```cd ``` +* Change directory to the koans directory: ```cd koans``` +* To export koans to run below command and open in IDE: + * IDEA IntelliJ: ```./gradlew idea (WINDOWS: gradlew.bat idea)``` + * Eclipse ```./gradlew eclipse (WINDOWS: gradlew.bat eclipse)``` +* Back to the console and run ```./run.sh``` (WINDOWS: ```run.bat```) +* Follow the instructions and start hacking diff --git a/koans/app/config/PathToEnlightenment.xml b/koans/app/config/PathToEnlightenment.xml new file mode 100755 index 00000000..dd104446 --- /dev/null +++ b/koans/app/config/PathToEnlightenment.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/koans/app/config/compilationcommands.properties b/koans/app/config/compilationcommands.properties new file mode 100644 index 00000000..d8b12bd0 --- /dev/null +++ b/koans/app/config/compilationcommands.properties @@ -0,0 +1,3 @@ +.groovy=groovyc -d ${bindir} -classpath ${classpath} ${filename} +.java=javac -d ${bindir} -classpath ${classpath} ${filename} + diff --git a/koans/app/config/config.properties b/koans/app/config/config.properties new file mode 100644 index 00000000..5d078c19 --- /dev/null +++ b/koans/app/config/config.properties @@ -0,0 +1,8 @@ +compile_timeout_in_ms=5000 +ignore_from_monitoring=(.*\\.idea|^\\.#.*\\..*|^#.*\\..*) +debug=false +exit_character=Q +enable_encouragement=false +path_xml_filename=PathToEnlightenment.xml +enable_expectation_result=true +interactive=true diff --git a/koans/app/config/i18n/messages_en.properties b/koans/app/config/i18n/messages_en.properties new file mode 100644 index 00000000..c7883473 --- /dev/null +++ b/koans/app/config/i18n/messages_en.properties @@ -0,0 +1,107 @@ +beginner.AboutKoans.findAboutKoansFile=You need to open the file ${file_path}${file_name}.java. There should be a method starting with @Koan called ${method_name}, look within it for your first lesson. Once you have solved that, save the file and check back for your next lesson. +beginner.AboutKoans.definitionOfKoanCompletion=A koan is considered complete when it no longer throws an exception. Often there is more than one way to solve a koan, but only one correct way. This is often hinted at within the koan itself, or the comments appearing here. + +beginner.AboutAssertions.assertBooleanTrue=The __ are an attempt to communicate the need to fill in an answer. Judging by context, what should __ be replaced with? +beginner.AboutAssertions.assertBooleanFalse=Like the prior koan. Ponder if you will, the power of simple assertions when verifying an object's behavior. +beginner.AboutAssertions.assertNullObject=A keyword in java to represent an uninitialized reference is 'null'. There are times when something should be null, and this assertion can prove that. +beginner.AboutAssertions.assertNotNullObject=Sometimes you merely wish to assert an object is not null. This assertion should be used sparingly, often a more specific assertion is appropriate. +beginner.AboutAssertions.assertEqualsUsingExpression=You can use expressions inside the assertion to calculate the value you are asserting on. +beginner.AboutAssertions.assertEqualsWithBetterFailureMessage=However if there is a specific assertion for the .equals() method that gives a much nicer failure message. (Compare its failure to the previous koan's failure) +beginner.AboutAssertions.assertEqualsWithDescriptiveMessage=You can add your own message to an assertXXX() method to provide more information if it is relevant. +beginner.AboutAssertions.assertSameInstance=An object may equal another object, but it will never be the same as another object. +beginner.AboutAssertions.assertNotSameInstance=Now we can see that two references to the same object is not the same as two references to two equal objects. + +beginner.AboutObjects.objectEqualsNull=An Object instance should NEVER equal null keyword. This applies to all subclasses (everything except primitives subclass Object). +beginner.AboutObjects.objectEqualsSelf=An Object instance should equal itself. This too applies to all subclasses of Object. +beginner.AboutObjects.objectIdentityEqualityIsTrueWhenReferringToSameObject=An object should equal itself, even if referenced from another variable name. +beginner.AboutObjects.subclassesEqualsMethodIsLooserThanDoubleEquals=Integer, and many other classes implement equals logically, in other words, they compare properties of each other and not just identity. +beginner.AboutObjects.doubleEqualsOperatorEvaluatesToTrueOnlyWithSameInstance=Double equals operator (==) does not invoke equal, it will evaluate to true if both references refer to the same object or primitive. +beginner.AboutObjects.doubleEqualsOperatorEvaluatesToFalseWithDifferentInstances=The inverse of the prior koan, though two objects may be logically equal, they are not referencing the same object. +beginner.AboutObjects.objectToString=It's easy to identify an object's state at a glance - with a good toString() implementation. Should be overridden in any objects with internal state. Default to string is classname of the instance followed by its hashCode in base 16 (hexadecimal). +beginner.AboutObjects.toStringConcatenates=Java 's string concatenation syntax utilizes addition operator to splice a string with virtually anything. +beginner.AboutObjects.toStringIsTestedForNullWhenInvokedImplicitly=String concatenation implicitly invokes toString on Objects, unless they are null. Notice no NullPointerException is thrown. + +beginner.AboutInheritance.overriddenMethodsMayReturnSubtype=An overridden method may return a subtype of the return type for the method being overridden. Look at the javadoc for java.util.Collection or java.util.List - it will reveal how to eliminate this type cast. + +beginner.AboutArrays.arraysDoNotConsiderElementsWhenEvaluatingEquality=Arrays utilize reference equality, they do not consider elements when determining equality. +beginner.AboutArrays.cloneEqualityIsNotRespected=The general contract of clone is that: Object a == new Object(); a != a.clone(); a.equals(a.clone()). Array instances DO NOT honor this contract, despite implementing Cloneable. +beginner.AboutArrays.anArraysHashCodeMethodDoesNotConsiderElements=Likewise with hashcode, an array instance's hashCode is that of the array, it does not incorporate elements. +beginner.AboutArrays.arraysHelperClassEqualsMethodConsidersElementsWhenDeterminingEquality=The Arrays.equals(...) method DOES evaluate elements when determining equality. This is called 'Logical Equality'. +beginner.AboutArrays.arraysHelperClassHashCodeMethodConsidersElementsWhenDeterminingHashCode=Likewise with hashCode, the Arrays.hashCode(...) method DOES consider elements when determining hashCode. +beginner.AboutArrays.arraysAreMutable=Arrays are always mutable, even when declared final. The final declaration prevents reassignment, but does nothing for elements. +beginner.AboutArrays.arraysAreIndexedAtZero=Arrays contain elements which are indexed by a number starting with zero. +beginner.AboutArrays.arrayIndexOutOfBounds=Array instances blow up when referencing an index that doesn't exist. +beginner.AboutArrays.arrayLengthCanBeChecked=It is often necessary to check the length of an array to ensure an index is valid. This is easy with the array's length property. + +intermediate.AboutAutoboxing.addPrimitivesToCollection=Before Java 5, we had to convert primitives to add to collections. +intermediate.AboutAutoboxing.addPrimitivesToCollectionWithAutoBoxing=With AutoBoxing, we can rely on the compiler to perform the conversion of a primitive to its corresponding wrapper type automatically. +intermediate.AboutAutoboxing.migrateYourExistingCodeToAutoBoxingWithoutFear=With AutoBoxing, we can intermix as well +intermediate.AboutAutoboxing.allPrimitivesCanBeAutoboxed=All primitives can be autoboxed + +advanced.AboutMocks.simpleAnonymousMock=How can this pass without touching the ClassUnderTest? + +#################################################################### +# Koans libraries properties +#################################################################### +__=REPLACE ME +all_koans_succeeded=Way to go! You've completed all of the koans! Feel like writing any? +expected=expected +may_offer_clue=may offer a clue as to how you may progress, now make haste! +investigate=Ponder what's going wrong in the +level=Level +line=Line +passing_suites=Passing Suites +progress=Progress +remaining_suites=Remaining Suites +what_went_wrong=What went wrong + +#only visible if enable_encouragement is true in config.properties +encouragement=Keep going, you will persevere! + +koan=koan +koans=koans +out_of=out of +you_have_conquered=You have conquered + +ArgumentType.duplicated_arg_error_part1=command line arg: +ArgumentType.duplicated_arg_error_part2=\ is duplicated. + +Backup.description=Backup all the koans in the src/ for easy restoration later (useful for developing koans). +Backup.args= -backup, backup, b +Backup.error=An issue was encountered saving a backup copy. Check that the directory exists and try again. +Backup.success=Koans were backed up successfully + +ClassArg.args= -class, class, c +ClassArg.description=Switch is optional, app tries to find a class definition for any unrecognized string - which becomes a method argument if class is not found. If class lookup succeeds - an instance of the class will become the only koansuite to run. Permits users/developers to focus on one suite at a time. +ClassArg.error= +ClassArg.success= + +Clear.args= -clear +Clear.description=Clears compiled artifacts and stored file system timestamps. +Clear.error=Unable to delete filesystem_hashes.dat or application data or class directory. +Clear.success=Classes and file system timestamps deleted successfully. + +Debug.args= -debug, debug, d +Debug.description=Enable debug state in the app. +Debug.error= +Debug.success= + +Help.args= -help, help, h, ? +Help.description=Help. Displays stuff to, er, help you. +Help.error= +Help.success= + +MethodArg.args= -method, method, m +MethodArg.description=Switch is optional, results from failing to find a class definition by an unrecognized string if switch is omitted. +MethodArg.error= +MethodArg.success= + +Reset.args= -reset, reset, r, restore, -restore +Reset.description=Restore all the koans in the src/ folder to their original (or last backed up) state. +Reset.error=There was an unanticipated error encountered restoring the koan files. You're best bet is to start with a fresh copy from your downloads. +Reset.success=Koans restored successfully + +RunKoans.args= +RunKoans.description=Default target. No switch - this runs if no switch is defined, or if a valid class is found as an argument. +RunKoans.error= +RunKoans.success= diff --git a/koans/app/lib/koans.jar b/koans/app/lib/koans.jar new file mode 100644 index 00000000..492231ba Binary files /dev/null and b/koans/app/lib/koans.jar differ diff --git a/koans/build.gradle b/koans/build.gradle new file mode 100644 index 00000000..cc82be26 --- /dev/null +++ b/koans/build.gradle @@ -0,0 +1,21 @@ +apply plugin: 'java' +apply plugin: 'idea' +apply plugin: 'eclipse' + +repositories { + flatDir { + dirs 'app/lib' + } +} + +sourceSets { + main { + java { + srcDir 'src' + } + } +} + +dependencies { + compile name: 'koans' +} \ No newline at end of file diff --git a/koans/gradle/wrapper/gradle-wrapper.jar b/koans/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..94114481 Binary files /dev/null and b/koans/gradle/wrapper/gradle-wrapper.jar differ diff --git a/koans/gradle/wrapper/gradle-wrapper.properties b/koans/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..c2030d0d --- /dev/null +++ b/koans/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Mar 26 18:10:52 GMT 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-bin.zip diff --git a/koans/gradlew b/koans/gradlew new file mode 100755 index 00000000..9d82f789 --- /dev/null +++ b/koans/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/koans/gradlew.bat b/koans/gradlew.bat new file mode 100644 index 00000000..aec99730 --- /dev/null +++ b/koans/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/koans/lib/koans.jar b/koans/lib/koans.jar deleted file mode 100755 index 6ea0d5ea..00000000 Binary files a/koans/lib/koans.jar and /dev/null differ diff --git a/koans/run.bat b/koans/run.bat new file mode 100755 index 00000000..bd54d6c2 --- /dev/null +++ b/koans/run.bat @@ -0,0 +1,32 @@ +@echo off +cls +setLocal EnableDelayedExpansion +set string=%~dp0 +set string=%string:\=/% +set CLASSPATH="%string%app/bin";"%string%app/config" +for /R "%~dp0/app/lib" %%a in (*.jar) do ( + set string=%%a + set string=!string:\=/! + set CLASSPATH=!CLASSPATH!;"!string!" +) +set CLASSPATH=!CLASSPATH!; +javac -version +if ERRORLEVEL 3 goto no_javac +java -version +if ERRORLEVEL 1 goto no_java +cls +java -Dapplication.basedir="%~dp0"" -classpath %CLASSPATH% com.sandwich.koan.runner.AppLauncher %1 %2 %3 %4 %5 %6 %7 %8 %9 + +goto end + +:no_java +cls +@echo java is not bound to PATH variable. +goto end + +:no_javac +cls +@echo javac is not bound to PATH variable. +goto end + +:end \ No newline at end of file diff --git a/koans/run.sh b/koans/run.sh new file mode 100755 index 00000000..cbee3bb0 --- /dev/null +++ b/koans/run.sh @@ -0,0 +1,29 @@ +#!/bin/bash +DIR="$( cd -P "$( dirname "$0" )" && pwd )" +exitOnError() +{ + rc=$? + if [[ $rc != 0 ]] ; then + echo ${1}' is missing from your PATH.' + exit $rc + fi +} + +buildClasspath() +{ + appDir=$1 + classpath=$appDir/bin + IFS=$'\n' + classpath=$classpath:$appDir/config/ + for jar in $appDir/lib/* + do + classpath=$classpath:$jar + done +} +javac -help > /dev/null 2>&1 +exitOnError 'javac' +java -version > /dev/null 2>&1 +exitOnError 'java' +buildClasspath "$DIR"/app +cmd="java -Dapplication.basedir=\"$DIR\" -classpath \"$classpath\" com.sandwich.koan.runner.AppLauncher "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"" +eval $cmd diff --git a/koans/src/PathToEnlightment.xml b/koans/src/PathToEnlightment.xml deleted file mode 100755 index 83c3ad51..00000000 --- a/koans/src/PathToEnlightment.xml +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/koans/src/advanced/AboutMocks.java b/koans/src/advanced/AboutMocks.java index 94978060..fd03e982 100755 --- a/koans/src/advanced/AboutMocks.java +++ b/koans/src/advanced/AboutMocks.java @@ -2,42 +2,45 @@ import com.sandwich.koan.Koan; +import static com.sandwich.util.Assert.fail; + public class AboutMocks { - - @Koan() - public void simpleAnonymousMock(){ - // HINT: pass different Collaborator implementation to constructor - // new ClassUnderTest(new Colloborator(){... - new ClassUnderTest().doSomething(); - // TODO: ponder why this assertion was failing - // originally... look in default constructor - } - - static interface Collaborator { - public void doBusinessStuff(); - } - - static class ClassUnderTest { - Collaborator c; - public ClassUnderTest(){ - // default is to pass a broken Collaborator, test should pass one - // that doesn't throw exception - this(new Collaborator(){ - public void doBusinessStuff() { - throw new AssertionError("Default collaborator's behavior is complicating testing."); - } - }); - } - public ClassUnderTest(Collaborator c){ - this.c = c; - } - public boolean doSomething(){ - c.doBusinessStuff(); - return true; - } - } - - - //TODO: perhaps show off some mocking frameworks? - + + static interface Collaborator { + public void doBusinessStuff(); + } + + static class ExplosiveCollaborator implements Collaborator { + public void doBusinessStuff() { + fail("Default collaborator's behavior is complicating testing."); + } + } + + static class ClassUnderTest { + Collaborator c; + + public ClassUnderTest() { + // default is to pass a broken Collaborator, test should pass one + // that doesn't throw exception + this(new ExplosiveCollaborator()); + } + + public ClassUnderTest(Collaborator c) { + this.c = c; + } + + public boolean doSomething() { + c.doBusinessStuff(); + return true; + } + } + + @Koan + public void simpleAnonymousMock() { + // HINT: pass a safe Collaborator implementation to constructor + // new ClassUnderTest(new Collaborator(){... it should not be the + // objective of this test to test that collaborator, so replace it + new ClassUnderTest().doSomething(); + } + } diff --git a/koans/src/beginner/AboutArithmeticOperators.java b/koans/src/beginner/AboutArithmeticOperators.java new file mode 100644 index 00000000..422647f4 --- /dev/null +++ b/koans/src/beginner/AboutArithmeticOperators.java @@ -0,0 +1,57 @@ +package beginner; + +import com.sandwich.koan.Koan; + +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; + +public class AboutArithmeticOperators { + + @Koan + public void simpleOperations() { + assertEquals(1, __); + assertEquals(1 + 1, __); + assertEquals(2 + 3 * 4, __); + assertEquals((2 + 3) * 4, __); + assertEquals(2 * 3 + 4, __); + assertEquals(2 - 3 + 4, __); + assertEquals(2 + 4 / 2, __); + assertEquals((2 + 4) / 2, __); + } + + @Koan + public void notSoSimpleOperations() { + assertEquals(1 / 2, __); + assertEquals(3 / 2, __); + assertEquals(1 % 2, __); + assertEquals(3 % 2, __); + } + + @Koan + public void minusMinusVariableMinusMinus() { + int i = 1; + assertEquals(--i, __); + assertEquals(i, __); + assertEquals(i--, __); + assertEquals(i, __); + } + + @Koan + public void plusPlusVariablePlusPlus() { + int i = 1; + assertEquals(++i, __); + assertEquals(i, __); + assertEquals(i++, __); + assertEquals(i, __); + } + + @Koan + public void timesAndDivInPlace() { + int i = 1; + i *= 2; + assertEquals(i, __); + i /= 2; + assertEquals(i, __); + } + +} diff --git a/koans/src/beginner/AboutArrays.java b/koans/src/beginner/AboutArrays.java index 3120fd38..0596e5da 100755 --- a/koans/src/beginner/AboutArrays.java +++ b/koans/src/beginner/AboutArrays.java @@ -1,76 +1,76 @@ package beginner; -import static com.sandwich.koan.constant.KoanConstants.__; -import static com.sandwich.util.Assert.assertEquals; +import com.sandwich.koan.Koan; import java.util.Arrays; -import com.sandwich.koan.Koan; +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; public class AboutArrays { - @Koan - public void arraysDoNotConsiderElementsWhenEvaluatingEquality(){ - // arrays utilize default object equality (A == {1} B == {1}, though A - // and B contain the same thing, the container is not the same - // referenced array instance... - assertEquals(new int[] { 1 }.equals(new int[] { 1 }), __); - } - - @Koan - public void cloneEqualityIs_NotRespected(){ //! - int[] original = new int[] { 1 }; - assertEquals(original.equals(original.clone()), __); - } - - @Koan - public void anArraysHashCodeMethodDoesNotConsiderElements(){ - int[] array0 = new int[]{0}; - int[] array1 = new int[]{0}; - assertEquals(Integer.valueOf(array0.hashCode()).equals(array1.hashCode()), __); // not equal! - // TODO: ponder the consequences when arrays are used in Hash Collection implementations. - } - - @Koan - public void arraysHelperClassEqualsMethodConsidersElementsWhenDeterminingEquality(){ - int[] array0 = new int[]{0}; - int[] array1 = new int[]{0}; - assertEquals(Arrays.equals(array0, array1), __); // whew - what most people assume - // about equals in regard to arrays! (logical equality) - } + @Koan + public void arraysDoNotConsiderElementsWhenEvaluatingEquality() { + // arrays utilize default object equality (A == {1} B == {1}, though A + // and B contain the same thing, the container is not the same + // referenced array instance... + assertEquals(new int[]{1}.equals(new int[]{1}), __); + } + + @Koan + public void cloneEqualityIsNotRespected() { //! + int[] original = new int[]{1}; + assertEquals(original.equals(original.clone()), __); + } + + @Koan + public void anArraysHashCodeMethodDoesNotConsiderElements() { + int[] array0 = new int[]{0}; + int[] array1 = new int[]{0}; + assertEquals(Integer.valueOf(array0.hashCode()).equals(array1.hashCode()), __); // not equal! + // TODO: ponder the consequences when arrays are used in Hash Collection implementations. + } + + @Koan + public void arraysHelperClassEqualsMethodConsidersElementsWhenDeterminingEquality() { + int[] array0 = new int[]{0}; + int[] array1 = new int[]{0}; + assertEquals(Arrays.equals(array0, array1), __); // whew - what most people assume + // about equals in regard to arrays! (logical equality) + } + + @Koan + public void arraysHelperClassHashCodeMethodConsidersElementsWhenDeterminingHashCode() { + int[] array0 = new int[]{0}; + int[] array1 = new int[]{0}; + // whew - what most people assume about hashCode in regard to arrays! + assertEquals(Integer.valueOf(Arrays.hashCode(array0)).equals(Arrays.hashCode(array1)), __); + } + + @Koan + public void arraysAreMutable() { + final boolean[] oneBoolean = new boolean[]{false}; + oneBoolean[0] = true; + assertEquals(oneBoolean[0], __); + } + + @Koan + public void arraysAreIndexedAtZero() { + int[] integers = new int[]{1, 2}; + assertEquals(integers[0], __); + assertEquals(integers[1], __); + } + + @Koan + public void arrayIndexOutOfBounds() { + int[] array = new int[]{1}; + @SuppressWarnings("unused") + int meh = array[1]; // remember 0 based indexes, 1 is the 2nd element (which doesn't exist) + } + + @Koan + public void arrayLengthCanBeChecked() { + assertEquals(new int[1].length, __); + } - @Koan - public void arraysHelperClassHashCodeMethodConsidersElementsWhenDeterminingHashCode(){ - int[] array0 = new int[]{0}; - int[] array1 = new int[]{0}; - // whew - what most people assume about hashCode in regard to arrays! - assertEquals(Integer.valueOf(Arrays.hashCode(array0)).equals(Arrays.hashCode(array1)), __); - } - - @Koan - public void arraysAreMutable(){ - final boolean[] oneBoolean = new boolean[]{false}; - oneBoolean[0] = true; - assertEquals(oneBoolean[0], __); - } - - @Koan - public void arraysAreIndexedAtZero(){ - int[] integers = new int[]{1,2}; - assertEquals(integers[0], __); - assertEquals(integers[1], __); - } - - @Koan - public void arrayIndexOutOfBounds(){ - int[] array = new int[]{1}; - @SuppressWarnings("unused") - int meh = array[1]; // remember 0 based indexes, 1 is the 2nd element (which doesn't exist) - } - - @Koan - public void arrayLengthCanBeChecked(){ - assertEquals(new int[1].length, __); - } - } diff --git a/koans/src/beginner/AboutAssertions.java b/koans/src/beginner/AboutAssertions.java old mode 100755 new mode 100644 index 3f27ed19..a83ba2ae --- a/koans/src/beginner/AboutAssertions.java +++ b/koans/src/beginner/AboutAssertions.java @@ -1,57 +1,86 @@ package beginner; -// FYI - usually bad practice to import statically, but can make code cleaner -import static com.sandwich.koan.constant.KoanConstants.__; -import static com.sandwich.util.Assert.assertEquals; -import static com.sandwich.util.Assert.assertFalse; -import static com.sandwich.util.Assert.assertNotNull; -import static com.sandwich.util.Assert.assertNotSame; -import static com.sandwich.util.Assert.assertNull; -import static com.sandwich.util.Assert.assertSame; -import static com.sandwich.util.Assert.assertTrue; - import com.sandwich.koan.Koan; +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.*; + public class AboutAssertions { - @Koan() - public void assertBooleanTrue() { - assertTrue(__); // should be true really - } - - @Koan() - public void assertBooleanFalse() { - assertFalse(__); - } - - @Koan() - public void assertNullObject(){ - assertNull(__); - } - - @Koan() - public void assertNotNullObject(){ - assertNotNull(null); // anything other than null should pass here... - } - - @Koan() - public void assertEqualsWithDescriptiveMessage() { - // Generally, when using an assertXXX methods, expectation is on the - // left and it is best practice to use a String for the first arg - // indication what has failed - assertEquals("1 should equal 1", 1, __); - } - - @Koan() - public void assertSameInstance(){ - Integer same = new Integer(1); - assertSame(same, __); - } - - @Koan() - public void assertNotSameInstance(){ - Integer same = new Integer(1); - Integer sameReference = same; - assertNotSame(same, sameReference); - } + @Koan + public void assertBooleanTrue() { + // there are two possibilities, true or false, what would it be here? + assertTrue(__); + } + + @Koan + public void assertBooleanFalse() { + assertFalse(__); + } + + @Koan + public void assertNullObject() { + // reference to the object can be null, a magic keyword, null, which means + // that there is nothing there + assertNull(__); + } + + @Koan + public void assertNullObjectReference() { + Object someObject = __; + assertNull(someObject); + } + + @Koan + public void assertNotNullObject() { + // but what when there should not be a null value? + assertNotNull(null); + } + + @Koan + public void assertEqualsUsingExpression() { + assertTrue("Hello World!".equals(__)); + } + + @Koan + public void assertEqualsWithAFewExpressions() { + assertEquals("Hello World!", __); + assertEquals(1, __); + assertEquals(2 + 2, __); + assertEquals(2 * 3, __); + assertEquals(3 - 8, __); + assertEquals(10 / 2, __); + } + + @Koan + public void assertEqualsWithDescriptiveMessage() { + // Generally, when using an assertXXX methods, expectation is on the + // left and it is best practice to use a String for the first arg + // indication what has failed + assertEquals("The answer to 'life the universe and everything' should be 42", 42, __); + } + + @Koan + public void assertSameInstance() { + Integer original = new Integer(1); + Integer same = original; + Integer different = new Integer(1); + // These are both equal to the original... + assertEquals(original, same); + assertEquals(original, different); + // ...but only one refers to the same instance as the original. + assertSame(original, __); + } + + @Koan + public void assertNotSameInstance() { + Integer original = new Integer(1); + Integer same = original; + Integer different = new Integer(1); + // These are both equal to the original... + assertEquals(original, same); + assertEquals(original, different); + // ...but only one of them refers to a different instance. + assertNotSame(original, same); // We want equal, but _not_ the same. + } } diff --git a/koans/src/beginner/AboutBitwiseOperators.java b/koans/src/beginner/AboutBitwiseOperators.java new file mode 100644 index 00000000..b2d17451 --- /dev/null +++ b/koans/src/beginner/AboutBitwiseOperators.java @@ -0,0 +1,65 @@ +package beginner; + +import com.sandwich.koan.Koan; + +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; + +public class AboutBitwiseOperators { + + @Koan + public void fullAnd() { + int i = 1; + if (true & (++i < 8)) i = i + 1; + assertEquals(i, __); + } + + @Koan + public void shortCircuitAnd() { + int i = 1; + if (true && (i < -28)) i = i + 1; + assertEquals(i, __); + } + + @Koan + public void aboutXOR() { + int i = 1; + int a = 6; + if ((a < 9) ^ false) i = i + 1; + assertEquals(i, __); + } + + @Koan + public void dontMistakeEqualsForEqualsEquals() { + int i = 1; + boolean a = false; + if (a = true) i++; + assertEquals(a, __); + assertEquals(i, __); + // How could you write the condition 'with a twist' to avoid this trap? + } + + @Koan + public void aboutBitShiftingRightShift() { + int rightShift = 8; + rightShift = rightShift >> 1; + assertEquals(rightShift, __); + } + + @Koan + public void aboutBitShiftingLeftShift() { + int leftShift = 0x80000000; // Is this number positive or negative? + leftShift = leftShift << 1; + assertEquals(leftShift, __); + } + + @Koan + public void aboutBitShiftingRightUnsigned() { + int rightShiftNegativeStaysNegative = 0x80000000; + rightShiftNegativeStaysNegative = rightShiftNegativeStaysNegative >> 4; + assertEquals(rightShiftNegativeStaysNegative, __); + int unsignedRightShift = 0x80000000; // always fills with 0 + unsignedRightShift >>>= 4; // Just like += + assertEquals(unsignedRightShift, __); + } +} diff --git a/koans/src/beginner/AboutCasting.java b/koans/src/beginner/AboutCasting.java index 4e385476..a0520b50 100644 --- a/koans/src/beginner/AboutCasting.java +++ b/koans/src/beginner/AboutCasting.java @@ -1,122 +1,114 @@ package beginner; +import com.sandwich.koan.Koan; + import static com.sandwich.koan.constant.KoanConstants.__; import static com.sandwich.util.Assert.assertEquals; import static com.sandwich.util.Assert.fail; -import com.sandwich.koan.Koan; - @SuppressWarnings("unused") public class AboutCasting { - - @Koan - public void longPlusInt() { - int a = 6; - long b = 10; - Object c = a + b; - assertEquals(c, __); - assertEquals(c instanceof Integer, __); - assertEquals(c instanceof Long, __); - } - - @Koan - public void forceIntTypecast() { - long a = 2147483648L; - // What happens if we force a long value into an int? - int b = (int)a; - assertEquals(b, __); - } - - @Koan - public void implicitTypecast() { - int a = 1; - int b = Integer.MAX_VALUE; - long c = a + b; - assertEquals(c, __); - } - - class GrandParent { public String complain() { return "Get your feet off the davenport!"; } } - class Parent extends GrandParent { public String complain() { return "TPS reports don't even have a cover letter!"; } } - + @Koan - public void downCastWithInerhitance() { - Parent parent = new Parent(); - GrandParent grandParent = parent; // Why isn't there an explicit cast? - assertEquals(grandParent instanceof GrandParent,__); - assertEquals(parent instanceof GrandParent,__); - assertEquals(parent instanceof Parent,__); - // parents know of their parents + public void longPlusInt() { + int a = 6; + long b = 10; + Object c = a + b; + assertEquals(c, __); + assertEquals(c instanceof Integer, __); + assertEquals(c instanceof Long, __); } - + @Koan - public void downCastAndPolymophism() { - Parent parent = new Parent(); - GrandParent grandParent = parent; - // Think about the result. Did you expect that? Why? - // Think about inheritance, objects, classes and instances. - assertEquals(parent.complain(), __); - assertEquals(grandParent.complain(), __); + public void forceIntTypecast() { + long a = 2147483648L; + // What happens if we force a long value into an int? + int b = (int) a; + assertEquals(b, __); } - + @Koan - public void upCastWithInheritance() { - GrandParent grandParent = new Parent(); - Parent parent = (Parent)grandParent; // Why do we need an explicit cast here? - assertEquals(grandParent instanceof GrandParent,__); - assertEquals(parent instanceof GrandParent,__); - assertEquals(parent instanceof Parent,__); - // a parent does not know of it's children implicitly, it is an open ended contract... - // so YOU need to define that for the compiler with a cast - this can vary at runtime + public void implicitTypecast() { + int a = 1; + int b = Integer.MAX_VALUE; + long c = a + b; // still overflows int... which is the Integer.MIN_VALUE, the operation occurs prior to assignment to long + assertEquals(c, __); + } + + interface Sleepable { + String sleep(); } - + + class Grandparent implements Sleepable { + public String sleep() { + return "zzzz"; + } + } + + class Parent extends Grandparent { + public String complain() { + return "TPS reports don't even have a cover letter!"; + } + } + + class Child extends Parent { + public String complain() { + return "Are we there yet!!"; + } + } + @Koan - public void upCastAndPolymophism() { - GrandParent grandParent = new GrandParent(); - Parent parent = (Parent)grandParent; - // Think about the result. Did you expect that? Why? - // How is that different from above? - assertEquals(grandParent.complain(),__); - assertEquals(parent.complain(),__); + public void upcastWithInheritance() { + Child child = new Child(); + Parent parentReference = child; // Why isn't there an explicit cast? + assertEquals(child instanceof Child, __); + assertEquals(parentReference instanceof Child, __); + assertEquals(parentReference instanceof Parent, __); + assertEquals(parentReference instanceof Grandparent, __); } - - interface Sleepable { - String sleep(); + + @Koan + public void upcastAndPolymorphism() { + Child child = new Child(); + Parent parentReference = child; + // If the result is unexpected, consider the difference between an instance and its reference + assertEquals(parentReference.complain(), __); } - - class Child extends Parent implements Sleepable{ - public String praise() { - return "I think you are a great software developer."; - } - public String sleep() { - return "zzzz"; - } + + @Koan + public void downcastWithInheritance() { + Grandparent child = new Child(); + Parent parentReference = (Parent) child; // Why do we need an explicit cast here? + Child childReference = (Child) parentReference; // Or here? + assertEquals(childReference instanceof Child, __); + assertEquals(childReference instanceof Parent, __); + assertEquals(childReference instanceof Grandparent, __); } - + @Koan - public void classCasting(){ - try{ - Object o = new Parent(); // were downcasting way to far here - would it be possible - // to even author this koan had we done what was safe, and - // held the reference as Sleepable? - ((Sleepable)o).sleep(); - }catch(ClassCastException x){ - fail("Parent does not implement Sleepable, maybe one of his kids do?"); - } + public void downcastAndPolymorphism() { + Grandparent child = new Child(); + Parent parent = (Child) child; + // Think about the result. Did you expect that? Why? + // How is that different from above? + assertEquals(parent.complain(), __); } - + @Koan - public void complicatedCast() { - Parent parent = new Child(); - // How can we access the stepchild's ability to "praise" - if the reference is held as a superclass? - assertEquals("I think you are a great software developer.", __); + public void classCasting() { + try { + Object o = new Object(); + ((Sleepable) o).sleep(); // would this even compile without the cast? + } catch (ClassCastException x) { + fail("Object does not implement Sleepable, maybe one of the people classes do?"); + } } - + @Koan - public void complicatedCastWithInterface() { - Parent parent = new Child(); - // What do you need to do in order to call "sleep"? - assertEquals("zzzz", __); + public void complicatedCast() { + Grandparent parent = new Parent(); + // How can we access the parent's ability to "complain" - if the reference is held as a superclass? + assertEquals("TPS reports don't even have a cover letter!", __); } - } diff --git a/koans/src/beginner/AboutConditionals.java b/koans/src/beginner/AboutConditionals.java index 24cc7d9a..ca3d3fd6 100644 --- a/koans/src/beginner/AboutConditionals.java +++ b/koans/src/beginner/AboutConditionals.java @@ -2,154 +2,199 @@ import com.sandwich.koan.Koan; + import static com.sandwich.koan.constant.KoanConstants.__; import static com.sandwich.util.Assert.assertEquals; public class AboutConditionals { - @Koan - public void basicIfWithoutCurly(){ - // Ifs without curly braces are ugly and not recommended but still valid: - int x = 1; - if (true) - x++; - assertEquals(x,__); - } - - @Koan - public void basicIfElseWithoutCurly(){ - // Ifs without curly braces are ugly and not recommended but still valid: - int x = 1; - boolean secretBoolean = false; - if (secretBoolean) - x++; - else - x--; - assertEquals(x,__); - } - - @Koan - public void basicIfElseIfElseWithoutCurly(){ - int x = 1; - boolean secretBoolean = false; - boolean otherBooleanCondition = true; - // Ifs without curly braces are ugly and not recommended but still valid: - if (secretBoolean) - x++; - else if (otherBooleanCondition) - x = 10; - else - x--; - assertEquals(x,__); - } - - @Koan - public void nestedIfsWithoutCurlysAreReallyMisleading() { - // Why are these ugly you ask? Well, try for yourself - int x = 1; - boolean secretBoolean = false; - boolean otherBooleanCondition = true; - // Ifs without curly braces are ugly and not recommended but still valid: - if (secretBoolean) x++; - if (otherBooleanCondition) x = 10; - else x--; - // Where does this else belong to!? - assertEquals(x,__); - } - - @Koan - public void ifAsIntended() { - boolean secretBoolean = true; - int x = 1; - if(secretBoolean) { - x++; - } else { - x = 0; - } - // There are different opinions on where the curly braces go... - // But as long as you put them here. You avoid problems as seen above. - assertEquals(x,__); - } - - @Koan - public void basicSwitchStatement() { - int i = 1; - String result = "Basic "; - switch(i) { - case 1: - result += "One"; - break; - case 2: - result += "Two"; - break; - default: - result += "Nothing"; - } - assertEquals(result, __); - } - - @Koan - public void switchStatementFallThrough() { - int i = 1; - String result = "Basic "; - switch(i) { - case 1: - result += "One"; - case 2: - result += "Two"; - default: - result += "Nothing"; - } - assertEquals(result, __); - } - - @Koan - public void switchStatementCrazyFallThrough() { - int i = 5; - String result = "Basic "; - switch(i) { - case 1: - result += "One"; - default: - result += "Nothing"; - case 2: - result += "Two"; - } - assertEquals(result, __); - } - - @Koan - public void switchStatementConstants() { - int i = 5; - // What happens if you remove the 'final' modifier? - // What does this mean for case values? - final int caseOne = 1; - String result = "Basic "; - switch(i) { - case caseOne: - result += "One"; - break; - default: - result += "Nothing"; - } - assertEquals(result, __); - } - - @Koan - public void switchStatementSwitchValues() { - // Try different (primitive) types for 'c' - // Which types do compile? - // Does boxing work? - byte c = 'a'; - String result = "Basic "; - switch(c) { - case 'a': - result += "One"; - break; - default: - result += "Nothing"; - } - assertEquals(result, __); - } + @Koan + public void basicIf() { + int x = 1; + if (true) { + x++; + } + assertEquals(x, __); + } + + @Koan + public void basicIfElse() { + int x = 1; + boolean secretBoolean = false; + if (secretBoolean) { + x++; + } else { + x--; + } + assertEquals(x, __); + } + + @Koan + public void basicIfElseIfElse() { + int x = 1; + boolean secretBoolean = false; + boolean otherBooleanCondition = true; + if (secretBoolean) { + x++; + } else if (otherBooleanCondition) { + x = 10; + } else { + x--; + } + assertEquals(x, __); + } + + @Koan + public void nestedIfsWithoutCurlysAreReallyMisleading() { + int x = 1; + boolean secretBoolean = false; + boolean otherBooleanCondition = true; + // Curly braces after an "if" or "else" are not required... + if (secretBoolean) + x++; + if (otherBooleanCondition) + x = 10; + else + x--; + // ...but they are recommended. + assertEquals(x, __); + } + + @Koan + public void ifAsIntended() { + int x = 1; + boolean secretBoolean = false; + boolean otherBooleanCondition = true; + // Adding curly braces avoids the "dangling else" problem seen + // above. + if (secretBoolean) { + x++; + if (otherBooleanCondition) { + x = 10; + } + } else { + x--; + } + assertEquals(x, __); + } + + @Koan + public void basicSwitchStatement() { + int i = 1; + String result = "Basic "; + switch (i) { + case 1: + result += "One"; + break; + case 2: + result += "Two"; + break; + default: + result += "Nothing"; + } + assertEquals(result, __); + } + + @Koan + public void switchStatementFallThrough() { + int i = 1; + String result = "Basic "; + switch (i) { + case 1: + result += "One"; + case 2: + result += "Two"; + default: + result += "Nothing"; + } + assertEquals(result, __); + } + + @Koan + public void switchStatementCrazyFallThrough() { + int i = 5; + String result = "Basic "; + switch (i) { + case 1: + result += "One"; + default: + result += "Nothing"; + case 2: + result += "Two"; + } + assertEquals(result, __); + } + + @Koan + public void switchStatementConstants() { + int i = 5; + // What happens if you remove the 'final' modifier? + // What does this mean for case values? + final int caseOne = 1; + String result = "Basic "; + switch (i) { + case caseOne: + result += "One"; + break; + default: + result += "Nothing"; + } + assertEquals(result, __); + } + + @Koan + public void switchStatementSwitchValues() { + // Try different (primitive) types for 'c' + // Which types do compile? + // Does boxing work? + char c = 'a'; + String result = "Basic "; + switch (c) { + case 'a': + result += "One"; + break; + default: + result += "Nothing"; + } + assertEquals(result, __); + } + + @Koan + public void shortCircuit() { + Counter trueCount = new Counter(true); + Counter falseCount = new Counter(false); + String x = "Hai"; + if (trueCount.count() || falseCount.count()) { + x = "kthxbai"; + } + assertEquals(x, __); + assertEquals(trueCount.count, __); + assertEquals(falseCount.count, __); + } + + @Koan + public void bitwise() { + Counter trueCount = new Counter(true); + Counter falseCount = new Counter(false); + String x = "Hai"; + if (trueCount.count() | falseCount.count()) { + x = "kthxbai"; + } + assertEquals(x, __); + assertEquals(trueCount.count, __); + assertEquals(falseCount.count, __); + } + + class Counter { + boolean returnValue; + int count = 0; + Counter(boolean returnValue) { + this.returnValue = returnValue; + } + boolean count() { + count++; + return returnValue; + } + } } diff --git a/koans/src/beginner/AboutConstructors.java b/koans/src/beginner/AboutConstructors.java index a156040d..e2cd6855 100644 --- a/koans/src/beginner/AboutConstructors.java +++ b/koans/src/beginner/AboutConstructors.java @@ -2,41 +2,56 @@ import com.sandwich.koan.Koan; + import static com.sandwich.koan.constant.KoanConstants.__; import static com.sandwich.util.Assert.assertEquals; public class AboutConstructors { - class A { - String someString = "a"; - public A() { someString+= "x"; } - - } - - class B extends A { - public B() { someString += "g"; }; - } - - @Koan - public void simpleConstructorOrder(){ - assertEquals(new B().someString, __); - } - - class Aa { - String someString = "a"; - public Aa() { someString+= "x"; } - public Aa(String s) { - someString += s; - } - } - - class Bb extends Aa { - public Bb() { super("Boo"); someString += "g"; }; - } - - @Koan - public void complexConstructorOrder(){ - assertEquals(new B().someString, __); - } - + class A { + String someString = "a"; + + public A() { + someString += "x"; + } + + } + + class B extends A { + public B() { + someString += "g"; + } + + } + + @Koan + public void simpleConstructorOrder() { + assertEquals(new B().someString, __); + } + + class Aa { + String someString = "a"; + + public Aa() { + someString += "x"; + } + + public Aa(String s) { + someString += s; + } + } + + class Bb extends Aa { + public Bb() { + super("Boo"); + someString += "g"; + } + + } + + @Koan + public void complexConstructorOrder() { + assertEquals(new Bb().someString, __); + } + } diff --git a/koans/src/beginner/AboutEnums.java b/koans/src/beginner/AboutEnums.java index 38afd691..807f5567 100644 --- a/koans/src/beginner/AboutEnums.java +++ b/koans/src/beginner/AboutEnums.java @@ -1,59 +1,67 @@ package beginner; import com.sandwich.koan.Koan; + import static com.sandwich.koan.constant.KoanConstants.__; import static com.sandwich.util.Assert.assertEquals; public class AboutEnums { - enum Colors { - Red, Blue, Green, Yellow // what happens if you add a ; here? - // What happens if you type Red() instead? - } - - @Koan - public void basicEnums() { - Colors blue = Colors.Blue; - assertEquals(blue == Colors.Blue, __); - assertEquals(blue == Colors.Red, __); - assertEquals(blue instanceof Colors, __); - } - - @Koan - public void basicEnumsAccess() { - Colors[] colorArray = Colors.values(); - assertEquals(colorArray[2], __); - } - - enum SkatSuits { - Clubs(12), Spades(11), Hearts(10), Dimonds(9); - SkatSuits(int v) { value = v; } - private int value; - } - - @Koan - public void enumsWithAttributes() { - // value is private but we still can access it. Why? - // Try moving the enum outside the AboutEnum class... What do you expect? - // What happens? - assertEquals(SkatSuits.Clubs.value > SkatSuits.Spades.value, __); - } - - enum OpticalMedia { - CD(650), DVD(4300), BluRay(50000); - OpticalMedia(int c) { - capacityInMegaBytes = c; - } - int capacityInMegaBytes; - int getCoolnessFactor() { - return (capacityInMegaBytes - 1000) * 10; - } - } - - @Koan - public void enumsWithMethods() { - assertEquals(OpticalMedia.CD.getCoolnessFactor(), __); - assertEquals(OpticalMedia.BluRay.getCoolnessFactor(), __); - } + enum Colors { + Red, Blue, Green, Yellow // what happens if you add a ; here? + // What happens if you type Red() instead? + } + + @Koan + public void basicEnums() { + Colors blue = Colors.Blue; + assertEquals(blue == Colors.Blue, __); + assertEquals(blue == Colors.Red, __); + assertEquals(blue instanceof Colors, __); + } + + @Koan + public void basicEnumsAccess() { + Colors[] colorArray = Colors.values(); + assertEquals(colorArray[2], __); + } + + enum SkatSuits { + Clubs(12), Spades(11), Hearts(10), Diamonds(9); + + SkatSuits(int v) { + value = v; + } + + private int value; + } + + @Koan + public void enumsWithAttributes() { + // value is private but we still can access it. Why? + // Try moving the enum outside the AboutEnum class... What do you expect? + // What happens? + assertEquals(SkatSuits.Clubs.value > SkatSuits.Spades.value, __); + } + + enum OpticalMedia { + CD(650), DVD(4300), BluRay(50000); + + OpticalMedia(int c) { + capacityInMegaBytes = c; + } + + int capacityInMegaBytes; + + int getCoolnessFactor() { + return (capacityInMegaBytes - 1000) * 10; + } + } + + @Koan + public void enumsWithMethods() { + assertEquals(OpticalMedia.CD.getCoolnessFactor(), __); + assertEquals(OpticalMedia.BluRay.getCoolnessFactor(), __); + } } diff --git a/koans/src/beginner/AboutEquality.java b/koans/src/beginner/AboutEquality.java new file mode 100644 index 00000000..18ac70cf --- /dev/null +++ b/koans/src/beginner/AboutEquality.java @@ -0,0 +1,52 @@ +package beginner; + +import com.sandwich.koan.Koan; + +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; + +public class AboutEquality { + + @Koan + public void doubleEqualsTestsIfTwoObjectsAreTheSame() { + Object object = new Object(); + Object sameObject = object; + assertEquals(object == sameObject, __); + assertEquals(object == new Object(), __); + } + + @Koan + public void equalsMethodByDefaultTestsIfTwoObjectsAreTheSame() { + Object object = new Object(); + assertEquals(object.equals(object), __); + assertEquals(object.equals(new Object()), __); + } + + @Koan + public void equalsMethodCanBeChangedBySubclassesToTestsIfTwoObjectsAreEqual() { + Object object = new Integer(1); + assertEquals(object.equals(object), __); + assertEquals(object.equals(new Integer(1)), __); + // Note: This means that for the class 'Object' there is no difference between 'equal' and 'same' + // but for the class 'Integer' there is difference - see below + } + + @Koan + public void equalsMethodCanBeChangedBySubclassesToTestsIfTwoObjectsAreEqualExample() { + Integer value1 = new Integer(4); + Integer value2 = new Integer(2 + 2); + assertEquals(value1.equals(value2), __); + assertEquals(value1, __); + } + + @Koan + public void objectsNeverEqualNull() { + assertEquals(new Object().equals(null), __); + } + + @Koan + public void objectsEqualThemselves() { + Object obj = new Object(); + assertEquals(obj.equals(obj), __); + } +} diff --git a/koans/src/beginner/AboutExceptions.java b/koans/src/beginner/AboutExceptions.java index a7d82244..b54ee194 100644 --- a/koans/src/beginner/AboutExceptions.java +++ b/koans/src/beginner/AboutExceptions.java @@ -1,84 +1,166 @@ package beginner; +import com.sandwich.koan.Koan; + import java.io.IOException; -import com.sandwich.koan.Koan; import static com.sandwich.koan.constant.KoanConstants.__; import static com.sandwich.util.Assert.assertEquals; public class AboutExceptions { - - private void doSutff() throws IOException { - throw new IOException(); - } - - @Koan - public void catchCheckedExceptions() { - String s; - try { - doSutff(); - s = "code run normally"; - } catch(IOException e) { - s = "exception thrown"; - } - assertEquals(s,__); - } - - @Koan - public void useFinally() { - String s = ""; - try { - doSutff(); - s = "code run normally"; - } catch(IOException e) { - s = "exception thrown"; - } finally { - s += " and finally ran as well"; - } - assertEquals(s,__); - } - - @Koan - public void finallyWithoutCatch() { - String s = ""; - try { - s = "code run normally"; - } finally { - s += " and finally ran as well"; - } - assertEquals(s,__); - } - - private void doUncheckedStuff() { - throw new RuntimeException(); - } - - @Koan - public void catchUncheckedExceptions() { - // What do you need to do to catch the - // unchecked exception? - doUncheckedStuff(); - } - - @SuppressWarnings("serial") - static class ParentException extends Exception {} - @SuppressWarnings("serial") - static class ChildException extends ParentException {} - - private void throwIt() throws ParentException { - throw new ChildException(); - } - - @Koan - public void catchOrder() { - String s = ""; - try { - throwIt(); - } catch(ChildException e) { - s = "ChildException"; - } catch(ParentException e) { - s = "ParentException"; - } - assertEquals(s, __); - } + + private void doStuff() throws IOException { + throw new IOException(); + } + + @Koan + public void catchCheckedExceptions() { + String s; + try { + doStuff(); + s = "code ran normally"; + } catch (IOException e) { + s = "exception thrown"; + } + assertEquals(s, __); + } + + @Koan + public void useFinally() { + String s = ""; + try { + doStuff(); + s += "code ran normally"; + } catch (IOException e) { + s += "exception thrown"; + } finally { + s += " and finally ran as well"; + } + assertEquals(s, __); + } + + @Koan + public void finallyWithoutCatch() { + String s = ""; + try { + s = "code ran normally"; + } finally { + s += " and finally ran as well"; + } + assertEquals(s, __); + } + + private void tryCatchFinallyWithVoidReturn(StringBuilder whatHappened) { + try { + whatHappened.append("did something dangerous"); + doStuff(); + } catch (IOException e) { + whatHappened.append("; the catch block executed"); + return; + } finally { + whatHappened.append(", but so did the finally!"); + } + } + + @Koan + public void finallyIsAlwaysRan() { + StringBuilder whatHappened = new StringBuilder(); + tryCatchFinallyWithVoidReturn(whatHappened); + assertEquals(whatHappened.toString(), __); + } + + @SuppressWarnings("finally") + // this is suppressed because returning in finally block is obviously a compiler warning + private String returnStatementsEverywhere(StringBuilder whatHappened) { + try { + whatHappened.append("try"); + doStuff(); + return "from try"; + } catch (IOException e) { + whatHappened.append(", catch"); + return "from catch"; + } finally { + whatHappened.append(", finally"); + // Think about how bad an idea it is to put a return statement in the finally block + // DO NOT DO THIS! + return "from finally"; + } + } + + @Koan + public void returnInFinallyBlock() { + StringBuilder whatHappened = new StringBuilder(); + // Which value will be returned here? + assertEquals(returnStatementsEverywhere(whatHappened), __); + assertEquals(whatHappened.toString(), __); + } + + private void doUncheckedStuff() { + throw new RuntimeException(); + } + + @Koan + public void catchUncheckedExceptions() { + // What do you need to do to catch the unchecked exception? + doUncheckedStuff(); + } + + @SuppressWarnings("serial") + static class ParentException extends Exception { + } + + @SuppressWarnings("serial") + static class ChildException extends ParentException { + } + + private void throwIt() throws ParentException { + throw new ChildException(); + } + + @Koan + public void catchOrder() { + String s = ""; + try { + throwIt(); + } catch (ChildException e) { + s = "ChildException"; + } catch (ParentException e) { + s = "ParentException"; + } + assertEquals(s, __); + } + + @Koan + public void failArgumentValidationWithAnIllegalArgumentException() { + // This koan demonstrates the use of exceptions in argument validation + String s = ""; + try { + s += validateUsingIllegalArgumentException(null); + } catch (IllegalArgumentException ex) { + s = "caught an IllegalArgumentException"; + } + assertEquals(s, __); + } + + @Koan + public void passArgumentValidationWithAnIllegalArgumentException() { + // This koan demonstrates the use of exceptions in argument validation + String s = ""; + try { + s += validateUsingIllegalArgumentException("valid"); + } catch (IllegalArgumentException ex) { + s = "caught an IllegalArgumentException"; + } + assertEquals(s, __); + } + + private int validateUsingIllegalArgumentException(String str) { + // This is effective and both the evaluation and the error message + // can be tailored which can be particularly handy if you're guarding + // against more than null values + if (null == str) { + throw new IllegalArgumentException("str should not be null"); + } + return str.length(); + } } diff --git a/koans/src/beginner/AboutInheritance.java b/koans/src/beginner/AboutInheritance.java index e1de156a..22068058 100644 --- a/koans/src/beginner/AboutInheritance.java +++ b/koans/src/beginner/AboutInheritance.java @@ -1,44 +1,111 @@ package beginner; +import com.sandwich.koan.Koan; + import static com.sandwich.koan.constant.KoanConstants.__; import static com.sandwich.util.Assert.assertEquals; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +public class AboutInheritance { -import com.sandwich.koan.Koan; + abstract class Animal { + abstract public String makeSomeNoise(); + } -public class AboutInheritance { + class Cow extends Animal { + @Override + public String makeSomeNoise() { + return "Moo!"; + } + } + + class Dog extends Animal { + @Override + public String makeSomeNoise() { + return "Woof!"; + } + + public boolean canFetch() { + return true; + } + } + + class Puppy extends Dog { + @Override + public String makeSomeNoise() { + return "Squeak!"; + } + public boolean canFetch() { + return false; + } + } + + @Koan + public void methodOverloading() { + Cow bob = new Cow(); + Dog max = new Dog(); + Puppy barney = new Puppy(); + assertEquals(bob.makeSomeNoise(), __); + assertEquals(max.makeSomeNoise(), __); + assertEquals(barney.makeSomeNoise(), __); + + assertEquals(max.canFetch(), __); + assertEquals(barney.canFetch(), __); + // but can Bob the Cow fetch? + } + + @Koan + public void methodOverloadingUsingPolymorphism() { + Animal bob = new Cow(); + Animal max = new Dog(); + Animal barney = new Puppy(); + assertEquals(bob.makeSomeNoise(), __); + assertEquals(max.makeSomeNoise(), __); + assertEquals(barney.makeSomeNoise(), __); + // but can max or barney (here as an Animal) fetch? + // try to write it down here + } + + @Koan + public void inheritanceHierarchy() { + Animal someAnimal = new Cow(); + Animal bob = new Cow(); + assertEquals(someAnimal.makeSomeNoise().equals(bob.makeSomeNoise()), __); + // cow is a Cow, but it can also be an animal + assertEquals(bob instanceof Animal, __); + assertEquals(bob instanceof Cow, __); + // but is it a Puppy? + assertEquals(bob instanceof Puppy, __); + } + + @Koan + public void deeperInheritanceHierarchy() { + Dog max = new Dog(); + Puppy barney = new Puppy(); + assertEquals(max instanceof Puppy, __); + assertEquals(max instanceof Dog, __); + assertEquals(barney instanceof Puppy, __); + assertEquals(barney instanceof Dog, __); + } - class Parent { - public String doStuff() { return "parent"; } - } - class Child extends Parent { - public String doStuff() { return "child"; } - public String doStuff(String s) { return s; } - } - - @Koan - public void differenceBetweenOverloadingAndOverriding() { - assertEquals(new Parent().doStuff(),__); - assertEquals(new Child().doStuff(),__); - assertEquals(new Child().doStuff("oh no"),__); - } - - abstract class ParentTwo { - abstract public Collection doStuff(); - } - - class ChildTwo extends ParentTwo { - public Collection doStuff() { return Collections.emptyList(); }; - } - - @Koan - public void overridenMethodsMayReturnSubtype() { - // What do you need to change in order to get rid of the type cast? - // Why does this work? - List list = (List)new ChildTwo().doStuff(); - assertEquals(list instanceof List, __); - } + // TODO overriding +// +// abstract class ParentTwo { +// abstract public Collection doStuff(); +// } +// +// class ChildTwo extends ParentTwo { +// public Collection doStuff() { +// return Collections.emptyList(); +// } +// +// ; +// } +// +// @Koan +// public void overriddenMethodsMayReturnSubtype() { +// // What do you need to change in order to get rid of the type cast? +// // Why does this work? +// List list = (List) new ChildTwo().doStuff(); +// assertEquals(list instanceof List, __); +// } } diff --git a/koans/src/beginner/AboutKoans.java b/koans/src/beginner/AboutKoans.java old mode 100755 new mode 100644 index fb73f732..5ab0c236 --- a/koans/src/beginner/AboutKoans.java +++ b/koans/src/beginner/AboutKoans.java @@ -1,22 +1,22 @@ package beginner; -import static com.sandwich.util.Assert.fail; - import com.sandwich.koan.Koan; +import static com.sandwich.util.Assert.fail; + public class AboutKoans { - @Koan - public void findAboutKoansFile(){ - fail("delete this line"); - } - - @Koan - public void definitionOfKoanCompletion(){ - boolean koanIsComplete = false; - if(!koanIsComplete){ - fail("what if koanIsComplete was true?"); - } - } - + @Koan + public void findAboutKoansFile() { + fail("delete this line to advance"); + } + + @Koan + public void definitionOfKoanCompletion() { + boolean koanIsComplete = false; + if (!koanIsComplete) { + fail("what if koanIsComplete variable was true?"); + } + } + } diff --git a/koans/src/beginner/AboutLoops.java b/koans/src/beginner/AboutLoops.java index ce6b4167..2a27e89c 100644 --- a/koans/src/beginner/AboutLoops.java +++ b/koans/src/beginner/AboutLoops.java @@ -2,122 +2,167 @@ import com.sandwich.koan.Koan; + import static com.sandwich.koan.constant.KoanConstants.__; import static com.sandwich.util.Assert.assertEquals; public class AboutLoops { - @Koan - public void basicForLoop() { - String s = ""; - for(int i = 0; i < 5; i++) { - s += i + " "; - } - assertEquals(s, __); - } - - @Koan - public void basicForLoopWithTwoVariables() { - String s = ""; - for(int i = 0, j = 10; i < 5 && j > 5; i++, j--) { - s += i + " " + j + " "; - } - assertEquals(s, __); - } - - @Koan - public void extendedForLoop() { - int[] is = {1,2,3,4}; - String s = "-"; - for(int j : is) { - s += "." + j; - } - assertEquals(s, __); - } - - @Koan - public void whileLoop() { - int result = 0; - while(result < 3) { - result++; - } - assertEquals(result, __); - } - - @Koan - public void doLoop() { - int result = 0; - do { - result++; - } while(false); - assertEquals(result, __); - } - - @Koan - public void extendedForLoopBreak() { - String[] sa = {"Dog", "Cat", "Tiger" }; - int count = 0; - for(String current : sa) { - if("Cat".equals(current)) { - break; - } - count++; - } - assertEquals(count, __); - } - - @Koan - public void extendedForLoopContinue() { - String[] sa = {"Dog", "Cat", "Tiger" }; - int count = 0; - for(String current : sa) { - if("Dog".equals(current)) { - continue; - } else { - count++; - } - } - assertEquals(count, __); - } - - - @Koan - public void forLoopContinueLabel() { - int count = 0; - outerLabel: - for(int i = 0; i < 5; i++) { - for(int j = 0; j < 5; j++) - { - count++; - if(count > 2) { - continue outerLabel; - } - } - count += 10; - } - // What does continue with a label mean? - // What gets executed? Where does the program flow continue? - assertEquals(count, __); - } - - @Koan - public void forLoopBreakLabel() { - int count = 0; - outerLabel: - for(int i = 0; i < 5; i++) { - for(int j = 0; j < 5; j++) - { - count++; - if(count > 2) { - break outerLabel; - } - } - count += 10; - } - // What does break with a label mean? - // What gets executed? Where does the program flow continue? - assertEquals(count, __); - } + @Koan + public void basicForLoop1() { + String s = ""; + for (int i = 0; i < 5; i++) { + s += i + " "; + } + assertEquals(s, __); + } + + @Koan + public void basicForLoop2() { + String s = ""; + for (int i = -5; i < 1; i++) { + s += i + " "; + } + assertEquals(s, __); + } + + @Koan + public void basicForLoop3() { + String s = ""; + for (int i = 5; i > 0; i--) { + s += i + " "; + } + assertEquals(s, __); + } + + @Koan + public void basicForLoop4() { + String s = ""; + for (int i = 0; i < 11; i += 2) { + s += i + " "; + } + assertEquals(s, __); + } + + @Koan + public void basicForLoop5() { + String s = ""; + for (int i = 1; i <= 16; i *= 2) { + s += i + " "; + } + assertEquals(s, __); + } + + @Koan + public void basicForLoopWithTwoVariables1() { + String s = ""; + for (int i = 0, j = 10; i < 5 && j > 5; i++, j--) { + s += i + " " + j + " "; + } + assertEquals(s, __); + } + + @Koan + public void nestedLoops() { + String s = ""; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + s += "(" + i + ", " + j + ") "; + } + s += " - "; + } + assertEquals(s, __); + } + + @Koan + public void extendedForLoop() { + int[] is = {1, 2, 3, 4}; + String s = ""; + for (int j : is) { + s += j + " "; + } + assertEquals(s, __); + } + + @Koan + public void whileLoop() { + int result = 0; + while (result < 3) { + result++; + } + assertEquals(result, __); + } + + @Koan + public void doLoop() { + int result = 0; + do { + result++; + } while (false); + assertEquals(result, __); + } + + @Koan + public void extendedForLoopBreak() { + String[] sa = {"Dog", "Cat", "Tiger"}; + int count = 0; + for (String current : sa) { + if ("Cat".equals(current)) { + break; + } + count++; + } + assertEquals(count, __); + } + + @Koan + public void extendedForLoopContinue() { + String[] sa = {"Dog", "Cat", "Tiger"}; + int count = 0; + for (String current : sa) { + if ("Dog".equals(current)) { + continue; + } else { + count++; + } + } + assertEquals(count, __); + } + + @Koan + public void forLoopContinueLabel() { + int count = 0; + outerLabel: + for (int i = 0; i < 6; i++) { + for (int j = 0; j < 6; j++) { + count++; + if (count > 2) { + continue outerLabel; + } + } + count += 10; + } + // What does continue with a label mean? + // What gets executed? Where does the program flow continue? + assertEquals(count, __); + } + @Koan + public void forLoopBreakLabel() { + int count = 0; + outerLabel: + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + count++; + if (count > 2) { + break outerLabel; + } + } + count += 10; + } + // What does break with a label mean? + // What gets executed? Where does the program flow continue? + assertEquals(count, __); + } } diff --git a/koans/src/beginner/AboutMethodPreference.java b/koans/src/beginner/AboutMethodPreference.java index 04d6af5f..52063487 100644 --- a/koans/src/beginner/AboutMethodPreference.java +++ b/koans/src/beginner/AboutMethodPreference.java @@ -1,51 +1,63 @@ package beginner; import com.sandwich.koan.Koan; + import static com.sandwich.koan.constant.KoanConstants.__; import static com.sandwich.util.Assert.assertEquals; public class AboutMethodPreference { - class A { - public String doStuff(int i) { return "int"; } - public String doStuff(Integer i) { return "Integer"; } - public String doStuff(Object i) { return "Object"; } - public String doStuff(int...i) { return "int vararg"; } - } - - @Koan - public void methodPreferenceInt() { - assertEquals(new A().doStuff(1), __); - } - - @Koan - public void methodPreferenceInteger() { - assertEquals(new A().doStuff(Integer.valueOf(1)), __); - } - - @Koan - public void methodPreferenceLong() { - long l = 1; - assertEquals(new A().doStuff(l), __); - } - - @Koan - public void methodPreferenceBoxedLong() { - Long l = Long.valueOf(1); - assertEquals(new A().doStuff(l), __); - } - - @Koan - public void methodPreferenceDouble() { - Double l = Double.valueOf(1); - assertEquals(new A().doStuff(l), __); - } - - @Koan - public void methodPreferenceMore() { - // What happens if you change 'Integer' to 'Double' - // Does this explain 'methodPreferenceDouble'? - // Think about why this happens? - assertEquals(new A().doStuff(1,Integer.valueOf(2)), __); - } + class A { + public String doStuff(int i) { + return "int"; + } + + public String doStuff(Integer i) { + return "Integer"; + } + + public String doStuff(Object i) { + return "Object"; + } + + public String doStuff(int... i) { + return "int vararg"; + } + } + + @Koan + public void methodPreferenceInt() { + assertEquals(new A().doStuff(1), __); + } + + @Koan + public void methodPreferenceInteger() { + assertEquals(new A().doStuff(Integer.valueOf(1)), __); + } + + @Koan + public void methodPreferenceLong() { + long l = 1; + assertEquals(new A().doStuff(l), __); + } + + @Koan + public void methodPreferenceBoxedLong() { + Long l = Long.valueOf(1); + assertEquals(new A().doStuff(l), __); + } + + @Koan + public void methodPreferenceDouble() { + Double l = Double.valueOf(1); + assertEquals(new A().doStuff(l), __); + } + + @Koan + public void methodPreferenceMore() { + // What happens if you change 'Integer' to 'Double' + // Does this explain 'methodPreferenceDouble'? + // Think about why this happens? + assertEquals(new A().doStuff(1, Integer.valueOf(2)), __); + } } diff --git a/koans/src/beginner/AboutObjects.java b/koans/src/beginner/AboutObjects.java index db6ba6dd..b93e5e28 100755 --- a/koans/src/beginner/AboutObjects.java +++ b/koans/src/beginner/AboutObjects.java @@ -1,77 +1,65 @@ package beginner; +import com.sandwich.koan.Koan; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; + import static com.sandwich.koan.constant.KoanConstants.__; import static com.sandwich.util.Assert.assertEquals; -import com.sandwich.koan.Koan; - public class AboutObjects { - @Koan() - public void objectEqualsNull(){ - // does a new object instance equal the null keyword? - assertEquals(new Object().equals(null), __); - } - - @Koan() - public void objectEqualsSelf(){ - Object obj = new Object(); - // does a new object equal itself? - assertEquals(obj.equals(obj), __); - } - - @Koan() - public void objectIdentityEqualityIsTrueWhenReferringToSameObject(){ - Object objectReference = new Object(); - Object referenceToSameObject = objectReference; - // does a new object == itself? - assertEquals(objectReference == referenceToSameObject, __); - } - - @Koan() - public void subclassesEqualsMethodIsLooserThanDoubleEquals(){ - Integer integer0 = new Integer(0); - Integer integer1 = new Integer(0); - assertEquals(integer0.equals(integer1), __); - } - - @Koan() - public void doubleEqualsOperatorEvalutesToTrueOnlyWithSameInstance(){ - Integer integer0 = new Integer(0); - Integer integer1 = integer0; // <- assigning same instance to different reference - assertEquals(integer0 == integer1, __); - } - - @Koan() - public void doubleEqualsOperatorEvalutesToFalseWithDifferentInstances(){ - Integer integer0 = new Integer(0); - Integer integer1 = new Integer(0); // <- new keyword is generating new object instance - assertEquals(integer0 == integer1, __); - } - - @Koan() - public void objectToString(){ - Object object = new Object(); - // TODO: Why is it best practice to ALWAYS override toString? - assertEquals((new StringBuilder()).append(Object.class.getName()) - .append('@') - .append(Integer.toHexString(object.hashCode())).toString(), __); //object.toString() - } - - @Koan() - public void toStringConcatenates(){ - final String string = "ha"; - Object object = new Object(){ - @Override public String toString(){ - return string; - } - }; - assertEquals(string + object, __); - } + @Koan + public void newObjectInstancesCanBeCreatedDirectly() { + assertEquals(new Object() instanceof Object, __); + } + + @Koan + public void allClassesInheritFromObject() { + class Foo { + } + + Class[] ancestors = getAncestors(new Foo()); + assertEquals(ancestors[0], __); + assertEquals(ancestors[1], __); + } + + @Koan + public void objectToString() { + Object object = new Object(); + // TODO: Why is it best practice to ALWAYS override toString? + String expectedToString = MessageFormat.format("{0}@{1}", Object.class.getName(), Integer.toHexString(object.hashCode())); + assertEquals(expectedToString, __); // hint: object.toString() + } + + @Koan + public void toStringConcatenates() { + final String string = "ha"; + Object object = new Object() { + @Override + public String toString() { + return string; + } + }; + assertEquals(string + object, __); + } + + @Koan + public void toStringIsTestedForNullWhenInvokedImplicitly() { + String string = "string"; + assertEquals(string + null, __); + } + + private Class[] getAncestors(Object object) { + List> ancestors = new ArrayList>(); + Class clazz = object.getClass(); + while (clazz != null) { + ancestors.add(clazz); + clazz = clazz.getSuperclass(); + } + return ancestors.toArray(new Class[]{}); + } - @Koan() - public void toStringIsTestedForNullWhenInvokedImplicitly(){ - String string = "string"; - assertEquals(string+null, __); - } } diff --git a/koans/src/beginner/AboutOperators.java b/koans/src/beginner/AboutOperators.java deleted file mode 100644 index a339cf82..00000000 --- a/koans/src/beginner/AboutOperators.java +++ /dev/null @@ -1,84 +0,0 @@ -package beginner; - -import com.sandwich.koan.Koan; -import static com.sandwich.koan.constant.KoanConstants.__; -import static com.sandwich.util.Assert.assertEquals; - -public class AboutOperators { - - @Koan - public void plusPlusVariablePlusPlus(){ - int i = 1; - assertEquals(++i,__); - assertEquals(i,__); - assertEquals(i++,__); - assertEquals(i,__); - } - - @Koan - public void shortCircuit() { - int i = 1; - int a = 6; // Why did we use a variable here? - // What happens if you replace 'a' with '6' below? - // Try this with an IDE like Eclipse... - if ( (a < 9 ) || (++i < 8) ) i = i + 1; - assertEquals(i,__); - } - - @Koan - public void fullAnd(){ - int i = 1; - if ( true & (++i < 8) ) i = i + 1; - assertEquals(i,__); - } - - @Koan - public void shortCircuitAnd(){ - int i = 1; - if ( true && (i < -28) ) i = i + 1; - assertEquals(i,__); - } - - @Koan - public void aboutXOR() { - int i = 1; - int a = 6; - if ( (a < 9 ) ^ false) i = i + 1; - assertEquals(i,__); - } - - @Koan - public void dontMistakeEqualsForEqualsEquals() { - int i = 1; - boolean a = false; - if (a = true) i++; - assertEquals(a, __); - assertEquals(i,__); - // How could you write the condition 'with a twist' to avoid this trap? - } - - @Koan - public void aboutBitShiftingRightShift() { - int rightShift = 8; - rightShift = rightShift >> 1; - assertEquals(rightShift, __); - } - - @Koan - public void aboutBitShiftingLeftShift() { - int leftShift = 0x80000000; // Is this number positive or negative? - leftShift = leftShift << 1; - assertEquals(leftShift, __); - } - - @Koan - public void aboutBitShiftingRightUnsigned() { - int rightShiftNegativeStaysNegative = 0x80000000; - rightShiftNegativeStaysNegative = rightShiftNegativeStaysNegative >> 4; - assertEquals(rightShiftNegativeStaysNegative, __); - int unsignedRightShift = 0x80000000; // always fills with 0 - unsignedRightShift >>>= 4; // Just like += - assertEquals(unsignedRightShift, __); - } - -} diff --git a/koans/src/beginner/AboutPrimitives.java b/koans/src/beginner/AboutPrimitives.java index a8c02dd7..2f196653 100644 --- a/koans/src/beginner/AboutPrimitives.java +++ b/koans/src/beginner/AboutPrimitives.java @@ -1,48 +1,227 @@ package beginner; +import com.sandwich.koan.Koan; + import static com.sandwich.koan.constant.KoanConstants.__; import static com.sandwich.util.Assert.assertEquals; -import com.sandwich.koan.Koan; - public class AboutPrimitives { - - @Koan() - public void byteSize() { - assertEquals(Byte.SIZE, __); - } - - @Koan() - public void shortSize() { - assertEquals(Short.SIZE, __); - } - - @Koan - public void integerSize() { - assertEquals(Integer.SIZE, __); - } - - @Koan - public void longSize() { - assertEquals(Long.SIZE, __); - } - - @Koan - public void charSizeAndValue() { - // a char basically is an unsigned int - assertEquals(Character.SIZE,__); - assertEquals(Character.MIN_VALUE,__); - assertEquals(Character.MAX_VALUE,__); - } - - // Floating Points - @Koan - public void floatSize() { - assertEquals(Float.SIZE,__); - } - - @Koan - public void doubleSize() { - assertEquals(Double.SIZE, __); - } + + @Koan + public void wholeNumbersAreOfTypeInt() { + assertEquals(getType(1), __); // hint: int.class + } + + @Koan + public void primitivesOfTypeIntHaveAnObjectTypeInteger() { + Object number = 1; + assertEquals(getType(number), __); + + // Primitives can be automatically changed into their object type via a process called auto-boxing + // We will explore this in more detail in intermediate.AboutAutoboxing + } + + @Koan + public void integersHaveAFairlyLargeRange() { + assertEquals(Integer.MIN_VALUE, __); + assertEquals(Integer.MAX_VALUE, __); + } + + @Koan + public void integerSize() { + assertEquals(Integer.SIZE, __); // This is the amount of bits used to store an int + } + + @Koan + public void wholeNumbersCanAlsoBeOfTypeLong() { + assertEquals(getType(1L), __); + } + + @Koan + public void primitivesOfTypeLongHaveAnObjectTypeLong() { + Object number = 1L; + assertEquals(getType(number), __); + } + + @Koan + public void longsHaveALargerRangeThanInts() { + assertEquals(Long.MIN_VALUE, __); + assertEquals(Long.MAX_VALUE, __); + } + + @Koan + public void longSize() { + assertEquals(Long.SIZE, __); + } + + @Koan + public void wholeNumbersCanAlsoBeOfTypeShort() { + assertEquals(getType((short) 1), __); // The '(short)' is called an explicit cast - to type 'short' + } + + @Koan + public void primitivesOfTypeShortHaveAnObjectTypeShort() { + Object number = (short) 1; + assertEquals(getType(number), __); + } + + @Koan + public void shortsHaveASmallerRangeThanInts() { + assertEquals(Short.MIN_VALUE, __); // hint: You'll need an explicit cast + assertEquals(Short.MAX_VALUE, __); + } + + @Koan + public void shortSize() { + assertEquals(Short.SIZE, __); + } + + @Koan + public void wholeNumbersCanAlsoBeOfTypeByte() { + assertEquals(getType((byte) 1), __); + } + + @Koan + public void primitivesOfTypeByteHaveAnObjectTypeByte() { + Object number = (byte) 1; + assertEquals(getType(number), __); + } + + @Koan + public void bytesHaveASmallerRangeThanShorts() { + assertEquals(Byte.MIN_VALUE, __); + assertEquals(Byte.MAX_VALUE, __); + + // Why would you use short or byte considering that you need to do explicit casts? + } + + @Koan + public void byteSize() { + assertEquals(Byte.SIZE, __); + } + + @Koan + public void wholeNumbersCanAlsoBeOfTypeChar() { + assertEquals(getType((char) 1), __); + } + + @Koan + public void singleCharactersAreOfTypeChar() { + assertEquals(getType('a'), __); + } + + @Koan + public void primitivesOfTypeCharHaveAnObjectTypeCharacter() { + Object number = (char) 1; + assertEquals(getType(number), __); + } + + @Koan + public void charsAreNotNegative() { + assertEquals((int) Character.MIN_VALUE, __); + assertEquals((int) Character.MAX_VALUE, __); + + // Why did we cast MIN_VALUE and MAX_VALUE to int? Try it without the cast. + } + + @Koan + public void charSize() { + assertEquals(Character.SIZE, __); + } + + @Koan + public void decimalNumbersAreOfTypeDouble() { + assertEquals(getType(1.0), __); + } + + @Koan + public void primitivesOfTypeDoubleCanBeDeclaredWithoutTheDecimalPoint() { + assertEquals(getType(1d), __); + } + + @Koan + public void primitivesOfTypeDoubleCanBeDeclaredWithExponents() { + assertEquals(getType(1e3), __); + assertEquals(1.0e3, __); + assertEquals(1E3, __); + } + + @Koan + public void primitivesOfTypeDoubleHaveAnObjectTypeDouble() { + Object number = 1.0; + assertEquals(getType(number), __); + } + + @Koan + public void doublesHaveALargeRange() { + assertEquals(Double.MIN_VALUE, __); + assertEquals(Double.MAX_VALUE, __); + } + + @Koan + public void doubleSize() { + assertEquals(Double.SIZE, __); + } + + @Koan + public void decimalNumbersCanAlsoBeOfTypeFloat() { + assertEquals(getType(1f), __); + } + + @Koan + public void primitivesOfTypeFloatCanBeDeclaredWithExponents() { + assertEquals(getType(1e3f), __); + assertEquals(1.0e3f, __); + assertEquals(1E3f, __); + } + + @Koan + public void primitivesOfTypeFloatHaveAnObjectTypeFloat() { + Object number = 1f; + assertEquals(getType(number), __); + } + + @Koan + public void floatsHaveASmallerRangeThanDoubles() { + assertEquals(Float.MIN_VALUE, __); + assertEquals(Float.MAX_VALUE, __); + } + + @Koan + public void floatSize() { + assertEquals(Float.SIZE, __); + } + + private Class getType(int value) { + return int.class; + } + + private Class getType(long value) { + return long.class; + } + + private Class getType(float value) { + return float.class; + } + + private Class getType(double value) { + return double.class; + } + + private Class getType(byte value) { + return byte.class; + } + + private Class getType(char value) { + return char.class; + } + + private Class getType(short value) { + return short.class; + } + + private Class getType(Object value) { + return value.getClass(); + } + } diff --git a/koans/src/beginner/AboutStrings.java b/koans/src/beginner/AboutStrings.java index 01f12b6c..600cfa0a 100644 --- a/koans/src/beginner/AboutStrings.java +++ b/koans/src/beginner/AboutStrings.java @@ -1,53 +1,186 @@ package beginner; +import com.sandwich.koan.Koan; + +import java.text.MessageFormat; + import static com.sandwich.koan.constant.KoanConstants.__; import static com.sandwich.util.Assert.assertEquals; - -import com.sandwich.koan.Koan; +import static com.sandwich.util.Assert.fail; public class AboutStrings { - @Koan - public void implicitStrings(){ - Class c = "just a plain ole string".getClass(); - assertEquals(c, __); - } - - @Koan - public void newString(){ - // very rarely if ever should Strings be created via new String() in - // practice - generally it is redundant, and done repetitively can be slow - String string = new String(); - String empty = ""; - assertEquals(string.equals(empty), __); - } - - @Koan - public void newStringIsRedundant(){ - String stringInstance = "zero"; - String stringReference = new String(stringInstance); - assertEquals(stringInstance.equals(stringReference), __); - } - - @Koan - public void newStringIsNotIdentitical(){ - String stringInstance = "zero"; - String stringReference = new String(stringInstance); - assertEquals(stringInstance == stringReference, __); - } - - @Koan - public void stringConcatination(){ - String one = "one"; - String space = " "; - String two = "two"; - assertEquals(one+space+two, __); - } - - @Koan - public void efficientStringConcatenation(){ - // the above implicit string concatenation looks nice - but, it is expensive. - // it creates a new string instance on each concatenation. here's a better alternative: - assertEquals(new StringBuilder("one").append(" ").append("two").toString(), __); - } + @Koan + public void implicitStrings() { + assertEquals("just a plain ole string".getClass(), __); + } + + @Koan + public void newString() { + // very rarely if ever should Strings be created via new String() in + // practice - generally it is redundant, and done repetitively can be slow + String string = new String(); + String empty = ""; + assertEquals(string.equals(empty), __); + } + + @Koan + public void newStringIsRedundant() { + String stringInstance = "zero"; + String stringReference = new String(stringInstance); + assertEquals(stringInstance.equals(stringReference), __); + } + + @Koan + public void newStringIsNotIdentical() { + String stringInstance = "zero"; + String stringReference = new String(stringInstance); + assertEquals(stringInstance == stringReference, __); + } + + @Koan + public void stringIsEmpty() { + assertEquals("".isEmpty(), __); + assertEquals("one".isEmpty(), __); + assertEquals(new String().isEmpty(), __); + assertEquals(new String("").isEmpty(), __); + assertEquals(new String("one").isEmpty(), __); + } + + @Koan + public void stringLength() { + assertEquals("".length(), __); + assertEquals("one".length(), __); + assertEquals("the number is one".length(), __); + } + + @Koan + public void stringTrim() { + assertEquals("".trim(), __); + assertEquals("one".trim(), "one"); + assertEquals(" one more time".trim(), __); + assertEquals(" one more time ".trim(), __); + assertEquals(" and again\t".trim(), __); + assertEquals("\t\t\twhat about now?\t".trim(), __); + } + + @Koan + public void stringConcatenation() { + String one = "one"; + String space = " "; + String two = "two"; + assertEquals(one + space + two, __); + assertEquals(space + one + two, __); + assertEquals(two + space + one, __); + } + + @Koan + public void stringUpperCase() { + String str = "I am a number one!"; + assertEquals(str.toUpperCase(), __); + } + + @Koan + public void stringLowerCase() { + String str = "I AM a number ONE!"; + assertEquals(str.toLowerCase(), __); + } + + @Koan + public void stringCompare() { + String str = "I AM a number ONE!"; + assertEquals(str.compareTo("I AM a number ONE!") == 0, __); + assertEquals(str.compareTo("I am a number one!") == 0, __); + assertEquals(str.compareTo("I AM A NUMBER ONE!") == 0, __); + } + + @Koan + public void stringCompareIgnoreCase() { + String str = "I AM a number ONE!"; + assertEquals(str.compareToIgnoreCase("I AM a number ONE!") == 0, __); + assertEquals(str.compareToIgnoreCase("I am a number one!") == 0, __); + assertEquals(str.compareToIgnoreCase("I AM A NUMBER ONE!") == 0, __); + } + + @Koan + public void stringStartsWith() { + assertEquals("".startsWith("one"), __); + assertEquals("one".startsWith("one"), __); + assertEquals("one is the number".startsWith("one"), __); + assertEquals("ONE is the number".startsWith("one"), __); + } + + @Koan + public void stringEndsWith() { + assertEquals("".endsWith("one"), __); + assertEquals("one".endsWith("one"), __); + assertEquals("the number is one".endsWith("one"), __); + assertEquals("the number is two".endsWith("one"), __); + assertEquals("the number is One".endsWith("one"), __); + } + + @Koan + public void stringSubstring() { + String str = "I AM a number ONE!"; + assertEquals(str.substring(0), __); + assertEquals(str.substring(1), __); + assertEquals(str.substring(5), __); + assertEquals(str.substring(14, 17), __); + assertEquals(str.substring(7, str.length()), __); + } + + @Koan + public void stringContains() { + String str = "I AM a number ONE!"; + assertEquals(str.contains("one"), __); + assertEquals(str.contains("ONE"), __); + } + + @Koan + public void stringReplace() { + String str = "I am a number ONE!"; + assertEquals(str.replace("ONE", "TWO"), __); + assertEquals(str.replace("I am", "She is"), __); + } + + @Koan + public void stringBuilderCanActAsAMutableString() { + assertEquals(new StringBuilder("one").append(" ").append("two").toString(), __); + } + + @Koan + public void readableStringFormattingWithStringFormat() { + assertEquals(String.format("%s %s %s", "a", "b", "a"), __); + } + + @Koan + public void extraArgumentsToStringFormatGetIgnored() { + assertEquals(String.format("%s %s %s", "a", "b", "c", "d"), __); + } + + @Koan + public void insufficientArgumentsToStringFormatCausesAnError() { + try { + String.format("%s %s %s", "a", "b"); + fail("No Exception was thrown!"); + } catch (Exception e) { + assertEquals(e.getClass(), __); + assertEquals(e.getMessage(), __); + } + } + + @Koan + public void readableStringFormattingWithMessageFormat() { + assertEquals(MessageFormat.format("{0} {1} {0}", "a", "b"), __); + } + + @Koan + public void extraArgumentsToMessageFormatGetIgnored() { + assertEquals(MessageFormat.format("{0} {1} {0}", "a", "b", "c"), __); + } + + @Koan + public void insufficientArgumentsToMessageFormatDoesNotReplaceTheToken() { + assertEquals(MessageFormat.format("{0} {1} {0}", "a"), __); + } } diff --git a/koans/src/beginner/AboutVarArgs.java b/koans/src/beginner/AboutVarArgs.java new file mode 100644 index 00000000..d187336b --- /dev/null +++ b/koans/src/beginner/AboutVarArgs.java @@ -0,0 +1,50 @@ +package beginner; + +import com.sandwich.koan.Koan; + +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; + +public class AboutVarArgs { + + class ExampleClass { + public boolean canBeTreatedAsArray(Integer... arguments) { + return arguments instanceof Integer[]; + } + + public int getLength(Integer... arguments) { + return arguments.length; + } + + public String verboseLength(String prefix, Object... arguments) { + return prefix + arguments.length; + } + + // ******* + // The following methods won't compile because Java only permits varargs as last argument + // ******* + // public String invalidMethodDeclaration(String... arguments, String... otherArguments) { return ""; } + // public String otherInvalidMethodDeclaration(String... arguments, String otherArgument) { return ""; } + } + + @Koan + public void varArgsCanBeTreatedAsArrays() { + assertEquals(new ExampleClass().canBeTreatedAsArray(1, 2, 3), __); + } + + @Koan + public void youCanPassInAsManyArgumentsAsYouLike() { + assertEquals(new ExampleClass().getLength(1, 2, 3), __); + assertEquals(new ExampleClass().getLength(1, 2, 3, 4, 5, 6, 7, 8), __); + } + + @Koan + public void youCanPassInZeroArgumentsIfYouLike() { + assertEquals(new ExampleClass().getLength(), __); + } + + @Koan + public void youCanHaveOtherTypesInTheMethodSignature() { + assertEquals(new ExampleClass().verboseLength("This is how many items were passed in: ", 1, 2, 3, 4), __); + } +} diff --git a/koans/src/intermediate/AboutAutoboxing.java b/koans/src/intermediate/AboutAutoboxing.java index 9bd6730f..4063eb2d 100755 --- a/koans/src/intermediate/AboutAutoboxing.java +++ b/koans/src/intermediate/AboutAutoboxing.java @@ -1,52 +1,52 @@ package intermediate; -import static com.sandwich.koan.constant.KoanConstants.__; -import static com.sandwich.util.Assert.assertEquals; +import com.sandwich.koan.Koan; import java.util.ArrayList; import java.util.List; -import com.sandwich.koan.Koan; +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; public class AboutAutoboxing { - @Koan - public void addPrimativesToCollection() { - List list = new ArrayList(); - list.add(0, new Integer(42)); - assertEquals(list.get(0), __); - } - - @Koan - public void addPrimativesToCollectionWithAutoBoxing() { - List list = new ArrayList(); - list.add(0, 42); - assertEquals(list.get(0), __); - } - - @Koan - public void migrateYourExistingCodeToAutoBoxingWithoutFear() { - List list = new ArrayList(); - list.add(0, new Integer(42)); - assertEquals(list.get(0), __); - - list.add(1, 84); - assertEquals(list.get(1), __); - } - - @Koan - public void allPrimativesCanBeAutoboxed() { - List doubleList = new ArrayList(); - doubleList.add(0, new Double(42)); - assertEquals(doubleList.get(0), __); - - List longList = new ArrayList(); - longList.add(0, new Long(42)); - assertEquals(longList.get(0), __); - - List characterList = new ArrayList(); - characterList.add(0, new Character('z')); - assertEquals(characterList.get(0), __); - } - + @Koan + public void addPrimitivesToCollection() { + List list = new ArrayList(); + list.add(0, new Integer(42)); + assertEquals(list.get(0), __); + } + + @Koan + public void addPrimitivesToCollectionWithAutoBoxing() { + List list = new ArrayList(); + list.add(0, 42); + assertEquals(list.get(0), __); + } + + @Koan + public void migrateYourExistingCodeToAutoBoxingWithoutFear() { + List list = new ArrayList(); + list.add(0, new Integer(42)); + assertEquals(list.get(0), __); + + list.add(1, 84); + assertEquals(list.get(1), __); + } + + @Koan + public void allPrimitivesCanBeAutoboxed() { + List doubleList = new ArrayList(); + doubleList.add(0, new Double(42)); + assertEquals(doubleList.get(0), __); + + List longList = new ArrayList(); + longList.add(0, new Long(42)); + assertEquals(longList.get(0), __); + + List characterList = new ArrayList(); + characterList.add(0, new Character('z')); + assertEquals(characterList.get(0), __); + } + } diff --git a/koans/src/intermediate/AboutCollections.java b/koans/src/intermediate/AboutCollections.java index de3dbbba..3d4cca35 100644 --- a/koans/src/intermediate/AboutCollections.java +++ b/koans/src/intermediate/AboutCollections.java @@ -1,133 +1,121 @@ package intermediate; +import com.sandwich.koan.Koan; + +import java.util.*; + import static com.sandwich.koan.constant.KoanConstants.__; import static com.sandwich.util.Assert.assertEquals; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.PriorityQueue; -import java.util.Queue; -import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; -import java.util.TreeSet; -import com.sandwich.koan.Koan; +public class AboutCollections { + @Koan + public void usingAnArrayList() { + // List = interface + // The generic syntax and special generic cases will be handled in + // AboutGenerics. We just use collections here to keep it + // simple. + List list = new ArrayList(); + // ArrayList: simple List implementation + list.add("Chicken"); + list.add("Dog"); + list.add("Chicken"); + assertEquals(list.get(0), __); + assertEquals(list.get(1), __); + assertEquals(list.get(2), __); + } -public class AboutCollections { - - @Koan - public void usingAnArrayList() { - // List = interface - // The generic syntax and special generic cases will be handled in - // AboutGenerics. We just use collections here to keep it - // simple. - List list = new ArrayList(); - // ArrayList: simple List implementation - list.add("Chicken"); - list.add("Dog"); - list.add("Chicken"); - assertEquals(list.get(0), __); - assertEquals(list.get(1), __); - assertEquals(list.get(2), __); - } - - @Koan - public void usingAQueue() { - // Queue = interface - Queue queue = new PriorityQueue(); - // PriorityQueue: simple queue implementation - queue.add("Cat"); - queue.add("Dog"); - assertEquals(queue.peek(), __); - assertEquals(queue.size(), __); - assertEquals(queue.poll(), __); - assertEquals(queue.size(), __); - assertEquals(queue.poll(), __); - assertEquals(queue.isEmpty(), __); - } - - @Koan - public void usingABasicSet() { - Set set = new HashSet(); - set.add("Dog"); - set.add("Cat"); - set.add("Dog"); - assertEquals(set.size(), __); - assertEquals(set.contains("Dog"), __); - assertEquals(set.contains("Cat"), __); - assertEquals(set.contains("Chicken"), __); - } - - @Koan - public void usingABasicMap() { - Map map = new HashMap(); - map.put("first key", "first value"); - map.put("second key", "second value"); - map.put("first key", "other value"); - assertEquals(map.size(), __); - assertEquals(map.containsKey("first key"), __); - assertEquals(map.containsKey("second key"), __); - assertEquals(map.containsValue("first value"), __); - assertEquals(map.get("first key"), __); - } - - @Koan - public void usingBackedArrayList() { - String[] array = {"a","b","c"}; - List list = Arrays.asList(array); - list.set(0, "x"); - assertEquals(array[0], __); - array[0] = "a"; - assertEquals(list.get(0), __); - // Just think of it as quantum state teleportation... - } - - @Koan - public void usingBackedSubMap() { - TreeMap map = new TreeMap(); - map.put("a", "Aha"); - map.put("b", "Boo"); - map.put("c", "Coon"); - map.put("e", "Emu"); - map.put("f", "Fox"); - SortedMap backedMap = map.subMap("c", "f"); - assertEquals(backedMap.size(), __); - assertEquals(map.size(), __); - backedMap.put("d", "Dog"); - assertEquals(backedMap.size(), __); - assertEquals(map.size(), __); - assertEquals(map.containsKey("d"), __); - // Again: backed maps are just like those little quantum states - // that are connected forever... - } - - @Koan - public void differenceBetweenOrderedAndSorted() { - TreeSet sorted = new TreeSet(); - sorted.add("c"); - sorted.add("z"); - sorted.add("a"); - assertEquals(sorted.first(), __); - assertEquals(sorted.last(), __); - // Look at the different constructors for a TreeSet (or TreeMap) - // Ponder how you might influence the sort order. Hold that thought - // until you approach AboutComparison - - LinkedHashSet ordered = new LinkedHashSet(); - ordered.add("c"); - ordered.add("z"); - ordered.add("a"); - StringBuffer sb = new StringBuffer(); - for(String s: ordered) { - sb.append(s); - } - assertEquals(sb.toString(), __); - } + @Koan + public void usingAQueue() { + // Queue = interface + Queue queue = new PriorityQueue(); + // PriorityQueue: simple queue implementation + queue.add("Cat"); + queue.add("Dog"); + assertEquals(queue.peek(), __); + assertEquals(queue.size(), __); + assertEquals(queue.poll(), __); + assertEquals(queue.size(), __); + assertEquals(queue.poll(), __); + assertEquals(queue.isEmpty(), __); + } + + @Koan + public void usingABasicSet() { + Set set = new HashSet(); + set.add("Dog"); + set.add("Cat"); + set.add("Dog"); + assertEquals(set.size(), __); + assertEquals(set.contains("Dog"), __); + assertEquals(set.contains("Cat"), __); + assertEquals(set.contains("Chicken"), __); + } + + @Koan + public void usingABasicMap() { + Map map = new HashMap(); + map.put("first key", "first value"); + map.put("second key", "second value"); + map.put("first key", "other value"); + assertEquals(map.size(), __); + assertEquals(map.containsKey("first key"), __); + assertEquals(map.containsKey("second key"), __); + assertEquals(map.containsValue("first value"), __); + assertEquals(map.get("first key"), __); + } + + @Koan + public void usingBackedArrayList() { + String[] array = {"a", "b", "c"}; + List list = Arrays.asList(array); + list.set(0, "x"); + assertEquals(array[0], __); + array[0] = "a"; + assertEquals(list.get(0), __); + // Just think of it as quantum state teleportation... + } + + @Koan + public void usingBackedSubMap() { + TreeMap map = new TreeMap(); + map.put("a", "Aha"); + map.put("b", "Boo"); + map.put("c", "Coon"); + map.put("e", "Emu"); + map.put("f", "Fox"); + SortedMap backedMap = map.subMap("c", "f"); + assertEquals(backedMap.size(), __); + assertEquals(map.size(), __); + backedMap.put("d", "Dog"); + assertEquals(backedMap.size(), __); + assertEquals(map.size(), __); + assertEquals(map.containsKey("d"), __); + // Again: backed maps are just like those little quantum states + // that are connected forever... + } + + @Koan + public void differenceBetweenOrderedAndSorted() { + TreeSet sorted = new TreeSet(); + sorted.add("c"); + sorted.add("z"); + sorted.add("a"); + assertEquals(sorted.first(), __); + assertEquals(sorted.last(), __); + // Look at the different constructors for a TreeSet (or TreeMap) + // Ponder how you might influence the sort order. Hold that thought + // until you approach AboutComparison + + LinkedHashSet ordered = new LinkedHashSet(); + ordered.add("c"); + ordered.add("z"); + ordered.add("a"); + StringBuffer sb = new StringBuffer(); + for (String s : ordered) { + sb.append(s); + } + assertEquals(sb.toString(), __); + } } diff --git a/koans/src/intermediate/AboutComparison.java b/koans/src/intermediate/AboutComparison.java index 6242f679..ee58ab64 100644 --- a/koans/src/intermediate/AboutComparison.java +++ b/koans/src/intermediate/AboutComparison.java @@ -1,76 +1,83 @@ package intermediate; -import static com.sandwich.koan.constant.KoanConstants.__; -import static com.sandwich.util.Assert.assertEquals; +import com.sandwich.koan.Koan; import java.util.Arrays; import java.util.Comparator; -import com.sandwich.koan.Koan; +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; public class AboutComparison { - @Koan - public void compareObjects() { - String a = "abc"; - String b = "bcd"; - assertEquals(a.compareTo(b), __); - assertEquals(a.compareTo(a), __); - assertEquals(b.compareTo(a), __); - } - - static class Car implements Comparable { - int horsepower; - // For an explanation for this implementation look at - // http://download.oracle.com/javase/6/docs/api/java/lang/Comparable.html#compareTo(T) - public int compareTo(Car o) { - return horsepower - o.horsepower; - } - - } - - @Koan - public void makeObjectsComparable() { - Car vwbeetle = new Car(); - vwbeetle.horsepower = 50; - Car porsche = new Car(); - porsche.horsepower = 300; - assertEquals(vwbeetle.compareTo(porsche), __); - } - - static class RaceHorse { - int speed; - int age; - @Override - public String toString() { - return "Speed: " + speed + " Age: " + age; - } - } - static class HorseSpeedComparator implements Comparator { - public int compare(RaceHorse o1, RaceHorse o2) { - return o1.age - o2.age; - } - } - static class HorseAgeComparator implements Comparator { - public int compare(RaceHorse o1, RaceHorse o2) { - return o1.speed - o2.speed; - } - } - - @Koan - public void makeObjectsComparableWithoutComparable() { - RaceHorse lindy = new RaceHorse(); - lindy.age = 10; lindy.speed = 2; - RaceHorse lightning = new RaceHorse(); - lightning.age = 2; lightning.speed = 10; - RaceHorse slowy = new RaceHorse(); - slowy.age = 12; slowy.speed = 1; - - RaceHorse[] horses = {lindy, slowy, lightning}; - - Arrays.sort(horses, new HorseAgeComparator()); - assertEquals(horses[0], __); - Arrays.sort(horses, new HorseSpeedComparator()); - assertEquals(horses[0], __); - } + @Koan + public void compareObjects() { + String a = "abc"; + String b = "bcd"; + assertEquals(a.compareTo(b), __); + assertEquals(a.compareTo(a), __); + assertEquals(b.compareTo(a), __); + } + + static class Car implements Comparable { + int horsepower; + + // For an explanation for this implementation look at + // http://download.oracle.com/javase/6/docs/api/java/lang/Comparable.html#compareTo(T) + public int compareTo(Car o) { + return horsepower - o.horsepower; + } + + } + + @Koan + public void makeObjectsComparable() { + Car vwbeetle = new Car(); + vwbeetle.horsepower = 50; + Car porsche = new Car(); + porsche.horsepower = 300; + assertEquals(vwbeetle.compareTo(porsche), __); + } + + static class RaceHorse { + int speed; + int age; + + @Override + public String toString() { + return "Speed: " + speed + " Age: " + age; + } + } + + static class HorseSpeedComparator implements Comparator { + public int compare(RaceHorse o1, RaceHorse o2) { + return o1.speed - o2.speed; + } + } + + static class HorseAgeComparator implements Comparator { + public int compare(RaceHorse o1, RaceHorse o2) { + return o1.age - o2.age; + } + } + + @Koan + public void makeObjectsComparableWithoutComparable() { + RaceHorse lindy = new RaceHorse(); + lindy.age = 10; + lindy.speed = 2; + RaceHorse lightning = new RaceHorse(); + lightning.age = 2; + lightning.speed = 10; + RaceHorse slowy = new RaceHorse(); + slowy.age = 12; + slowy.speed = 1; + + RaceHorse[] horses = {lindy, slowy, lightning}; + + Arrays.sort(horses, new HorseAgeComparator()); + assertEquals(horses[0], __); + Arrays.sort(horses, new HorseSpeedComparator()); + assertEquals(horses[0], __); + } } diff --git a/koans/src/intermediate/AboutDates.java b/koans/src/intermediate/AboutDates.java index 4e4923bd..6a157b76 100644 --- a/koans/src/intermediate/AboutDates.java +++ b/koans/src/intermediate/AboutDates.java @@ -1,72 +1,73 @@ package intermediate; -import static com.sandwich.koan.constant.KoanConstants.__; -import static com.sandwich.util.Assert.assertEquals; +import com.sandwich.koan.Koan; import java.text.DateFormat; import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; -import com.sandwich.koan.Koan; +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; public class AboutDates { - - private Date date = new Date(100010001000L); - - @Koan - public void dateToString() { - assertEquals(date.toString(), __); - } - - @Koan - public void changingDateValue() { - int oneHourInMiliseconds = 360000; - date.setTime(date.getTime() + oneHourInMiliseconds); - assertEquals(date.toString(), __); - } - - @Koan - public void usingCalendarToChangeDates() { - Calendar cal = Calendar.getInstance(); - cal.setTime(date); - cal.add(Calendar.MONTH, 1); - assertEquals(cal.getTime().toString(), __); - } - @Koan - public void usingRollToChangeDatesDoesntWrapOtherFields() { - Calendar cal = Calendar.getInstance(); - cal.setTime(date); - cal.roll(Calendar.MONTH, 12); - assertEquals(cal.getTime().toString(), __); - } - - @Koan - public void usingDateFormatToFormatDate() { - String formattedDate = DateFormat.getDateInstance().format(date); - assertEquals(formattedDate, __); - } - - @Koan - public void usingDateFormatToFormatDateShort() { - String formattedDate = DateFormat.getDateInstance(DateFormat.SHORT).format(date); - assertEquals(formattedDate, __); - } - - @Koan - public void usingDateFormatToFormatDateFull() { - String formattedDate = DateFormat.getDateInstance(DateFormat.FULL).format(date); - // There is also DateFormat.MEDIUM and DateFormat.LONG... you get the idea ;-) - assertEquals(formattedDate, __); - } - - @Koan - public void usingDateFormatToParseDates() throws ParseException { - DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT); - Date date2 = dateFormat.parse("Sat Sep 01 12:34:46 CET 2001"); - assertEquals(date2.toString(), __); - // What happened to the time? What do you need to change to keep the time as well? - } + private Date date = new Date(100010001000L); + + @Koan + public void dateToString() { + assertEquals(date.toString(), __); + } + + @Koan + public void changingDateValue() { + int oneHourInMiliseconds = 3600000; + date.setTime(date.getTime() + oneHourInMiliseconds); + assertEquals(date.toString(), __); + } + + @Koan + public void usingCalendarToChangeDates() { + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.add(Calendar.MONTH, 1); + assertEquals(cal.getTime().toString(), __); + } + + @Koan + public void usingRollToChangeDatesDoesntWrapOtherFields() { + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.roll(Calendar.MONTH, 12); + assertEquals(cal.getTime().toString(), __); + } + + @Koan + public void usingDateFormatToFormatDate() { + String formattedDate = DateFormat.getDateInstance().format(date); + assertEquals(formattedDate, __); + } + + @Koan + public void usingDateFormatToFormatDateShort() { + String formattedDate = DateFormat.getDateInstance(DateFormat.SHORT).format(date); + assertEquals(formattedDate, __); + } + + @Koan + public void usingDateFormatToFormatDateFull() { + String formattedDate = DateFormat.getDateInstance(DateFormat.FULL).format(date); + // There is also DateFormat.MEDIUM and DateFormat.LONG... you get the idea ;-) + assertEquals(formattedDate, __); + } + + @Koan + public void usingDateFormatToParseDates() throws ParseException { + DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy"); + Date date2 = dateFormat.parse("01-01-2000"); + assertEquals(date2.toString(), __); + // What happened to the time? What do you need to change to keep the time as well? + } } diff --git a/koans/src/intermediate/AboutEquality.java b/koans/src/intermediate/AboutEquality.java index d6bbd711..f3f50e8e 100644 --- a/koans/src/intermediate/AboutEquality.java +++ b/koans/src/intermediate/AboutEquality.java @@ -1,121 +1,128 @@ package intermediate; import com.sandwich.koan.Koan; + import static com.sandwich.koan.constant.KoanConstants.__; import static com.sandwich.util.Assert.assertEquals; public class AboutEquality { + // This suite of Koans expands on the concepts introduced in beginner.AboutEquality + + @Koan + public void sameObject() { + Object a = new Object(); + Object b = a; + assertEquals(a == b, __); + } + + @Koan + public void equalObject() { + Integer a = new Integer(1); + Integer b = new Integer(1); + assertEquals(a.equals(b), __); + assertEquals(b.equals(a), __); + } + + @Koan + public void noObjectShouldBeEqualToNull() { + assertEquals(new Object().equals(null), __); + } + + static class Car { + private String name = ""; + private int horsepower = 0; + + public Car(String s, int p) { + name = s; + horsepower = p; + } + + @Override + public boolean equals(Object other) { + // Change this implementation to match the equals contract + // Car objects with same horsepower and name values should be considered equal + // http://download.oracle.com/javase/6/docs/api/java/lang/Object.html#equals(java.lang.Object) + return false; + } + + @Override + public int hashCode() { + // @see http://download.oracle.com/javase/6/docs/api/java/lang/Object.html#hashCode() + return super.hashCode(); + } + } + + @Koan + public void equalForOwnObjects() { + Car car1 = new Car("Beetle", 50); + Car car2 = new Car("Beetle", 50); + // @see Car.equals (around line 45) for the place to solve this + assertEquals(car1.equals(car2), true); + assertEquals(car2.equals(car1), true); + } + + @Koan + public void unequalForOwnObjects() { + Car car1 = new Car("Beetle", 50); + Car car2 = new Car("Porsche", 300); + // @see Car.equals (around line 45) for the place to solve this + assertEquals(car1.equals(car2), false); + } + + @Koan + public void unequalForOwnObjectsWithDifferentType() { + Car car1 = new Car("Beetle", 50); + String s = "foo"; + // @see Car.equals (around line 45) for the place to solve this + assertEquals(car1.equals(s), false); + } + + @Koan + public void equalNullForOwnObjects() { + Car car1 = new Car("Beetle", 50); + // @see Car.equals (around line 45) for the place to solve this + assertEquals(car1.equals(null), false); + } + + @Koan + public void ownHashCode() { + // As a general rule: When you override equals you should override + // hash code + // Read the hash code contract to figure out why + // http://download.oracle.com/javase/6/docs/api/java/lang/Object.html#hashCode() + + // Implement Car.hashCode around line 51 so that the following assertions pass + Car car1 = new Car("Beetle", 50); + Car car2 = new Car("Beetle", 50); + assertEquals(car1.equals(car2), true); + assertEquals(car1.hashCode() == car2.hashCode(), true); + } + + static class Chicken { + String color = "green"; + + @Override + public int hashCode() { + return 4000; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof Chicken)) + return false; + return ((Chicken) other).color.equals(color); + } + } + + @Koan + public void ownHashCodeImplementationPartTwo() { + Chicken chicken1 = new Chicken(); + chicken1.color = "black"; + Chicken chicken2 = new Chicken(); + assertEquals(chicken1.equals(chicken2), __); + assertEquals(chicken1.hashCode() == chicken2.hashCode(), __); + // Does this still fit the hashCode contract? Why (not)? + // Fix the Chicken class to correct this. + } - @Koan - public void sameObject() { - Object a = new Object(); - Object b = a; - assertEquals(a == b, __); - } - - @Koan - public void equalObject() { - Integer a = new Integer(1); - Integer b = new Integer(1); - assertEquals(a.equals(b), __); - assertEquals(b.equals(a), __); - } - - @Koan - public void noObjectShouldbeEqualToNull() { - assertEquals(new Object().equals(null), __); - } - - static class Car { - @SuppressWarnings("unused") - private String name = ""; - @SuppressWarnings("unused") - private int horsepower = 0; - public Car(String s, int p) { - name = s; horsepower = p; - } - @Override - public boolean equals(Object other) { - // Change this implementation to match the equals contract - // Car objects with same horsepower and name values should be considered equal - // http://download.oracle.com/javase/6/docs/api/java/lang/Object.html#equals(java.lang.Object) - return false; - } - - @Override - public int hashCode() { - // see koan ownHashCode - return super.hashCode(); - } - } - @Koan - public void equalForOwnObjects() { - Car car1 = new Car("Beetle", 50); - Car car2 = new Car("Beetle", 50); - // See line 37 for the task you have to solve - assertEquals(car1.equals(car2), true); - assertEquals(car2.equals(car1), true); - } - - @Koan - public void unequalForOwnObjects() { - Car car1 = new Car("Beetle", 50); - Car car2 = new Car("Prosche", 300); - // See line 37 for the task you have to solve - assertEquals(car1.equals(car2), false); - } - - @Koan - public void unequalForOwnObjectsWithDifferentType() { - Car car1 = new Car("Beetle", 50); - String s = "foo"; - // See line 37 for the task you have to solve - assertEquals(car1.equals(s), false); - } - - @Koan - public void equalNullForOwnObjects() { - Car car1 = new Car("Beetle", 50); - // See line 37 for the task you have to solve - assertEquals(car1.equals(null), false); - } - - @Koan - public void ownHashCode() { - // As a general rule: When you override equals you should override - // hash code - // Read the hash code contract to figure out why - // http://download.oracle.com/javase/6/docs/api/java/lang/Object.html#hashCode() - - // Implement a hash code version in line 47 so that the following assertions pass - Car car1 = new Car("Beetle", 50); - Car car2 = new Car("Beetle", 50); - assertEquals(car1.equals(car2), true); - assertEquals(car1.hashCode() == car2.hashCode(), true); - } - - static class Chicken { - String color = "green"; - @Override - public int hashCode() { - return 4000; - } - @Override - public boolean equals(Object other) { - if(!(other instanceof Chicken)) - return false; - return ((Chicken)other).color.equals(color); - } - } - - @Koan - public void ownHashCodeImplementationPartTwo() { - Chicken chicken1 = new Chicken(); chicken1.color = "black"; - Chicken chicken2 = new Chicken(); - assertEquals(chicken1.equals(chicken2), __); - assertEquals(chicken1.hashCode() == chicken2.hashCode(), __); - // Does this still fit the hashCode contract? Why? - // If it's valid why is this still not a good idea? - } - } diff --git a/koans/src/intermediate/AboutFileIO.java b/koans/src/intermediate/AboutFileIO.java index c6c63b5b..3fb98e7d 100644 --- a/koans/src/intermediate/AboutFileIO.java +++ b/koans/src/intermediate/AboutFileIO.java @@ -1,80 +1,98 @@ package intermediate; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintWriter; +import com.sandwich.koan.Koan; + +import java.io.*; +import java.util.logging.Logger; import static com.sandwich.koan.constant.KoanConstants.__; import static com.sandwich.util.Assert.assertEquals; -import com.sandwich.koan.Koan; - public class AboutFileIO { - @Koan - public void fileObjectDoesntCreateFile() { - File f = new File("foo.txt"); - assertEquals(f.exists(), __); - } + @Koan + public void fileObjectDoesntCreateFile() { + File f = new File("i-never.exist"); + assertEquals(f.exists(), __); + } + + @Koan + public void fileCreationAndDeletion() throws IOException { + File f = new File("foo.txt"); + f.createNewFile(); + assertEquals(f.exists(), __); + f.delete(); + assertEquals(f.exists(), __); + } - @Koan - public void fileCreationAndDeletion() throws IOException { - File f = new File("foo.txt"); - f.createNewFile(); - assertEquals(f.exists(), __); - f.delete(); - assertEquals(f.exists(), __); - } + @Koan + public void basicFileWritingAndReading() throws IOException { + File file = new File("file.txt"); + FileWriter fw = new FileWriter(file); + String data = "First line\nSecond line"; + fw.write(data); + fw.flush(); + fw.close(); - @Koan - public void basicFileWritingAndReading() throws IOException { - File file = new File("file.txt"); - FileWriter fw = new FileWriter(file); - fw.write("First line\nSecond line"); - fw.flush(); - fw.close(); + char[] in = new char[data.length()]; + int size = 0; + FileReader fr = new FileReader(file); + size = fr.read(in); + // No flush necessary! + fr.close(); + assertEquals(size, __); + String expected = new String(in); + assertEquals(expected.length(), __); + assertEquals(expected, __); + file.delete(); + } - char[] in = new char[50]; - int size = 0; - FileReader fr = new FileReader(file); - size = fr.read(in); - // No flush necessary! - fr.close(); - assertEquals(size, __); - assertEquals(new String(in), __); - file.delete(); - } + @Koan + public void betterFileWritingAndReading() throws IOException { + File file = new File("file.txt"); + file.deleteOnExit(); + FileWriter fw = new FileWriter(file); + PrintWriter pw = new PrintWriter(fw); + pw.println("First line"); + pw.println("Second line"); + pw.close(); - @Koan - public void betterFileWritingAndReading() throws IOException { - File file = new File("file.txt"); - FileWriter fw = new FileWriter(file); - PrintWriter pw = new PrintWriter(fw); - pw.println("First line"); - pw.println("Second line"); - pw.close(); + FileReader fr = new FileReader(file); + BufferedReader br = null; + try { + br = new BufferedReader(fr); + assertEquals(br.readLine(), __); // first line + assertEquals(br.readLine(), __); // second line + assertEquals(br.readLine(), __); // what now? + } finally { + // anytime you open access to a file, you should close it or you may + // lock it from other processes (ie frustrate people) + closeStream(br); + } + } - FileReader fr = new FileReader(file); - BufferedReader br = new BufferedReader(fr); - assertEquals(br.readLine(), __); // first line - assertEquals(br.readLine(), __); // second line - assertEquals(br.readLine(), __); // what now? - } + private void closeStream(BufferedReader br) { + if (br != null) { + try { + br.close(); + } catch (IOException x) { + Logger.getAnonymousLogger().severe("Unable to close reader."); + } + } + } - @Koan - public void directChainingForReadingAndWriting() throws IOException { - File file = new File("file.txt"); - PrintWriter pw = new PrintWriter(file); - pw.println("1. line"); - pw.println("2. line"); - pw.close(); + @Koan + public void directChainingForReadingAndWriting() throws IOException { + File file = new File("file.txt"); + PrintWriter pw = new PrintWriter(file); + pw.println("1. line"); + pw.println("2. line"); + pw.close(); - StringBuffer sb = new StringBuffer(); - // Add the loop to go through the file line by line and add the line - // to the StringBuffer - assertEquals(sb.toString(), "1. line\n2. line"); - } + StringBuffer sb = new StringBuffer(); + // Add the loop to go through the file line by line and add the line + // to the StringBuffer + assertEquals(sb.toString(), "1. line\n2. line"); + } } + diff --git a/koans/src/intermediate/AboutInnerClasses.java b/koans/src/intermediate/AboutInnerClasses.java index eb58fa66..8d9c7d0f 100644 --- a/koans/src/intermediate/AboutInnerClasses.java +++ b/koans/src/intermediate/AboutInnerClasses.java @@ -1,130 +1,150 @@ package intermediate; +import com.sandwich.koan.Koan; + import static com.sandwich.koan.constant.KoanConstants.__; import static com.sandwich.util.Assert.assertEquals; -import com.sandwich.koan.Koan; +public class AboutInnerClasses { -interface Ignorable { - String ignoreAll(); -} + interface Ignoreable { + String ignoreAll(); + } -public class AboutInnerClasses { + class Inner { + public String doStuff() { + return "stuff"; + } + + public int returnOuter() { + return x; + } + } + + @Koan + public void creatingInnerClassInstance() { + Inner someObject = new Inner(); + assertEquals(someObject.doStuff(), __); + } + + @Koan + public void creatingInnerClassInstanceWithOtherSyntax() { + AboutInnerClasses.Inner someObject = this.new Inner(); + assertEquals(someObject.doStuff(), __); + } + + private int x = 10; + + @Koan + public void accessingOuterClassMembers() { + Inner someObject = new Inner(); + assertEquals(someObject.returnOuter(), __); + } + + @Koan + public void innerClassesInMethods() { + class MethodInnerClass { + int oneHundred() { + return 100; + } + } + assertEquals(new MethodInnerClass().oneHundred(), __); + // Where can you use this class? + } + + class AnotherInnerClass { + int thousand() { + return 1000; + } + + AnotherInnerClass crazyReturn() { + class SpecialInnerClass extends AnotherInnerClass { + int thousand() { + return 2000; + } + } + ; + return new SpecialInnerClass(); + } + } + + @Koan + public void innerClassesInMethodsThatEscape() { + AnotherInnerClass ic = new AnotherInnerClass(); + assertEquals(ic.thousand(), __); + AnotherInnerClass theCrazyIC = ic.crazyReturn(); + assertEquals(theCrazyIC.thousand(), __); + } + + int theAnswer() { + return 42; + } + + @Koan + public void creatingAnonymousInnerClasses() { + AboutInnerClasses anonymous = new AboutInnerClasses() { + int theAnswer() { + return 23; + } + };// <- Why do you need a semicolon here? + assertEquals(anonymous.theAnswer(), __); + } + + @Koan + public void creatingAnonymousInnerClassesToImplementInterface() { + Ignoreable ignoreable = new Ignoreable() { + public String ignoreAll() { + return null; + } + }; // Complete the code so that the statement below is correct. + // Look at the koan above for inspiration + assertEquals(ignoreable.ignoreAll(), "SomeInterestingString"); + // Did you just created an object of an interface type? + // Or did you create a class that implemented this interface and + // an object of that type? + } + + @Koan + public void innerClassAndInheritance() { + Inner someObject = new Inner(); + // The statement below is obvious... + // Try to change the 'Inner' below to "AboutInnerClasses' + // Why do you get an error? + // What does that imply for inner classes and inheritance? + assertEquals(someObject instanceof Inner, __); + } + + class OtherInner extends AboutInnerClasses { + } + + @Koan + public void innerClassAndInheritanceOther() { + OtherInner someObject = new OtherInner(); + // What do you expect here? + // Compare this result with the last koan. What does that mean? + assertEquals(someObject instanceof AboutInnerClasses, __); + } + + static class StaticInnerClass { + public int importantNumber() { + return 3; + } + } + + @Koan + public void staticInnerClass() { + StaticInnerClass someObject = new StaticInnerClass(); + assertEquals(someObject.importantNumber(), __); + // What happens if you try to access 'x' or 'theAnswer' from the outer class? + // What does this mean for static inner classes? + // Try to create a sub package of this package which is named 'StaticInnerClass' + // Does it work? Why not? + } - class Inner { - public String doStuff() { return "stuff"; } - public int returnOuter() { return x; } - } - - @Koan - public void creatingInnerClassInstance() { - Inner someObject = new Inner(); - assertEquals(someObject.doStuff(),__); - } - - @Koan - public void creatingInnerClassInstanceWithOtherSyntax() { - AboutInnerClasses.Inner someObject = this.new Inner(); - assertEquals(someObject.doStuff(),__); - } - - private int x = 10; - - @Koan - public void accessingOuterClassMembers() { - Inner someObject = new Inner(); - assertEquals(someObject.returnOuter(),__); - } - - @Koan - public void innerClassesInMethods() { - class MethodInnerClass { - int oneHundred() { return 100; } - }; // <- Why do you need a semicolon here? - assertEquals(new MethodInnerClass().oneHundred(), __); - // Where can you use this class? - } - - class AnotherInnerClass { - int thousand() { return 1000; } - - AnotherInnerClass crazyReturn() { - class SpecialInnerClass extends AnotherInnerClass { - int thousand() { - return 2000; - } - }; - return new SpecialInnerClass(); - } - } - - @Koan - public void innerClassesInMethodsThatEscape() { - AnotherInnerClass ic = new AnotherInnerClass(); - assertEquals(ic.thousand(), __); - AnotherInnerClass theCrazyIC = ic.crazyReturn(); - assertEquals(theCrazyIC.thousand(), __); - } - - int theAnswer() { return 42; } - - @Koan - public void creatingAnonymousInnerClasses() { - AboutInnerClasses anonymous = new AboutInnerClasses() { - int theAnswer() { return 23; } - }; - assertEquals(anonymous.theAnswer(), __); - } - - @SuppressWarnings("null") - @Koan - public void creatingAnonymousInnerClassesToImplementInterface() { - Ignorable ignorable = null; // Complete the code so that the statement below is correct. - // Look at the koan above for inspiration - assertEquals(ignorable.ignoreAll(), "SomeInterestingString"); - // Did you just created an object of an interface type? - // Or did you create a class that implemented this interface and - // an object of that type? - } - - @Koan - public void innerClassAndInheritance() { - Inner someObject = new Inner(); - // The statement below is obvious... - // Try to change the 'Inner' below to "AboutInnerClasses' - // Why do you get an error? - // What does that imply for inner classes and inheritance? - assertEquals(someObject instanceof Inner, __); - } - - class OtherInner extends AboutInnerClasses { } - - @Koan - public void innerClassAndInheritanceOther() { - OtherInner someObject = new OtherInner(); - // What do you expect here? - // Compare this result with the last koan. What does that mean? - assertEquals(someObject instanceof AboutInnerClasses, __); - } - - static class StaticInnerClass { - public int importantNumber() { return 3; } - } - - @Koan - public void staticInnerClass() { - StaticInnerClass someObject = new StaticInnerClass(); - assertEquals(someObject.importantNumber(), __); - // What happens if you try to access 'x' or 'theAnswer' from the outer class? - // What does this mean for static inner classes? - // Try to create a sub package of this package which is named 'StaticInnerClass' - // Does it work? Why not? - } - - @Koan - public void staticInnerClassFullyQualified() { - AboutInnerClasses.StaticInnerClass someObject = new AboutInnerClasses.StaticInnerClass(); - assertEquals(someObject.importantNumber(), __); - } + @Koan + public void staticInnerClassFullyQualified() { + AboutInnerClasses.StaticInnerClass someObject = new AboutInnerClasses.StaticInnerClass(); + assertEquals(someObject.importantNumber(), __); + } } diff --git a/koans/src/intermediate/AboutLocale.java b/koans/src/intermediate/AboutLocale.java index 18d7a210..48729987 100644 --- a/koans/src/intermediate/AboutLocale.java +++ b/koans/src/intermediate/AboutLocale.java @@ -1,7 +1,6 @@ package intermediate; -import static com.sandwich.koan.constant.KoanConstants.__; -import static com.sandwich.util.Assert.assertEquals; +import com.sandwich.koan.Koan; import java.text.DateFormat; import java.text.NumberFormat; @@ -9,42 +8,43 @@ import java.util.Date; import java.util.Locale; -import com.sandwich.koan.Koan; +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; public class AboutLocale { - @Koan - public void localizedOutputOfDates() { - Calendar cal = Calendar.getInstance(); - cal.set(2011, 3, 3); - Date date = cal.getTime(); - Locale localeBR = new Locale("pt","BR"); // portuguese, Brazil - DateFormat dateformatBR = DateFormat.getDateInstance(DateFormat.FULL, localeBR); - assertEquals(dateformatBR.format(date), __); - - Locale localeJA = new Locale("ja"); // Japan - DateFormat dateformatJA = DateFormat.getDateInstance(DateFormat.FULL, localeJA); - // Well if you don't know how to type these characters, try "de", "it" or "us" ;-) - assertEquals(dateformatJA.format(date), __); - } - - @Koan - public void getCountryInformation() { - Locale locBR = new Locale("pt","BR"); - assertEquals(locBR.getDisplayCountry(), __); - assertEquals(locBR.getDisplayCountry(locBR), __); - - Locale locCH = new Locale("it","CH"); - assertEquals(locCH.getDisplayCountry(), __); - assertEquals(locCH.getDisplayCountry(locCH), __); - assertEquals(locCH.getDisplayCountry(new Locale("de","CH")), __); - } - - @Koan - public void formatCurrency() { - float someAmount = 442.23f; // Don't use floats for money in real life. Really. It's a bad idea. - Locale locBR = new Locale("pt","BR"); - NumberFormat nf = NumberFormat.getCurrencyInstance(locBR); - assertEquals(nf.format(someAmount), __); - } + @Koan + public void localizedOutputOfDates() { + Calendar cal = Calendar.getInstance(); + cal.set(2011, 3, 3); + Date date = cal.getTime(); + Locale localeBR = new Locale("pt", "BR"); // portuguese, Brazil + DateFormat dateformatBR = DateFormat.getDateInstance(DateFormat.FULL, localeBR); + assertEquals(dateformatBR.format(date), __); + + Locale localeJA = new Locale("ja"); // Japan + DateFormat dateformatJA = DateFormat.getDateInstance(DateFormat.FULL, localeJA); + // Well if you don't know how to type these characters, try "de", "it" or "us" ;-) + assertEquals(dateformatJA.format(date), __); + } + + @Koan + public void getCountryInformation() { + Locale locBR = new Locale("pt", "BR"); + assertEquals(locBR.getDisplayCountry(), __); + assertEquals(locBR.getDisplayCountry(locBR), __); + + Locale locCH = new Locale("it", "CH"); + assertEquals(locCH.getDisplayCountry(), __); + assertEquals(locCH.getDisplayCountry(locCH), __); + assertEquals(locCH.getDisplayCountry(new Locale("de", "CH")), __); + } + + @Koan + public void formatCurrency() { + float someAmount = 442.23f; // Don't use floats for money in real life. Really. It's a bad idea. + Locale locBR = new Locale("pt", "BR"); + NumberFormat nf = NumberFormat.getCurrencyInstance(locBR); + assertEquals(nf.format(someAmount), __); + } } diff --git a/koans/src/intermediate/AboutRegularExpressions.java b/koans/src/intermediate/AboutRegularExpressions.java index ad0ad011..d5999645 100644 --- a/koans/src/intermediate/AboutRegularExpressions.java +++ b/koans/src/intermediate/AboutRegularExpressions.java @@ -1,57 +1,57 @@ package intermediate; -import static com.sandwich.koan.constant.KoanConstants.__; -import static com.sandwich.util.Assert.assertEquals; +import com.sandwich.koan.Koan; import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.sandwich.koan.Koan; +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; public class AboutRegularExpressions { - - @Koan - public void basicMatching() { - Pattern p = Pattern.compile("xyz"); - Matcher m = p.matcher("xyzxxxxyz"); - // index 012345678 - assertEquals(m.find(), __); - assertEquals(m.start(), __); - assertEquals(m.find(), __); - assertEquals(m.start(), __); - assertEquals(m.find(), __); - } - - @Koan - public void extendedMatching() { - Pattern p = Pattern.compile("x.z"); - Matcher m = p.matcher("xyz u x z u xfz"); - // index 012345678901234 - assertEquals(m.find(), __); - assertEquals(m.start(), __); - assertEquals(m.find(), __); - assertEquals(m.start(), __); - assertEquals(m.find(), __); - assertEquals(m.start(), __); - } - - @Koan - public void escapingMetaCharacters() { - Pattern p = Pattern.compile("end\\."); - Matcher m = p.matcher("begin. end."); - // index 01234567890 - assertEquals(m.find(), __); - assertEquals(m.start(), __); - } - - @Koan - public void splittingStrings() { - String csvDataLine = "1,name,description"; - String[] data = csvDataLine.split(","); // you can use any regex here - assertEquals(data[0], __); - assertEquals(data[1], __); - assertEquals(data[2], __); - } + + @Koan + public void basicMatching() { + Pattern p = Pattern.compile("xyz"); + Matcher m = p.matcher("xyzxxxxyz"); + // index 012345678 + assertEquals(m.find(), __); + assertEquals(m.start(), __); + assertEquals(m.find(), __); + assertEquals(m.start(), __); + assertEquals(m.find(), __); + } + + @Koan + public void extendedMatching() { + Pattern p = Pattern.compile("x.z"); + Matcher m = p.matcher("xyz u x z u xfz"); + // index 012345678901234 + assertEquals(m.find(), __); + assertEquals(m.start(), __); + assertEquals(m.find(), __); + assertEquals(m.start(), __); + assertEquals(m.find(), __); + assertEquals(m.start(), __); + } + + @Koan + public void escapingMetaCharacters() { + Pattern p = Pattern.compile("end\\."); + Matcher m = p.matcher("begin. end."); + // index 01234567890 + assertEquals(m.find(), __); + assertEquals(m.start(), __); + } + + @Koan + public void splittingStrings() { + String csvDataLine = "1,name,description"; + String[] data = csvDataLine.split(","); // you can use any regex here + assertEquals(data[0], __); + assertEquals(data[1], __); + assertEquals(data[2], __); + } } diff --git a/koans/src/intermediate/AboutSerialization.java b/koans/src/intermediate/AboutSerialization.java index 141c793b..b4b428e7 100644 --- a/koans/src/intermediate/AboutSerialization.java +++ b/koans/src/intermediate/AboutSerialization.java @@ -1,173 +1,224 @@ package intermediate; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.NotSerializableException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; - import com.sandwich.koan.Koan; + +import java.io.*; +import java.util.logging.Logger; + import static com.sandwich.koan.constant.KoanConstants.__; import static com.sandwich.util.Assert.assertEquals; public class AboutSerialization { - - @Koan - public void simpleSerialization() throws FileNotFoundException, IOException, ClassNotFoundException { - String s = "Hello world"; - // serialize - ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("SerializeFile")); - os.writeObject(s); - os.close(); - - // deserialize - ObjectInputStream is = new ObjectInputStream(new FileInputStream("SerializeFile")); - String otherString = (String)is.readObject(); - assertEquals(otherString, __); - } - - static class Starship implements Serializable { - - // Although it is not enforced, you should define this constant - // to make sure you serialize/deserialize only compatible versions - // of your objects - private static final long serialVersionUID = 1L; - int maxWarpSpeed; - } - - @Koan - public void customObjectSerialization() throws IOException, ClassNotFoundException { - Starship s = new Starship(); - s.maxWarpSpeed = 9; - ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("SerializeFile")); - os.writeObject(s); - os.close(); - - ObjectInputStream is = new ObjectInputStream(new FileInputStream("SerializeFile")); - Starship onTheOtherSide = (Starship)is.readObject(); - - assertEquals(onTheOtherSide.maxWarpSpeed, __); - } - - static class Engine { - String type; - public Engine(String t) { type = t; } - }; - - @SuppressWarnings("serial") - static class Car implements Serializable { - // Transient means: Ignore field for serialization - transient Engine engine; - // Notice these methods are private and will be called by the JVM - // internally - as if they where defined by the Serializable interface - // but they aren't defined as part of the interface - private void writeObject(ObjectOutputStream os) throws IOException { - os.defaultWriteObject(); - os.writeObject(engine.type); - } - private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { - is.defaultReadObject(); - engine = new Engine((String)is.readObject()); - } - } - - @Koan - public void customObjectSerializationWithTransientFields() throws FileNotFoundException, IOException, ClassNotFoundException { - // Note that this kind of access of fields is not good OO practice. - // But let's focus on serialization here :) - Car car = new Car(); - car.engine = new Engine("diesel"); - ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("SerializeFile")); - os.writeObject(car); - os.close(); - - ObjectInputStream is = new ObjectInputStream(new FileInputStream("SerializeFile")); - Car deserializedCar = (Car)is.readObject(); - - assertEquals(deserializedCar.engine.type, __); - } - - @SuppressWarnings("serial") - class Boat implements Serializable { - Engine engine; - } - - @Koan - public void customSerializationWithUnserializableFields() throws FileNotFoundException, IOException { - Boat car = new Boat(); - car.engine = new Engine("diesel"); - ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("SerializeFile")); - String marker = "Start "; - try { - os.writeObject(car); - } catch(NotSerializableException e) { - marker += "Exception"; - } - os.close(); - assertEquals(marker, __); - } - - @SuppressWarnings("serial") - static class Animal implements Serializable { - String name; - public Animal(String s) { - name = s; - } - } - - @SuppressWarnings("serial") - static class Dog extends Animal { - public Dog(String s) { - super(s); - } - } - - @Koan - public void serializeWithInteritance() throws IOException, ClassNotFoundException { - Dog d = new Dog("snoopy"); - ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("SerializeFile")); - os.writeObject(d); - os.close(); - - ObjectInputStream is = new ObjectInputStream(new FileInputStream("SerializeFile")); - Dog otherDog = (Dog)is.readObject(); - assertEquals(otherDog.name, __); - } - - static class Plane { - String name; - public Plane(String s) { - name = s; - } - public Plane() {}; - } - @SuppressWarnings("serial") - static class MilitaryPlane extends Plane implements Serializable { - public MilitaryPlane(String s) { - super(s); - } - } - - @Koan - public void serializeWithInheritanceWhenParentNotserializable() throws FileNotFoundException, IOException, ClassNotFoundException { - MilitaryPlane p = new MilitaryPlane("F22"); - - ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("SerializeFile")); - os.writeObject(p); - os.close(); - - ObjectInputStream is = new ObjectInputStream(new FileInputStream("SerializeFile")); - MilitaryPlane otherPlane = (MilitaryPlane)is.readObject(); - // Does this surprise you? - assertEquals(otherPlane.name, __); - - // Think about how serialization creates objects... - // It does not use constructors! But if a parent object is not serializable - // it actually uses contstructors and if the fields are not in a serializable class... - // unexpected things - like this - may happen - } + + @Koan + public void simpleSerialization() throws FileNotFoundException, IOException, ClassNotFoundException { + String s = "Hello world"; + // serialize + File file = new File("SerializeFile"); + file.deleteOnExit(); + ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(file)); + os.writeObject(s); + os.close(); + + // deserialize + ObjectInputStream is = null; + try { + is = new ObjectInputStream(new FileInputStream("SerializeFile")); + String otherString = (String) is.readObject(); + assertEquals(otherString, __); + } finally { + closeStream(is); + } + } + + static class Starship implements Serializable { + + // Although it is not enforced, you should define this constant + // to make sure you serialize/deserialize only compatible versions + // of your objects + private static final long serialVersionUID = 1L; + int maxWarpSpeed; + } + + @Koan + public void customObjectSerialization() throws IOException, ClassNotFoundException { + Starship s = new Starship(); + s.maxWarpSpeed = 9; + File file = new File("SerializeFile"); + file.deleteOnExit(); + ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(file)); + os.writeObject(s); + os.close(); + + ObjectInputStream is = null; + try { + is = new ObjectInputStream(new FileInputStream("SerializeFile")); + Starship onTheOtherSide = (Starship) is.readObject(); + assertEquals(onTheOtherSide.maxWarpSpeed, __); + } finally { + closeStream(is); + } + } + + static class Engine { + String type; + + public Engine(String t) { + type = t; + } + } + + @SuppressWarnings("serial") + static class Car implements Serializable { + // Transient means: Ignore field for serialization + transient Engine engine; + + // Notice these methods are private and will be called by the JVM + // internally - as if they where defined by the Serializable interface + // but they aren't defined as part of the interface + private void writeObject(ObjectOutputStream os) throws IOException { + os.defaultWriteObject(); + os.writeObject(engine.type); + } + + private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { + is.defaultReadObject(); + engine = new Engine((String) is.readObject()); + } + } + + @Koan + public void customObjectSerializationWithTransientFields() throws FileNotFoundException, IOException, ClassNotFoundException { + // Note that this kind of access of fields is not good OO practice. + // But let's focus on serialization here :) + Car car = new Car(); + car.engine = new Engine("diesel"); + File file = new File("SerializeFile"); + file.deleteOnExit(); + ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(file)); + os.writeObject(car); + os.close(); + + ObjectInputStream is = null; + try { + is = new ObjectInputStream(new FileInputStream("SerializeFile")); + Car deserializedCar = (Car) is.readObject(); + assertEquals(deserializedCar.engine.type, __); + } finally { + closeStream(is); + } + } + + @SuppressWarnings("serial") + class Boat implements Serializable { + Engine engine; + } + + @Koan + public void customSerializationWithUnserializableFields() throws FileNotFoundException, IOException { + Boat boat = new Boat(); + boat.engine = new Engine("diesel"); + File file = new File("SerializeFile"); + file.deleteOnExit(); + ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(file)); + String marker = "Start "; + try { + os.writeObject(boat); + } catch (NotSerializableException e) { + marker += "Exception"; + } + os.close(); + assertEquals(marker, __); + } + + @SuppressWarnings("serial") + static class Animal implements Serializable { + String name; + + public Animal(String s) { + name = s; + } + } + + @SuppressWarnings("serial") + static class Dog extends Animal { + public Dog(String s) { + super(s); + } + } + + @Koan + public void serializeWithInheritance() throws IOException, ClassNotFoundException { + Dog d = new Dog("snoopy"); + File file = new File("SerializeFile"); + file.deleteOnExit(); + ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(file)); + os.writeObject(d); + os.close(); + + ObjectInputStream is = null; + try { + is = new ObjectInputStream(new FileInputStream("SerializeFile")); + Dog otherDog = (Dog) is.readObject(); + assertEquals(otherDog.name, __); + } finally { + closeStream(is); + } + } + + static class Plane { + String name; + + public Plane(String s) { + name = s; + } + + public Plane() { + } + + } + + @SuppressWarnings("serial") + static class MilitaryPlane extends Plane implements Serializable { + public MilitaryPlane(String s) { + super(s); + } + } + + @Koan + public void serializeWithInheritanceWhenParentNotSerializable() throws FileNotFoundException, IOException, ClassNotFoundException { + MilitaryPlane p = new MilitaryPlane("F22"); + + ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("SerializeFile")); + os.writeObject(p); + os.close(); + + ObjectInputStream is = null; + try { + is = new ObjectInputStream(new FileInputStream("SerializeFile")); + MilitaryPlane otherPlane = (MilitaryPlane) is.readObject(); + // Does this surprise you? + assertEquals(otherPlane.name, __); + + // Think about how serialization creates objects... + // It does not use constructors! But if a parent object is not serializable + // it actually uses constructors and if the fields are not in a serializable class... + // unexpected things - like this - may happen + } finally { + closeStream(is); + } + } + + private void closeStream(ObjectInputStream ois) { + if (ois != null) { + try { + ois.close(); + } catch (IOException x) { + Logger.getAnonymousLogger().severe("Unable to close reader."); + } + } + } + } + diff --git a/koans/src/java7/AboutDiamondOperator.java b/koans/src/java7/AboutDiamondOperator.java new file mode 100644 index 00000000..a9c32ac0 --- /dev/null +++ b/koans/src/java7/AboutDiamondOperator.java @@ -0,0 +1,36 @@ +package java7; + +import com.sandwich.koan.Koan; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; + +public class AboutDiamondOperator { + + @Koan + public void diamondOperator() { + String[] animals = {"Dog", "Cat", "Tiger", "Elephant", "Zebra"}; + //Generic type of array list inferred - empty <> operator + List animalsList = new ArrayList<>(Arrays.asList(animals)); + assertEquals(animalsList, __); + } + + @Koan + public void diamondOperatorInMethodCall() { + String[] animals = {"Dog", "Cat", "Tiger", "Elephant", "Zebra"}; + //type of new ArrayList<>() inferred from method parameter + List animalsList = fill(new ArrayList<>()); + assertEquals(animalsList, __); + } + + private List fill(List list) { + String[] animals = {"Dog", "Cat", "Tiger", "Elephant", "Zebra"}; + list.addAll(Arrays.asList(animals)); + return list; + } + +} diff --git a/koans/src/java7/AboutJava7LiteralsEnhancements.java b/koans/src/java7/AboutJava7LiteralsEnhancements.java new file mode 100644 index 00000000..4d8e2a38 --- /dev/null +++ b/koans/src/java7/AboutJava7LiteralsEnhancements.java @@ -0,0 +1,44 @@ +package java7; + +import com.sandwich.koan.Koan; + +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; + +public class AboutJava7LiteralsEnhancements { + + @Koan + public void binaryLiterals() { + //binary literals are marked with 0b prefix + short binaryLiteral = 0b1111; + assertEquals(binaryLiteral, __); + } + + @Koan + public void binaryLiteralsWithUnderscores() { + //literals can use underscores for improved readability + short binaryLiteral = 0b1111_1111; + assertEquals(binaryLiteral, __); + } + + @Koan + public void numericLiteralsWithUnderscores() { + long literal = 111_111_111L; + //notice capital "B" - a valid binary literal prefix + short multiplier = 0B1_000; + assertEquals(literal * multiplier, __); + } + + @Koan + public void negativeBinaryLiteral() { + int negativeBinaryLiteral = 0b1111_1111_1111_1111_1111_1111_1111_1100 / 4; + assertEquals(negativeBinaryLiteral, __); + } + + @Koan + public void binaryLiteralsWithBitwiseOperator() { + int binaryLiteral = ~0b1111_1111; + assertEquals(binaryLiteral, __); + } + +} diff --git a/koans/src/java7/AboutRequireNotNull.java b/koans/src/java7/AboutRequireNotNull.java new file mode 100644 index 00000000..de4b017f --- /dev/null +++ b/koans/src/java7/AboutRequireNotNull.java @@ -0,0 +1,46 @@ +package java7; + +import com.sandwich.koan.Koan; + +import java.util.Objects; + +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; + +public class AboutRequireNotNull { + + @Koan + public void failArgumentValidationWithRequireNotNull() { + // This koan demonstrates the use of Objects.requireNotNull + // in place of traditional argument validation using exceptions + String s = ""; + try { + s += validateUsingRequireNotNull(null); + } catch (NullPointerException ex) { + s = "caught a NullPointerException"; + } + assertEquals(s, __); + } + + @Koan + public void passArgumentValidationWithRequireNotNull() { + // This koan demonstrates the use of Objects.requireNotNull + // in place of traditional argument validation using exceptions + String s = ""; + try { + s += validateUsingRequireNotNull("valid"); + } catch (NullPointerException ex) { + s = "caught a NullPointerException"; + } + assertEquals(s, __); + } + + private int validateUsingRequireNotNull(String str) { + // If you're only concerned with null values requireNotNull + // is concise and the point of the NullPointerException it + // throws is clear, though you can optionally provide a + // description as well + return Objects.requireNonNull(str).length(); + } + +} diff --git a/koans/src/java7/AboutStringsInSwitch.java b/koans/src/java7/AboutStringsInSwitch.java new file mode 100644 index 00000000..31a79c6f --- /dev/null +++ b/koans/src/java7/AboutStringsInSwitch.java @@ -0,0 +1,30 @@ +package java7; + +import com.sandwich.koan.Koan; + +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; + +public class AboutStringsInSwitch { + + @Koan + public void stringsInSwitchStatement() { + String[] animals = {"Dog", "Cat", "Tiger", "Elephant", "Zebra"}; + String dangerous = null; + String notDangerous = null; + for (String animal : animals) { + switch (animal) { + case "Tiger": + dangerous = animal; + case "Dog": + case "Cat": + case "Elephant": + case "Zebra": + notDangerous = animal; + } + } + assertEquals(notDangerous, __); + assertEquals(dangerous, __); + } + +} diff --git a/koans/src/java7/AboutTryWithResources.java b/koans/src/java7/AboutTryWithResources.java new file mode 100644 index 00000000..4934b1e1 --- /dev/null +++ b/koans/src/java7/AboutTryWithResources.java @@ -0,0 +1,109 @@ +package java7; + +import com.sandwich.koan.Koan; + +import java.io.*; + +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; + +public class AboutTryWithResources { + + class AutoClosableResource implements AutoCloseable { + public void foo() throws WorkException { + throw new WorkException("Exception thrown while working"); + } + + public void close() throws CloseException { + throw new CloseException("Exception thrown while closing"); + } + } + + class WorkException extends Exception { + public WorkException(String message) { + super(message); + } + } + + class CloseException extends Exception { + public CloseException(String message) { + super(message); + } + } + + @Koan + public void lookMaNoClose() { + String str = "first line" + + System.lineSeparator() + + "second line"; + InputStream is = new ByteArrayInputStream(str.getBytes()); + String line; + /* BufferedReader implementing @see java.lang.AutoCloseable interface */ + try (BufferedReader br = + new BufferedReader( + new InputStreamReader(is))) { + line = br.readLine(); + //br guaranteed to be closed + } catch (IOException e) { + line = "error"; + } + assertEquals(line, __); + } + + @Koan + public void lookMaNoCloseWithException() throws IOException { + String line = "no need to close readers"; + try (BufferedReader br = + new BufferedReader( + new FileReader("I do not exist!"))) { + line = br.readLine(); + } catch (FileNotFoundException e) { + line = "no more leaking!"; + } + assertEquals(line, __); + } + + @Koan + public void lookMaNoCloseWithMultipleResources() throws IOException { + String str = "first line" + + System.lineSeparator() + + "second line"; + InputStream is = new ByteArrayInputStream(str.getBytes()); + String line; + //multiple resources in the same try declaration + try (BufferedReader br = + new BufferedReader( + new FileReader("I do not exist!")); + BufferedReader brFromString = + new BufferedReader( + new InputStreamReader(is)) + ) { + line = br.readLine(); + line += brFromString.readLine(); + } catch (IOException e) { + line = "error"; + } + assertEquals(line, __); + } + + @Koan + public void supressException() { + String message = ""; + try { + bar(); + } catch (WorkException e) { + message += e.getMessage() + " " + e.getSuppressed()[0].getMessage(); + } catch (CloseException e) { + message += e.getMessage(); + } + assertEquals(message, __); + } + + + public void bar() throws CloseException, WorkException { + try (AutoClosableResource autoClosableResource = + new AutoClosableResource()) { + autoClosableResource.foo(); + } + } +} \ No newline at end of file diff --git a/koans/src/java8/AboutBase64.java b/koans/src/java8/AboutBase64.java new file mode 100644 index 00000000..ed1235e8 --- /dev/null +++ b/koans/src/java8/AboutBase64.java @@ -0,0 +1,40 @@ +package java8; + +import com.sandwich.koan.Koan; + +import java.util.Base64; +import java.io.UnsupportedEncodingException; + +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; + +public class AboutBase64 { + + private final String plainText = "lorem ipsum"; + private final String encodedText = "bG9yZW0gaXBzdW0="; + + @Koan + public void base64Encoding() { + try { + // Encode the plainText + // This uses the basic Base64 encoding scheme but there are corresponding + // getMimeEncoder and getUrlEncoder methods available if you require a + // different format/Base64 Alphabet + assertEquals(encodedText, Base64.getEncoder().encodeToString(__.getBytes("utf-8"))); + } catch (UnsupportedEncodingException ex) {} + } + + @Koan + public void base64Decoding() { + // Decode the Base64 encodedText + // This uses the basic Base64 decoding scheme but there are corresponding + // getMimeDecoder and getUrlDecoder methods available if you require a + // different format/Base64 Alphabet + byte[] decodedBytes = Base64.getDecoder().decode(__); + try { + String decodedText = new String(decodedBytes, "utf-8"); + assertEquals(plainText, decodedText); + } catch (UnsupportedEncodingException ex) {} + } + +} diff --git a/koans/src/java8/AboutDefaultMethods.java b/koans/src/java8/AboutDefaultMethods.java new file mode 100644 index 00000000..d2a5c994 --- /dev/null +++ b/koans/src/java8/AboutDefaultMethods.java @@ -0,0 +1,47 @@ +package java8; + +import com.sandwich.koan.Koan; + +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; + +public class AboutDefaultMethods { + + @Koan + public void interfaceDefaultMethod() { + StringUtil stringUtil = new StringUtil() { + @Override + public String reverse(String s) { + return new StringBuilder(s).reverse().toString(); + } + }; + String capitalizedReversed = stringUtil.capitalize( + stringUtil.reverse("gnirut")); + assertEquals(capitalizedReversed, __); + } + + @Koan + public void interfaceStaticMethod() { + assertEquals(StringUtil.enclose("me"), __); + } + + interface StringUtil { + + //static method in interface + static String enclose(String in) { + return "[" + in + "]"; + } + + String reverse(String s); + + //interface can contain non-abstract method implementations marked by "default" keyword + default String capitalize(String s) { + return s.toUpperCase(); + } + + default String capitalizeFirst(String s) { + return s.substring(0, 1).toUpperCase() + s.substring(1); + } + } + +} diff --git a/koans/src/java8/AboutLambdas.java b/koans/src/java8/AboutLambdas.java new file mode 100644 index 00000000..8cab569b --- /dev/null +++ b/koans/src/java8/AboutLambdas.java @@ -0,0 +1,90 @@ +package java8; + +import com.sandwich.koan.Koan; + +import java.util.function.Function; + +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; + +public class AboutLambdas { + + interface Caps { + public String capitalize(String name); + } + + String fieldFoo = "Lambdas"; + + @Override + public String toString() { + return "CAPS"; + } + + static String str = ""; + + //lambda has access to "this" + Caps thisLambdaField = s -> this.toString(); + //lambda has access to object methods + Caps toStringLambdaField = s -> toString(); + + @Koan + public void verySimpleLambda() throws InterruptedException { + Runnable r8 = () -> str = "changed in lambda"; + r8.run(); + assertEquals(str, __); + } + + @Koan + public void simpleLambda() { + Caps caps = (String n) -> { + return n.toUpperCase(); + }; + String capitalized = caps.capitalize("James"); + assertEquals(capitalized, __); + } + + @Koan + public void simpleSuccinctLambda() { + //parameter type can be omitted, + //code block braces {} and return statement can be omitted for single statement lambda + //parameter parenthesis can be omitted for single parameter lambda + Caps caps = s -> s.toUpperCase(); + String capitalized = caps.capitalize("Arthur"); + assertEquals(capitalized, __); + } + + @Koan + public void lambdaField() { + assertEquals(thisLambdaField.capitalize(""), __); + } + + @Koan + public void lambdaField2() { + assertEquals(toStringLambdaField.capitalize(""), __); + } + + @Koan + public void effectivelyFinal() { + //final can be omitted like this: + /* final */ + String effectivelyFinal = "I'm effectively final"; + Caps caps = s -> effectivelyFinal.toUpperCase(); + assertEquals(caps.capitalize(effectivelyFinal), __); + } + + @Koan + public void methodReference() { + Caps caps = String::toUpperCase; + String capitalized = caps.capitalize("Gosling"); + assertEquals(capitalized, __); + } + + @Koan + public void thisIsSurroundingClass() { + //"this" in lambda points to surrounding class + Function foo = s -> s + this.fieldFoo + s; + assertEquals(foo.apply("|"), __); + } + +} + diff --git a/koans/src/java8/AboutLocalTime.java b/koans/src/java8/AboutLocalTime.java new file mode 100644 index 00000000..c99e1dc1 --- /dev/null +++ b/koans/src/java8/AboutLocalTime.java @@ -0,0 +1,26 @@ +package java8; + +import com.sandwich.koan.Koan; + +import java.time.LocalTime; +import java.time.temporal.ChronoUnit; + +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; + +public class AboutLocalTime { + + @Koan + public void localTime() { + LocalTime t1 = LocalTime.of(7, 30); + assertEquals(t1, LocalTime.parse(__)); + } + + @Koan + public void localTimeMinus() { + LocalTime t1 = LocalTime.parse("10:30"); + LocalTime t2 = t1.minus(2, ChronoUnit.HOURS); + assertEquals(t2, LocalTime.parse(__)); + } + +} diff --git a/koans/src/java8/AboutMultipleInheritance.java b/koans/src/java8/AboutMultipleInheritance.java new file mode 100644 index 00000000..40a54c56 --- /dev/null +++ b/koans/src/java8/AboutMultipleInheritance.java @@ -0,0 +1,37 @@ +package java8; + +import com.sandwich.koan.Koan; + +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; + +public class AboutMultipleInheritance { + + interface Human { + default String sound() { + return "hello"; + } + } + + interface Bull { + default String sound() { + return "moo"; + } + } + + class Minotaur implements Human, Bull { + //both interfaces implement same default method + //has to be overridden + @Override + public String sound() { + return Bull.super.sound(); + } + } + + @Koan + public void multipleInheritance() { + Minotaur minotaur = new Minotaur(); + assertEquals(minotaur.sound(), __); + } + +} diff --git a/koans/src/java8/AboutOptional.java b/koans/src/java8/AboutOptional.java new file mode 100644 index 00000000..d2ba6b53 --- /dev/null +++ b/koans/src/java8/AboutOptional.java @@ -0,0 +1,39 @@ +package java8; + +import com.sandwich.koan.Koan; + +import java.util.Optional; + +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; + +public class AboutOptional { + + boolean optionalIsPresentField = false; + + @Koan + public void isPresent() { + boolean optionalIsPresent = false; + Optional value = notPresent(); + if (value.isPresent()) { + optionalIsPresent = true; + } + assertEquals(optionalIsPresent, __); + } + + @Koan + public void ifPresentLambda() { + Optional value = notPresent(); + value.ifPresent(x -> optionalIsPresentField = true); + assertEquals(optionalIsPresentField, __); + } + + //use optional on api to signal that value is optional + public Optional notPresent() { + return Optional.empty(); + } + + private Optional present() { + return Optional.of("is present"); + } +} diff --git a/koans/src/java8/AboutStreams.java b/koans/src/java8/AboutStreams.java new file mode 100644 index 00000000..ee0c90e4 --- /dev/null +++ b/koans/src/java8/AboutStreams.java @@ -0,0 +1,128 @@ +package java8; + +import com.sandwich.koan.Koan; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static com.sandwich.koan.constant.KoanConstants.__; +import static com.sandwich.util.Assert.assertEquals; + +public class AboutStreams { + + String str = ""; + + List places = Arrays.asList("Belgrade", "Zagreb", "Sarajevo", "Skopje", "Ljubljana", "Podgorica"); + + @Koan + public void simpleCount() { + long count = places.stream().count(); + assertEquals(count, __); + } + + @Koan + public void filteredCount() { + long count = places.stream() + .filter(s -> s.startsWith("S")) + .count(); + assertEquals(count, __); + } + + @Koan + public void max() { + String longest = places.stream() + .max(Comparator.comparing(cityName -> cityName.length())) + .get(); + assertEquals(longest, __); + } + + @Koan + public void min() { + String shortest = places.stream() + .min(Comparator.comparing(cityName -> cityName.length())) + .get(); + assertEquals(shortest, __); + } + + @Koan + public void reduce() { + String join = places.stream() + .reduce("", String::concat); + assertEquals(join, __); + } + + @Koan + public void reduceWithoutStarterReturnsOptional() { + Optional join = places.stream() + .reduce(String::concat); + assertEquals(join.get(), __); + } + + @Koan + public void join() { + String join = places.stream() + .reduce((accumulated, cityName) -> accumulated + "\", \"" + cityName) + .get(); + assertEquals(join, __); + } + + @Koan + public void stringJoin() { + String join = places.stream() + .collect(Collectors.joining("\", \"")); + assertEquals(join, __); + } + + @Koan + public void mapReduce() { + OptionalDouble averageLengthOptional = places.stream() + .mapToInt(String::length) + .average(); + double averageLength = Math.round(averageLengthOptional.getAsDouble()); + assertEquals(averageLength, __); + } + + @Koan + public void parallelMapReduce() { + int lengthSum = places.parallelStream() + .mapToInt(String::length) + .sum(); + assertEquals(lengthSum, __); + } + + @Koan + public void limitSkip() { + int lengthSum_Limit_3_Skip_1 = places.stream() + .mapToInt(String::length) + .limit(3) + .skip(1) + .sum(); + assertEquals(lengthSum_Limit_3_Skip_1, __); + } + + @Koan + public void lazyEvaluation() { + Stream stream = places.stream() + .filter(s -> { + str = "hello"; + return s.startsWith("S"); + }); + assertEquals(str, __); + } + + @Koan + public void sumRange() { + int sum = IntStream.range(1, 4).sum(); + assertEquals(sum, __); + } + + @Koan + public void rangeToList() { + List range = IntStream.range(1, 4) + .boxed() + .collect(Collectors.toList()); + assertEquals(range, __); + } +} diff --git a/koans/src/run.bat b/koans/src/run.bat deleted file mode 100755 index fb34fe3b..00000000 --- a/koans/src/run.bat +++ /dev/null @@ -1,24 +0,0 @@ -@echo off -cls - -javac -version -if ERRORLEVEL 3 goto no_javac -java -version -if ERRORLEVEL 1 goto no_java -mkdir ..\bin -cls -java -classpath ..\bin;..\lib\koans.jar com.sandwich.koan.runner.AppLauncher - -goto end - -:no_java -REM cls -@echo java is not bound to PATH variable. -goto end - -:no_javac -REM cls -@echo javac is not bound to PATH variable. -goto end - -:end diff --git a/koans/src/run.sh b/koans/src/run.sh deleted file mode 100755 index 4ffee5ac..00000000 --- a/koans/src/run.sh +++ /dev/null @@ -1,3 +0,0 @@ -mkdir ../bin -clear -java -classpath ../bin:../lib/koans.jar com.sandwich.koan.runner.AppLauncher "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9" diff --git a/lib/build.gradle b/lib/build.gradle new file mode 100644 index 00000000..0f6aae3d --- /dev/null +++ b/lib/build.gradle @@ -0,0 +1,47 @@ +apply plugin: 'java' +apply plugin: 'idea' +apply plugin: 'eclipse' + +repositories { + mavenCentral() +} + +dependencies { + testCompile 'junit:junit:4.+' + testCompile 'org.easymock:easymock:3.4' +} + +task createJar(type: Jar){ + archiveName = 'koans.jar' + manifest { + attributes 'Implementation-Title': 'Java Koans', + 'Implementation-Version': 2.0, + 'Main-Class': 'com.sandwich.koan.runner.AppLauncher' + } + baseName = "java-koans" + from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }} + with jar +} + +task copyJarToBin { + doLast { + copy { + from './build/libs/koans.jar' + into '../koans/app/lib' + } + } +} + +test { + + classpath = project.sourceSets.test.runtimeClasspath + files("${projectDir}/../koans/app/config") + +} + +allprojects { + sourceCompatibility = 1.5 + targetCompatibility = 1.5 +} + +task buildApp (dependsOn: [clean, test, createJar, copyJarToBin]) + diff --git a/lib/gradle/wrapper/gradle-wrapper.jar b/lib/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..94114481 Binary files /dev/null and b/lib/gradle/wrapper/gradle-wrapper.jar differ diff --git a/lib/gradle/wrapper/gradle-wrapper.properties b/lib/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..c2030d0d --- /dev/null +++ b/lib/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Mar 26 18:10:52 GMT 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-bin.zip diff --git a/lib/gradlew b/lib/gradlew new file mode 100755 index 00000000..9d82f789 --- /dev/null +++ b/lib/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/lib/gradlew.bat b/lib/gradlew.bat new file mode 100644 index 00000000..8a0b282a --- /dev/null +++ b/lib/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/lib/src/main/java/com/sandwich/koan/ApplicationSettings.java b/lib/src/main/java/com/sandwich/koan/ApplicationSettings.java new file mode 100644 index 00000000..168e1b7d --- /dev/null +++ b/lib/src/main/java/com/sandwich/koan/ApplicationSettings.java @@ -0,0 +1,75 @@ +package com.sandwich.koan; + +import java.io.FileInputStream; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; + +import com.sandwich.util.io.directories.DirectoryManager; + +public class ApplicationSettings { + + private static ResourceBundle CONFIG_BUNDLE; + + private static boolean debug; + + public static void setDebug(boolean debug2) { + debug = debug2; + } + + public static boolean isDebug(){ + return debug || isEqual(getConfigBundle().getString("debug"), true, true); + } + + public static boolean isEncouragementEnabled(){ + return isEqual(getConfigBundle().getString("enable_encouragement"), true, true); + } + + public static boolean isExpectationResultVisible(){ + return isEqual(getConfigBundle().getString("enable_expectation_result"), true, true); + } + + public static boolean isInteractive(){ + return isEqual(getConfigBundle().getString("interactive"), true, true); + } + + public static char getExitChar(){ + return getConfigBundle().getString("exit_character").charAt(0); + } + + public static String getPathXmlFileName(){ + return getConfigBundle().getString("path_xml_filename"); + } + + public static long getFileCompilationTimeoutInMs(){ + return Long.valueOf(getConfigBundle().getString("compile_timeout_in_ms")); + } + + public static String getMonitorIgnorePattern(){ + return getConfigBundle().getString("ignore_from_monitoring"); + } + + private static boolean isEqual(String value, Object e2, boolean ignoreCase){ + if(value == e2){ + return true; + } + if(value != null){ + String stringValue = String.valueOf(e2); + return ignoreCase ? value.equalsIgnoreCase(stringValue) : value.equals(stringValue); + } + return false; + } + + private static ResourceBundle getConfigBundle(){ + if(CONFIG_BUNDLE == null){ + try { + CONFIG_BUNDLE = new PropertyResourceBundle(new FileInputStream( + DirectoryManager.injectFileSystemSeparators( + DirectoryManager.getConfigDir(), "config.properties"))); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return CONFIG_BUNDLE; + } + +} diff --git a/koans-lib/src/com/sandwich/koan/Koan.java b/lib/src/main/java/com/sandwich/koan/Koan.java similarity index 100% rename from koans-lib/src/com/sandwich/koan/Koan.java rename to lib/src/main/java/com/sandwich/koan/Koan.java diff --git a/lib/src/main/java/com/sandwich/koan/KoanClassLoader.java b/lib/src/main/java/com/sandwich/koan/KoanClassLoader.java new file mode 100644 index 00000000..93f78505 --- /dev/null +++ b/lib/src/main/java/com/sandwich/koan/KoanClassLoader.java @@ -0,0 +1,78 @@ +package com.sandwich.koan; + +import java.io.File; +import java.io.UTFDataFormatException; + +import com.sandwich.koan.util.ApplicationUtils; +import com.sandwich.util.io.FileMonitor; +import com.sandwich.util.io.FileMonitorFactory; +import com.sandwich.util.io.classloader.DynamicClassLoader; +import com.sandwich.util.io.directories.DirectoryManager; + +public class KoanClassLoader extends DynamicClassLoader { + + private static KoanClassLoader instance; + private final FileMonitor fileMonitor; + + private KoanClassLoader(){ + this(DirectoryManager.getBinDir(), + DirectoryManager.getSourceDir(), + buildClassPath(), + DynamicClassLoader.class.getClassLoader(), + ApplicationSettings.getFileCompilationTimeoutInMs(), + getFileMonitor()); + } + + private static FileMonitor getFileMonitor() { + try{ + return FileMonitorFactory.getInstance(new File(DirectoryManager.getMainDir()), new File(DirectoryManager.getDataFile())); + }catch(RuntimeException x){ + if(x.getCause() instanceof UTFDataFormatException){ + ApplicationUtils.getPresenter().displayError("Issue loading file system hashes, please rerun run.bat or run.sh with the -clear switch to reset."); + System.exit(4); + } + throw x; + } + } + + private KoanClassLoader(String binDir, String sourceDir, + String[] classPath, ClassLoader parent, + long timeout, FileMonitor fileMonitor) { + super(binDir, sourceDir, classPath, parent, timeout); + this.fileMonitor = fileMonitor; + } + + @Override + public boolean isFileModifiedSinceLastPoll(String sourcePath, long lastModified) { + return fileMonitor.isFileModifiedSinceLastPoll(sourcePath, lastModified); + } + + @Override + public void updateFileSavedTime(File sourceFile) { + fileMonitor.updateFileSaveTime(sourceFile); + } + + private static String[] buildClassPath() { + File[] jars = new File(DirectoryManager.getProjectLibraryDir()).listFiles(); + String[] classPath = new String[jars.length]; + for (int i = 0; i < jars.length; i++) { + if(jars[i].getAbsolutePath().toLowerCase().endsWith(".jar")){ + classPath[i] = jars[i].getAbsolutePath(); + } + } + return classPath; + } + + synchronized public static DynamicClassLoader getInstance(){ + if(instance == null){ + instance = new KoanClassLoader(); + } + return instance.clone(); + } + + @Override + public KoanClassLoader clone(){ + return new KoanClassLoader(getBinDir(), getSourceDir(), getClassPath(), getClass().getClassLoader(), getTimeout(), fileMonitor); + } + +} diff --git a/koans-lib/src/com/sandwich/koan/KoanIncompleteException.java b/lib/src/main/java/com/sandwich/koan/KoanIncompleteException.java similarity index 100% rename from koans-lib/src/com/sandwich/koan/KoanIncompleteException.java rename to lib/src/main/java/com/sandwich/koan/KoanIncompleteException.java diff --git a/koans-lib/src/com/sandwich/koan/KoanMethod.java b/lib/src/main/java/com/sandwich/koan/KoanMethod.java similarity index 79% rename from koans-lib/src/com/sandwich/koan/KoanMethod.java rename to lib/src/main/java/com/sandwich/koan/KoanMethod.java index 7af9955c..07ffa2da 100755 --- a/koans-lib/src/com/sandwich/koan/KoanMethod.java +++ b/lib/src/main/java/com/sandwich/koan/KoanMethod.java @@ -3,20 +3,18 @@ import java.lang.reflect.Method; import com.sandwich.koan.path.xmltransformation.KoanElementAttributes; -import com.sandwich.koan.path.xmltransformation.XmlVariableInjector; -import com.sandwich.util.io.DynamicClassLoader; +import com.sandwich.koan.path.xmltransformation.RbVariableInjector; +import com.sandwich.util.io.KoanSuiteCompilationListener; public class KoanMethod { private final transient Method method; private final String lesson; private final boolean displayIncompleteException; - private static final DynamicClassLoader classLoader = new DynamicClassLoader(); + private static final KoanSuiteCompilationListener listener = new KoanSuiteCompilationListener(); private KoanMethod(KoanElementAttributes koanAttributes) throws SecurityException, NoSuchMethodException{ - this( koanAttributes.lesson, - classLoader.loadClass(koanAttributes.className).getMethod(koanAttributes.name), - !"false".equalsIgnoreCase(koanAttributes.displayIncompleteKoanException)); + this(null, koanAttributes); } public static KoanMethod getInstance(KoanElementAttributes koanAttributes){ @@ -40,10 +38,17 @@ private KoanMethod(String lesson, Method method, boolean displayIncompleteExcept throw new IllegalArgumentException("method may not be null"); } this.method = method; - this.lesson = new XmlVariableInjector(lesson, method).injectLessonVariables(); + this.lesson = new RbVariableInjector(lesson, method).injectLessonVariables(); this.displayIncompleteException = displayIncompleteException; } + public KoanMethod(String lesson, KoanElementAttributes koanAttributes) throws SecurityException, NoSuchMethodException { + this( lesson, + KoanClassLoader.getInstance().loadClass(koanAttributes.className, listener) + .getMethod(koanAttributes.name), + !"false".equalsIgnoreCase(koanAttributes.displayIncompleteKoanException)); + } + public String getLesson() { return lesson; } diff --git a/koans-lib/src/com/sandwich/koan/cmdline/CommandLineArgument.java b/lib/src/main/java/com/sandwich/koan/cmdline/CommandLineArgument.java similarity index 69% rename from koans-lib/src/com/sandwich/koan/cmdline/CommandLineArgument.java rename to lib/src/main/java/com/sandwich/koan/cmdline/CommandLineArgument.java index aeb80528..d0810289 100755 --- a/koans-lib/src/com/sandwich/koan/cmdline/CommandLineArgument.java +++ b/lib/src/main/java/com/sandwich/koan/cmdline/CommandLineArgument.java @@ -1,23 +1,25 @@ package com.sandwich.koan.cmdline; +import java.util.Arrays; + import com.sandwich.koan.constant.ArgumentType; public class CommandLineArgument implements Runnable, Comparable { private ArgumentType argType; - private String value; + private String[] values; boolean isPlantedByApp = false; - public CommandLineArgument(ArgumentType argType, String value) { - this(argType, value, false); + public CommandLineArgument(ArgumentType argType, String... values) { + this(argType, false, values); } - public CommandLineArgument(ArgumentType argType, String value, boolean isPlantedByApp) { + public CommandLineArgument(ArgumentType argType, boolean isPlantedByApp, String... values) { this.argType = argType; - this.value = value; + this.values = values; this.isPlantedByApp = isPlantedByApp; } - public String getValue() { - return value; + public String[] getValues() { + return values; } public ArgumentType getArgumentType() { @@ -33,7 +35,7 @@ public boolean isPlantedByApp() { } public void run(){ - argType.run(value); + argType.run(values); } public int compareTo(CommandLineArgument o) { @@ -49,7 +51,7 @@ public int compareTo(CommandLineArgument o) { @Override public String toString() { return new StringBuilder("CommandLineArgument [argType=").append(argType) - .append(", value=").append(value).append("]").toString(); + .append(", value=").append(Arrays.toString(values)).append("]").toString(); } @Override @@ -57,7 +59,7 @@ public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((argType == null) ? 0 : argType.hashCode()); - result = prime * result + ((value == null) ? 0 : value.hashCode()); + result = prime * result + ((values == null) ? 0 : Arrays.hashCode(values)); return result; } @@ -72,10 +74,10 @@ public boolean equals(Object obj) { CommandLineArgument other = (CommandLineArgument) obj; if (argType != other.argType) return false; - if (value == null) { - if (other.value != null) + if (values == null) { + if (other.values != null) return false; - } else if (!value.equals(other.value)) + } else if (!Arrays.equals(values, other.values)) return false; return true; } diff --git a/lib/src/main/java/com/sandwich/koan/cmdline/CommandLineArgumentBuilder.java b/lib/src/main/java/com/sandwich/koan/cmdline/CommandLineArgumentBuilder.java new file mode 100755 index 00000000..f8406c59 --- /dev/null +++ b/lib/src/main/java/com/sandwich/koan/cmdline/CommandLineArgumentBuilder.java @@ -0,0 +1,85 @@ +package com.sandwich.koan.cmdline; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map.Entry; +import java.util.logging.Logger; + +import com.sandwich.koan.ApplicationSettings; +import com.sandwich.koan.constant.ArgumentType; +import com.sandwich.koan.ui.SuitePresenter; +import com.sandwich.koan.util.ApplicationUtils; + +public class CommandLineArgumentBuilder extends LinkedHashMap { + + private static final long serialVersionUID = 7635285665311420603L; + + public CommandLineArgumentBuilder(String...args){ + args = filterOutNullElements(args); + if(args.length == 0){ + put(ArgumentType.RUN_KOANS, new CommandLineArgument(ArgumentType.RUN_KOANS, new String[0])); + } else if (args.length == 1 && ArgumentType.findTypeByString(args[0]) == null){ + put(ArgumentType.CLASS_ARG, new CommandLineArgument(ArgumentType.CLASS_ARG, args[0])); + } else if (args.length == 2 && ArgumentType.findTypeByString(args[0]) == null && ArgumentType.findTypeByString(args[1]) == null){ + put(ArgumentType.CLASS_ARG, new CommandLineArgument(ArgumentType.CLASS_ARG, args[0])); + put(ArgumentType.METHOD_ARG, new CommandLineArgument(ArgumentType.METHOD_ARG, args[1])); + } else { + ArgumentType type = null; + List params = null; + for(int index = 0; index < args.length; index++){ + ArgumentType tmpType = ArgumentType.findTypeByString(args[index]); + if(tmpType == null){ + if(type == null){ + Logger.getAnonymousLogger().warning("The argument: " + args[index] + " is not recognized, it will be ignored"); + }else{ + params.add(args[index]); + } + }else{ + if(type == null){ + type = tmpType; + params = new ArrayList(); + }else{ + put(type, new CommandLineArgument(type, params.toArray(new String[params.size()]))); + type = null; + params = new ArrayList(); + } + } + } + if(type != null && params != null){ + put(type, new CommandLineArgument(type, params.toArray(new String[params.size()]))); + } + } + applyAssumedStartupBehaviors(); + } + + private String[] filterOutNullElements(String... args) { + List tempArgs = new ArrayList(); + for(String arg : args){ + if(arg != null && arg.trim().length() > 0){ + tempArgs.add(arg.trim()); + } + } + return tempArgs.toArray(new String[tempArgs.size()]); + } + + void applyAssumedStartupBehaviors() { + if(ApplicationUtils.isFirstTimeAppHasBeenRun()){ + ArgumentType.BACKUP.run(new String[0]); + ApplicationUtils.getPresenter().clearMessages(); + } + if(isEmpty() || !containsKey(ArgumentType.RUN_KOANS) && ( + containsKey(ArgumentType.CLASS_ARG) || + containsKey(ArgumentType.DEBUG))){ + if(ApplicationSettings.isDebug()){ + SuitePresenter presenter = ApplicationUtils.getPresenter(); + presenter.displayMessage("Planting default run target."); + for(Entry argEntry : entrySet()){ + presenter.displayMessage("Key: '"+argEntry.getKey()+"'"); + presenter.displayMessage("Value: '"+argEntry.getValue()+"'"); + } + } + put(ArgumentType.RUN_KOANS, new CommandLineArgument(ArgumentType.RUN_KOANS, true, new String[0])); + } + } +} diff --git a/koans-lib/src/com/sandwich/koan/cmdline/CommandLineArgumentRunner.java b/lib/src/main/java/com/sandwich/koan/cmdline/CommandLineArgumentRunner.java similarity index 100% rename from koans-lib/src/com/sandwich/koan/cmdline/CommandLineArgumentRunner.java rename to lib/src/main/java/com/sandwich/koan/cmdline/CommandLineArgumentRunner.java diff --git a/koans-lib/src/com/sandwich/koan/cmdline/behavior/AbstractArgumentBehavior.java b/lib/src/main/java/com/sandwich/koan/cmdline/behavior/AbstractArgumentBehavior.java similarity index 100% rename from koans-lib/src/com/sandwich/koan/cmdline/behavior/AbstractArgumentBehavior.java rename to lib/src/main/java/com/sandwich/koan/cmdline/behavior/AbstractArgumentBehavior.java diff --git a/koans-lib/src/com/sandwich/koan/cmdline/behavior/ArgumentBehavior.java b/lib/src/main/java/com/sandwich/koan/cmdline/behavior/ArgumentBehavior.java similarity index 75% rename from koans-lib/src/com/sandwich/koan/cmdline/behavior/ArgumentBehavior.java rename to lib/src/main/java/com/sandwich/koan/cmdline/behavior/ArgumentBehavior.java index 73ead3bc..876fd88b 100755 --- a/koans-lib/src/com/sandwich/koan/cmdline/behavior/ArgumentBehavior.java +++ b/lib/src/main/java/com/sandwich/koan/cmdline/behavior/ArgumentBehavior.java @@ -3,7 +3,7 @@ public interface ArgumentBehavior { - void run(String value) throws Exception; + void run(String... values) throws Exception; String getErrorMessage(); String getSuccessMessage(); diff --git a/lib/src/main/java/com/sandwich/koan/cmdline/behavior/Backup.java b/lib/src/main/java/com/sandwich/koan/cmdline/behavior/Backup.java new file mode 100755 index 00000000..151c1c2a --- /dev/null +++ b/lib/src/main/java/com/sandwich/koan/cmdline/behavior/Backup.java @@ -0,0 +1,25 @@ +package com.sandwich.koan.cmdline.behavior; + +import java.io.File; +import java.io.IOException; + +import com.sandwich.util.io.CopyFileOperation; + +public class Backup extends KoanFileCopying{ + + @Override + protected void copy(String backupSrcDirectory, String appSrcDirectory) + throws IOException { + File backupDir = new File(backupSrcDirectory); + if(!backupDir.exists()){ + backupDir.mkdirs(); + } + File sourceDir = new File(appSrcDirectory); + new CopyFileOperation(sourceDir, backupDir){ + public void onNew(File file) throws IOException { + file.mkdirs(); + }; + }.operate(); + } + +} diff --git a/lib/src/main/java/com/sandwich/koan/cmdline/behavior/ClassArg.java b/lib/src/main/java/com/sandwich/koan/cmdline/behavior/ClassArg.java new file mode 100755 index 00000000..26110761 --- /dev/null +++ b/lib/src/main/java/com/sandwich/koan/cmdline/behavior/ClassArg.java @@ -0,0 +1,16 @@ +package com.sandwich.koan.cmdline.behavior; + +import com.sandwich.koan.path.PathToEnlightenment; + +public class ClassArg extends AbstractArgumentBehavior { + + public void run(String...koanSuiteClassName) { + if(koanSuiteClassName != null && + koanSuiteClassName.length > 0 && + koanSuiteClassName[0] != null && + koanSuiteClassName[0].trim().length() != 0){ + PathToEnlightenment.filterBySuite(koanSuiteClassName[0]); + } + } + +} diff --git a/lib/src/main/java/com/sandwich/koan/cmdline/behavior/Clear.java b/lib/src/main/java/com/sandwich/koan/cmdline/behavior/Clear.java new file mode 100644 index 00000000..6bfb5e6d --- /dev/null +++ b/lib/src/main/java/com/sandwich/koan/cmdline/behavior/Clear.java @@ -0,0 +1,38 @@ +package com.sandwich.koan.cmdline.behavior; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import com.sandwich.koan.util.ApplicationUtils; +import com.sandwich.util.io.directories.DirectoryManager; + +public class Clear extends AbstractArgumentBehavior { + + List files = Arrays.asList( + DirectoryManager.getDataFile(), + DirectoryManager.getBinDir(), + DirectoryManager.getDataDir()); + + public void run(String... values) throws Exception { + List unableToDelete = new ArrayList(); + for(String fileName : files){ + File file = new File(fileName); + if(file.exists()){ + if(file.delete()){ + ApplicationUtils.getPresenter().displayMessage(file.getAbsolutePath() + " deleted successfully."); + }else{ + unableToDelete.add(file); + ApplicationUtils.getPresenter().displayError(file.getAbsolutePath() + " was NOT DELETED. Please delete manually."); + } + }else{ + ApplicationUtils.getPresenter().displayMessage(file.getAbsolutePath() + " does not exist. Skipping."); + } + } + if(!unableToDelete.isEmpty()){ + throw new RuntimeException("Unable to delete: "+unableToDelete+" see output for details."); + } + } + +} diff --git a/lib/src/main/java/com/sandwich/koan/cmdline/behavior/Debug.java b/lib/src/main/java/com/sandwich/koan/cmdline/behavior/Debug.java new file mode 100755 index 00000000..5fefa506 --- /dev/null +++ b/lib/src/main/java/com/sandwich/koan/cmdline/behavior/Debug.java @@ -0,0 +1,14 @@ +package com.sandwich.koan.cmdline.behavior; + +import com.sandwich.koan.ApplicationSettings; + +public class Debug extends AbstractArgumentBehavior{ + + public void run(String... args){ + ApplicationSettings.setDebug(ApplicationSettings.isDebug() || + "true".equalsIgnoreCase(args[0]) || + args[0] == null || + args[0].trim().length() == 0); + } + +} diff --git a/koans-lib/src/com/sandwich/koan/cmdline/behavior/Help.java b/lib/src/main/java/com/sandwich/koan/cmdline/behavior/Help.java similarity index 94% rename from koans-lib/src/com/sandwich/koan/cmdline/behavior/Help.java rename to lib/src/main/java/com/sandwich/koan/cmdline/behavior/Help.java index 8003b6e6..f6c8e604 100755 --- a/koans-lib/src/com/sandwich/koan/cmdline/behavior/Help.java +++ b/lib/src/main/java/com/sandwich/koan/cmdline/behavior/Help.java @@ -6,11 +6,12 @@ import com.sandwich.koan.constant.ArgumentType; import com.sandwich.koan.constant.KoanConstants; +import com.sandwich.koan.util.ApplicationUtils; public class Help extends AbstractArgumentBehavior { - public void run(String value) { + public void run(String...values) { StringBuilder sb = new StringBuilder( "Java Koans - HELP").append(KoanConstants.EOL); line(sb, "-----------------"); @@ -32,8 +33,7 @@ public void run(String value) { makeCharWidth(70, getPositionOnCurrentLine(sb), argType.description())) .append(KoanConstants.EOL); } - - System.out.println(sb.toString()); + ApplicationUtils.getPresenter().displayMessage(sb.toString()); } private String makeCharWidth(int width, int positionOnCurrentLine, String text) { diff --git a/koans-lib/src/com/sandwich/koan/cmdline/behavior/KoanFileCopying.java b/lib/src/main/java/com/sandwich/koan/cmdline/behavior/KoanFileCopying.java similarity index 63% rename from koans-lib/src/com/sandwich/koan/cmdline/behavior/KoanFileCopying.java rename to lib/src/main/java/com/sandwich/koan/cmdline/behavior/KoanFileCopying.java index 404d9886..373c6459 100755 --- a/koans-lib/src/com/sandwich/koan/cmdline/behavior/KoanFileCopying.java +++ b/lib/src/main/java/com/sandwich/koan/cmdline/behavior/KoanFileCopying.java @@ -2,19 +2,22 @@ import java.io.IOException; +import com.sandwich.koan.ui.SuitePresenter; +import com.sandwich.koan.util.ApplicationUtils; import com.sandwich.util.io.directories.DirectoryManager; public abstract class KoanFileCopying extends AbstractArgumentBehavior { - public void run(String value) { + public void run(String... values) { + SuitePresenter presenter = ApplicationUtils.getPresenter(); try { copy(DirectoryManager.getProjectDataSourceDir(), DirectoryManager.getSourceDir()); } catch (IOException e) { e.printStackTrace(); - System.out.println(getErrorMessage()); + presenter.displayError(getErrorMessage()); System.exit(-1); } - System.out.println(getSuccessMessage()); + presenter.displayMessage(getSuccessMessage()); } protected abstract void copy(String backupSrcDirectory, String appSrcDirectory) throws IOException; diff --git a/lib/src/main/java/com/sandwich/koan/cmdline/behavior/MethodArg.java b/lib/src/main/java/com/sandwich/koan/cmdline/behavior/MethodArg.java new file mode 100755 index 00000000..94f64f28 --- /dev/null +++ b/lib/src/main/java/com/sandwich/koan/cmdline/behavior/MethodArg.java @@ -0,0 +1,16 @@ +package com.sandwich.koan.cmdline.behavior; + +import com.sandwich.koan.path.PathToEnlightenment; + +public class MethodArg extends AbstractArgumentBehavior { + + public void run(String...koanName) { + if(koanName != null && + koanName.length > 0 && + koanName[0] != null && + koanName[0].trim().length() != 0){ + PathToEnlightenment.filterByKoan(koanName[0]); + } + } + +} diff --git a/koans-lib/src/com/sandwich/koan/cmdline/behavior/Reset.java b/lib/src/main/java/com/sandwich/koan/cmdline/behavior/Reset.java similarity index 53% rename from koans-lib/src/com/sandwich/koan/cmdline/behavior/Reset.java rename to lib/src/main/java/com/sandwich/koan/cmdline/behavior/Reset.java index ed90f597..9161de01 100755 --- a/koans-lib/src/com/sandwich/koan/cmdline/behavior/Reset.java +++ b/lib/src/main/java/com/sandwich/koan/cmdline/behavior/Reset.java @@ -2,14 +2,14 @@ import java.io.IOException; -import com.sandwich.util.io.FileUtils; +import com.sandwich.util.io.CopyFileOperation; -public class Reset extends KoanFileCopying{ +public class Reset extends KoanFileCopying { @Override protected void copy(String backupSrcDirectory, String appSrcDirectory) throws IOException { - FileUtils.copy(backupSrcDirectory, appSrcDirectory); + new CopyFileOperation(backupSrcDirectory, appSrcDirectory).operate(); } } diff --git a/koans-lib/src/com/sandwich/koan/constant/ArgumentType.java b/lib/src/main/java/com/sandwich/koan/constant/ArgumentType.java similarity index 79% rename from koans-lib/src/com/sandwich/koan/constant/ArgumentType.java rename to lib/src/main/java/com/sandwich/koan/constant/ArgumentType.java index 7fa38c68..724f2243 100755 --- a/koans-lib/src/com/sandwich/koan/constant/ArgumentType.java +++ b/lib/src/main/java/com/sandwich/koan/constant/ArgumentType.java @@ -1,8 +1,5 @@ package com.sandwich.koan.constant; -import static com.sandwich.koan.constant.KoanConstants.ARGUMENTS; -import static com.sandwich.koan.constant.KoanConstants.DESCRIPTION; - import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -13,26 +10,32 @@ import com.sandwich.koan.cmdline.behavior.ArgumentBehavior; import com.sandwich.koan.cmdline.behavior.Backup; import com.sandwich.koan.cmdline.behavior.ClassArg; +import com.sandwich.koan.cmdline.behavior.Clear; import com.sandwich.koan.cmdline.behavior.Debug; import com.sandwich.koan.cmdline.behavior.Help; import com.sandwich.koan.cmdline.behavior.MethodArg; import com.sandwich.koan.cmdline.behavior.Reset; import com.sandwich.koan.runner.RunKoans; +import com.sandwich.koan.util.ApplicationUtils; import com.sandwich.util.Strings; public enum ArgumentType implements ArgumentBehavior { - HELP( Help.class), - RESET( Reset.class), - BACKUP( Backup.class), - DEBUG( Debug.class), + HELP( Help.class), + RESET( Reset.class), + BACKUP( Backup.class), + DEBUG( Debug.class), + CLEAR( Clear.class), //TEST( Test.class), // important class MUST come before method - due to how Enum implements comparable and order // dependent logic later @ see ArgumentTypeTest.testClassPrecedesMethod - CLASS_ARG( ClassArg.class), - METHOD_ARG( MethodArg.class), - RUN_KOANS( RunKoans.class); + CLASS_ARG( ClassArg.class), + METHOD_ARG( MethodArg.class), + RUN_KOANS( RunKoans.class); + + private static final String DESCRIPTION = "description"; + private static final String ARGUMENTS = "args"; private final List args; private final ArgumentBehavior behavior; @@ -68,10 +71,10 @@ public String description(){ } TYPES_BY_STRING = Collections.unmodifiableMap(types); } - public void run(String value) { + public void run(String... values) { try{ - behavior.run(value); - behavior.getSuccessMessage(); + behavior.run(values); + ApplicationUtils.getPresenter().displayMessage(behavior.getSuccessMessage()); }catch(Throwable t){ if(behavior instanceof RunKoans){ if(t instanceof RuntimeException){ @@ -81,7 +84,7 @@ public void run(String value) { } }else{ Logger.getAnonymousLogger().severe(t.getLocalizedMessage()); - System.out.println(behavior.getErrorMessage()); + ApplicationUtils.getPresenter().displayError(behavior.getErrorMessage()); } } } diff --git a/lib/src/main/java/com/sandwich/koan/constant/KoanConstants.java b/lib/src/main/java/com/sandwich/koan/constant/KoanConstants.java new file mode 100755 index 00000000..40025ed1 --- /dev/null +++ b/lib/src/main/java/com/sandwich/koan/constant/KoanConstants.java @@ -0,0 +1,31 @@ +package com.sandwich.koan.constant; + +import com.sandwich.util.Strings; + +public abstract class KoanConstants { + + private KoanConstants(){} + + public static final String __ = Strings.getMessage("__"); + + public static final String EOL = System.getProperty("line.separator"); + public static final String EOLS = "[\n\r"+EOL+"]"; + + public static final String PERIOD = "."; + public static final String EXPECTATION_LEFT_ARG= "has expectation as wrong argument!"; + public static final String EXPECTED_LEFT = ":<"; + public static final String EXPECTED_RIGHT = ">"; + public static final String LINE_NO_START = ".java:"; + public static final String LINE_NO_END = ")"; + + public static final String COMPLETE_CHAR = "X"; + public static final String INCOMPLETE_CHAR = "-"; + + public static final int PROGRESS_BAR_WIDTH = 50; + public static final String PROGRESS_BAR_START = "["; + public static final String PROGRESS_BAR_END = "]"; + + public static final String XML_PARAMETER_START = "${"; + public static final String XML_PARAMETER_END = "}"; + +} diff --git a/koans-lib/src/com/sandwich/koan/path/PathToEnlightenment.java b/lib/src/main/java/com/sandwich/koan/path/PathToEnlightenment.java similarity index 84% rename from koans-lib/src/com/sandwich/koan/path/PathToEnlightenment.java rename to lib/src/main/java/com/sandwich/koan/path/PathToEnlightenment.java index 178ad1ce..7233de01 100755 --- a/koans-lib/src/com/sandwich/koan/path/PathToEnlightenment.java +++ b/lib/src/main/java/com/sandwich/koan/path/PathToEnlightenment.java @@ -1,18 +1,19 @@ package com.sandwich.koan.path; +import java.io.File; import java.io.FileNotFoundException; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; +import com.sandwich.koan.ApplicationSettings; import com.sandwich.koan.Koan; import com.sandwich.koan.constant.KoanConstants; import com.sandwich.koan.path.xmltransformation.KoanElementAttributes; import com.sandwich.koan.path.xmltransformation.XmlToPathTransformer; import com.sandwich.koan.path.xmltransformation.XmlToPathTransformerImpl; -import com.sandwich.util.Counter; -import com.sandwich.util.io.FileUtils; import com.sandwich.util.io.directories.DirectoryManager; +import com.sandwich.util.io.filecompiler.FileCompiler; public abstract class PathToEnlightenment { @@ -31,11 +32,17 @@ static Path createPath(){ } } - private static XmlToPathTransformer getXmlToPathTransformer(){ + static XmlToPathTransformer getXmlToPathTransformer(){ if(xmlToPathTransformer == null){ try { - xmlToPathTransformer = new XmlToPathTransformerImpl(DirectoryManager.injectFileSystemSeparators( - DirectoryManager.getProdSourceDir(), KoanConstants.PATH_XML_NAME), suiteName, koanMethod); + String filename = DirectoryManager.injectFileSystemSeparators( + DirectoryManager.getConfigDir(), ApplicationSettings.getPathXmlFileName()); + File file = new File(filename); + if(!file.exists()){ + throw new RuntimeException("No "+filename+" was found at: "+file.getAbsolutePath()); + } + xmlToPathTransformer = new XmlToPathTransformerImpl(filename, + suiteName, koanMethod); } catch (FileNotFoundException e) { throw new RuntimeException(e); } @@ -53,7 +60,7 @@ public static void filterByKoan(String koan){ xmlToPathTransformer = null; } - public static Path getPathToEnlightment(){ + public static Path getPathToEnlightenment(){ if(theWay == null){ theWay = createPath(); } @@ -79,28 +86,31 @@ public int getTotalNumberOfKoans() { if(methodName != null){ return 1; } - Counter total = new Counter(); + int total = 0; Iterator>>> koanMethodsBySuiteByPackageIter = getKoanMethodsBySuiteByPackage(); while(koanMethodsBySuiteByPackageIter.hasNext()){ Entry>> e = koanMethodsBySuiteByPackageIter.next(); for(Entry> e1 : e.getValue().entrySet()){ - countKoanAnnotationsInJavaFileGivenClassName(e1.getKey(), total); + total += countKoanAnnotationsInJavaFileGivenClassName(e1.getKey()); } } - return (int)total.getCount(); + return total; } - private void countKoanAnnotationsInJavaFileGivenClassName(String className, Counter total) { - String[] lines = FileUtils.getContentsOfOriginalJavaFile(className).split(KoanConstants.EOLS); + private int countKoanAnnotationsInJavaFileGivenClassName(String className) { + String[] lines = FileCompiler.getContentsOfJavaFile( + DirectoryManager.getSourceDir(), className).split(KoanConstants.EOLS); String koanClassSimpleNameWithAnnotationPrefix = '@'+Koan.class.getSimpleName(); + int total = 0; for(String line : lines){ String trimmedLine = line.trim(); if(trimmedLine.contains(koanClassSimpleNameWithAnnotationPrefix) && !trimmedLine.startsWith("//") && !trimmedLine.startsWith("*") && !trimmedLine.startsWith("/*")){ - total.count(); + total++; } } + return total; } public Iterator>>> iterator() { diff --git a/koans-lib/src/com/sandwich/koan/path/xmltransformation/KoanElementAttributes.java b/lib/src/main/java/com/sandwich/koan/path/xmltransformation/KoanElementAttributes.java similarity index 75% rename from koans-lib/src/com/sandwich/koan/path/xmltransformation/KoanElementAttributes.java rename to lib/src/main/java/com/sandwich/koan/path/xmltransformation/KoanElementAttributes.java index f90b2148..124fd144 100644 --- a/koans-lib/src/com/sandwich/koan/path/xmltransformation/KoanElementAttributes.java +++ b/lib/src/main/java/com/sandwich/koan/path/xmltransformation/KoanElementAttributes.java @@ -3,10 +3,9 @@ public class KoanElementAttributes{ - public String lesson, name, displayIncompleteKoanException, className; + public String name, displayIncompleteKoanException, className; - public KoanElementAttributes(String lesson, String name, String displayIncompleteKoanException, String className){ - this.lesson = lesson; + public KoanElementAttributes(String name, String displayIncompleteKoanException, String className){ this.name = name; this.displayIncompleteKoanException = displayIncompleteKoanException; this.className = className; @@ -22,7 +21,6 @@ public int hashCode() { * result + ((displayIncompleteKoanException == null) ? 0 : displayIncompleteKoanException.hashCode()); - result = prime * result + ((lesson == null) ? 0 : lesson.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @@ -47,11 +45,6 @@ public boolean equals(Object obj) { } else if (!displayIncompleteKoanException .equals(other.displayIncompleteKoanException)) return false; - if (lesson == null) { - if (other.lesson != null) - return false; - } else if (!lesson.equals(other.lesson)) - return false; if (name == null) { if (other.name != null) return false; @@ -62,7 +55,7 @@ public boolean equals(Object obj) { @Override public String toString() { - return "KoanElementAttributes [lesson=" + lesson + ", name=" + name + return "KoanElementAttributes [name=" + name + ", displayIncompleteKoanException=" + displayIncompleteKoanException + ", className=" + className + "]"; diff --git a/koans-lib/src/com/sandwich/koan/path/xmltransformation/XmlVariableInjector.java b/lib/src/main/java/com/sandwich/koan/path/xmltransformation/RbVariableInjector.java similarity index 59% rename from koans-lib/src/com/sandwich/koan/path/xmltransformation/XmlVariableInjector.java rename to lib/src/main/java/com/sandwich/koan/path/xmltransformation/RbVariableInjector.java index d5c238ec..e7ec0d53 100755 --- a/koans-lib/src/com/sandwich/koan/path/xmltransformation/XmlVariableInjector.java +++ b/lib/src/main/java/com/sandwich/koan/path/xmltransformation/RbVariableInjector.java @@ -1,11 +1,13 @@ package com.sandwich.koan.path.xmltransformation; -import java.io.File; import java.lang.reflect.Method; import com.sandwich.koan.constant.KoanConstants; +import com.sandwich.util.Strings; +import com.sandwich.util.io.directories.DirectoryManager; +import com.sandwich.util.io.filecompiler.FileCompiler; -public class XmlVariableInjector { +public class RbVariableInjector { private final String lesson; private final String methodName; @@ -13,15 +15,22 @@ public class XmlVariableInjector { private final String suitePath; - public XmlVariableInjector(String lesson, Method koanMethod){ + public RbVariableInjector(String lesson, Method koanMethod){ if(koanMethod == null){ throw new IllegalArgumentException("koanMethod may not be null"); } + Class declaringClass = koanMethod.getDeclaringClass(); + if(lesson == null){ + lesson = Strings.getMessage(declaringClass.getCanonicalName() + '.' + koanMethod.getName()); + if(Strings.wasNotFound(lesson)){ + lesson = null; + } + } this.lesson = lesson; methodName = koanMethod.getName(); - suiteName = koanMethod.getDeclaringClass().getSimpleName(); - suitePath = new File(koanMethod.getDeclaringClass().getName().replace('.', '/')).getAbsolutePath() - .replace(koanMethod.getDeclaringClass().getSimpleName(), ""); + suiteName = declaringClass.getSimpleName(); + String path = FileCompiler.getSourceFileFromClass(DirectoryManager.getSourceDir(), declaringClass.getName()).getAbsolutePath(); + suitePath = path.substring(0, path.lastIndexOf(DirectoryManager.FILESYSTEM_SEPARATOR) + 1); } public String injectLessonVariables() { diff --git a/koans-lib/src/com/sandwich/koan/path/xmltransformation/XmlToPathTransformer.java b/lib/src/main/java/com/sandwich/koan/path/xmltransformation/XmlToPathTransformer.java similarity index 100% rename from koans-lib/src/com/sandwich/koan/path/xmltransformation/XmlToPathTransformer.java rename to lib/src/main/java/com/sandwich/koan/path/xmltransformation/XmlToPathTransformer.java diff --git a/koans-lib/src/com/sandwich/koan/path/xmltransformation/XmlToPathTransformerImpl.java b/lib/src/main/java/com/sandwich/koan/path/xmltransformation/XmlToPathTransformerImpl.java similarity index 91% rename from koans-lib/src/com/sandwich/koan/path/xmltransformation/XmlToPathTransformerImpl.java rename to lib/src/main/java/com/sandwich/koan/path/xmltransformation/XmlToPathTransformerImpl.java index 3f4b8711..8a729526 100755 --- a/koans-lib/src/com/sandwich/koan/path/xmltransformation/XmlToPathTransformerImpl.java +++ b/lib/src/main/java/com/sandwich/koan/path/xmltransformation/XmlToPathTransformerImpl.java @@ -1,119 +1,116 @@ -package com.sandwich.koan.path.xmltransformation; - -import java.io.File; -import java.io.FileNotFoundException; -import java.util.LinkedHashMap; -import java.util.Map; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -import org.w3c.dom.DOMException; -import org.w3c.dom.Document; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import com.sandwich.koan.path.PathToEnlightenment.Path; - -public class XmlToPathTransformerImpl implements XmlToPathTransformer { - - private final File xmlFile; - private final String suiteName; - private final String methodName; - - public XmlToPathTransformerImpl(){ - xmlFile = null; - suiteName = null; - methodName = null; - } - - public XmlToPathTransformerImpl(String xmlFileLocation, String suiteName, String methodName) throws FileNotFoundException { - this.xmlFile = new File(xmlFileLocation); - this.suiteName = suiteName; - this.methodName = methodName; - if(!xmlFile.exists()){ - throw new FileNotFoundException(xmlFile.getAbsolutePath() - + " was not found. it may have been deleted, renamed."); - } - } - - public Path transform(){ - try{ - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - DocumentBuilder db = dbf.newDocumentBuilder(); - Document doc = db.parse(xmlFile); - doc.getDocumentElement().normalize(); - NodeList nodeLst = doc.getElementsByTagName("package"); - /* - * Map of string values read from xml in following form - * package - * |-suite class name - * |-method name - * |-method attributes from xml - */ - Map>> koans = - new LinkedHashMap>>(); - for(int i = 0; i < nodeLst.getLength(); i++){ - Node node = nodeLst.item(i); - String packageTitle = node.getAttributes().getNamedItem("name").getNodeValue(); - String pkg = node.getAttributes().getNamedItem("pkg").getNodeValue(); - koans.put(packageTitle, getKoanElementAttributesByMethodNameBySuite(pkg, node.getChildNodes())); - } - return new Path(methodName, koans); - }catch(Exception x){ - throw new RuntimeException(x); - } - } - - Map> getKoanElementAttributesByMethodNameBySuite(String pkg, - NodeList childNodes) throws DOMException, ClassNotFoundException, - InstantiationException, IllegalAccessException { - Map> koansByMethodNameByClass = - new LinkedHashMap>(); - for (int i = 0; i < childNodes.getLength(); i++) { - Node node = childNodes.item(i); - if ("suite".equalsIgnoreCase(node.getNodeName())) { - String className = pkg + '.' + node.getAttributes().getNamedItem("class").getNodeValue(); - if (suiteName == null || suiteName.equalsIgnoreCase(className)) { - koansByMethodNameByClass.put(className, extractKoansAndRawLessons(className, node.getChildNodes())); - } - } - } - return koansByMethodNameByClass; - } - - Map extractKoansAndRawLessons( - String className, NodeList nodes) { - Map rawKoanAttributesByMethodName = new LinkedHashMap(); - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - if ("koan".equalsIgnoreCase(node.getNodeName())) { - NamedNodeMap attributes = node.getAttributes(); - String name = attributes.getNamedItem("name").getNodeValue(); - Node lesson = attributes.getNamedItem("lesson"); - String rawLesson = lesson == null ? null : lesson - .getNodeValue(); - Node displayKoanIncompleteExceptionNode = attributes - .getNamedItem("displayIncompleteKoanException"); - String displayIncompleteKoanException = displayKoanIncompleteExceptionNode == null ? null - : displayKoanIncompleteExceptionNode.getNodeValue(); - if (rawKoanAttributesByMethodName.containsKey(name)) { - throw new DuplicateKoanException(className, name); - } - rawKoanAttributesByMethodName.put(name, new KoanElementAttributes( - rawLesson, name, displayIncompleteKoanException, className)); - } - } - return rawKoanAttributesByMethodName; - } - - static class DuplicateKoanException extends RuntimeException { - private static final long serialVersionUID = 3846796580641690961L; - public DuplicateKoanException(String className, String name){ - super("Duplicate koans in the same suite: "+className+" with the name "+name); - } - } - -} - +package com.sandwich.koan.path.xmltransformation; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import com.sandwich.koan.path.PathToEnlightenment.Path; + +public class XmlToPathTransformerImpl implements XmlToPathTransformer { + + private final File xmlFile; + private final String suiteName; + private final String methodName; + + public XmlToPathTransformerImpl(){ + xmlFile = null; + suiteName = null; + methodName = null; + } + + public XmlToPathTransformerImpl(String xmlFileLocation, String suiteName, String methodName) throws FileNotFoundException { + this.xmlFile = new File(xmlFileLocation); + this.suiteName = suiteName; + this.methodName = methodName; + if(!xmlFile.exists()){ + throw new FileNotFoundException(xmlFile.getAbsolutePath() + + " was not found. it may have been deleted, renamed."); + } + } + + public Path transform(){ + try{ + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document doc = db.parse(xmlFile); + doc.getDocumentElement().normalize(); + NodeList nodeLst = doc.getElementsByTagName("package"); + /* + * Map of string values read from xml in following form + * package + * |-suite class name + * |-method name + * |-method attributes from xml + */ + Map>> koans = + new LinkedHashMap>>(); + for(int i = 0; i < nodeLst.getLength(); i++){ + Node node = nodeLst.item(i); + String packageTitle = node.getAttributes().getNamedItem("name").getNodeValue(); + String pkg = node.getAttributes().getNamedItem("pkg").getNodeValue(); + koans.put(packageTitle, getKoanElementAttributesByMethodNameBySuite(pkg, node.getChildNodes())); + } + return new Path(methodName, koans); + }catch(Exception x){ + throw new RuntimeException(x); + } + } + + Map> getKoanElementAttributesByMethodNameBySuite(String pkg, + NodeList childNodes) throws DOMException, ClassNotFoundException, + InstantiationException, IllegalAccessException { + Map> koansByMethodNameByClass = + new LinkedHashMap>(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node node = childNodes.item(i); + if ("suite".equalsIgnoreCase(node.getNodeName())) { + String className = pkg + '.' + node.getAttributes().getNamedItem("class").getNodeValue(); + if (suiteName == null || suiteName.equalsIgnoreCase(className)) { + koansByMethodNameByClass.put(className, extractKoansAndRawLessons(className, node.getChildNodes())); + } + } + } + return koansByMethodNameByClass; + } + + Map extractKoansAndRawLessons( + String className, NodeList nodes) { + Map rawKoanAttributesByMethodName = new LinkedHashMap(); + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + if ("koan".equalsIgnoreCase(node.getNodeName())) { + NamedNodeMap attributes = node.getAttributes(); + String name = attributes.getNamedItem("name").getNodeValue(); + Node displayKoanIncompleteExceptionNode = attributes + .getNamedItem("displayIncompleteKoanException"); + String displayIncompleteKoanException = displayKoanIncompleteExceptionNode == null ? null + : displayKoanIncompleteExceptionNode.getNodeValue(); + if (rawKoanAttributesByMethodName.containsKey(name)) { + throw new DuplicateKoanException(className, name); + } + rawKoanAttributesByMethodName.put(name, new KoanElementAttributes( + name, displayIncompleteKoanException, className)); + } + } + return rawKoanAttributesByMethodName; + } + + static class DuplicateKoanException extends RuntimeException { + private static final long serialVersionUID = 3846796580641690961L; + public DuplicateKoanException(String className, String name){ + super("Duplicate koans in the same suite: "+className+" with the name "+name); + } + } + +} + diff --git a/koans-lib/src/com/sandwich/koan/path/xmltransformation/XmlVariableDictionary.java b/lib/src/main/java/com/sandwich/koan/path/xmltransformation/XmlVariableDictionary.java similarity index 100% rename from koans-lib/src/com/sandwich/koan/path/xmltransformation/XmlVariableDictionary.java rename to lib/src/main/java/com/sandwich/koan/path/xmltransformation/XmlVariableDictionary.java diff --git a/koans-lib/src/com/sandwich/koan/result/KoanMethodResult.java b/lib/src/main/java/com/sandwich/koan/result/KoanMethodResult.java similarity index 100% rename from koans-lib/src/com/sandwich/koan/result/KoanMethodResult.java rename to lib/src/main/java/com/sandwich/koan/result/KoanMethodResult.java diff --git a/koans-lib/src/com/sandwich/koan/result/KoanSuiteResult.java b/lib/src/main/java/com/sandwich/koan/result/KoanSuiteResult.java similarity index 100% rename from koans-lib/src/com/sandwich/koan/result/KoanSuiteResult.java rename to lib/src/main/java/com/sandwich/koan/result/KoanSuiteResult.java diff --git a/lib/src/main/java/com/sandwich/koan/runner/AppLauncher.java b/lib/src/main/java/com/sandwich/koan/runner/AppLauncher.java new file mode 100755 index 00000000..af6cab9b --- /dev/null +++ b/lib/src/main/java/com/sandwich/koan/runner/AppLauncher.java @@ -0,0 +1,68 @@ +package com.sandwich.koan.runner; + +import java.io.File; +import java.io.IOException; +import java.util.Map; + +import com.sandwich.koan.ApplicationSettings; +import com.sandwich.koan.cmdline.CommandLineArgument; +import com.sandwich.koan.cmdline.CommandLineArgumentBuilder; +import com.sandwich.koan.cmdline.CommandLineArgumentRunner; +import com.sandwich.koan.constant.ArgumentType; +import com.sandwich.koan.util.ApplicationUtils; +import com.sandwich.util.io.FileMonitor; +import com.sandwich.util.io.FileMonitorFactory; +import com.sandwich.util.io.KoanFileCompileAndRunListener; +import com.sandwich.util.io.directories.DirectoryManager; + +public class AppLauncher { + + public static void main(final String... args) throws Throwable { + Map argsMap = new CommandLineArgumentBuilder( + args); + if (argsMap.containsKey(ArgumentType.RUN_KOANS)) { + if (ApplicationSettings.isInteractive()) { + new Thread(new Runnable() { + public void run() { + do { + try { + char c = (char) System.in.read(); + char exitChar = ApplicationSettings.getExitChar(); + if (Character.toUpperCase(c) == Character.toUpperCase(exitChar)) { + exit(0); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } while (true); + } + + }).start(); + FileMonitor monitor = FileMonitorFactory.getInstance(new File( + DirectoryManager.getProdMainDir()), new File( + DirectoryManager.getDataFile())); + if (ApplicationUtils.isFirstTimeAppHasBeenRun()) { + monitor.writeChanges(); + } + monitor.addFileSavedListener(new KoanFileCompileAndRunListener(argsMap)); + } + } + new CommandLineArgumentRunner(argsMap).run(); + if (ApplicationSettings.isDebug()) { + StringBuilder argsBuilder = new StringBuilder(); + int argNumber = 0; + for (String arg : args) { + argsBuilder.append("Argument number " + + String.valueOf(++argNumber) + ": '" + arg + "'"); + } + ApplicationUtils.getPresenter().displayMessage( + argsBuilder.toString()); + } + } + + static void exit(int status) { + FileMonitorFactory.closeAll(); + System.exit(status); + } + +} diff --git a/koans-lib/src/com/sandwich/koan/runner/KoanMethodRunner.java b/lib/src/main/java/com/sandwich/koan/runner/KoanMethodRunner.java similarity index 79% rename from koans-lib/src/com/sandwich/koan/runner/KoanMethodRunner.java rename to lib/src/main/java/com/sandwich/koan/runner/KoanMethodRunner.java index 4d3baca2..44576a15 100755 --- a/koans-lib/src/com/sandwich/koan/runner/KoanMethodRunner.java +++ b/lib/src/main/java/com/sandwich/koan/runner/KoanMethodRunner.java @@ -15,9 +15,15 @@ import com.sandwich.koan.constant.KoanConstants; import com.sandwich.koan.result.KoanMethodResult; import com.sandwich.util.ExceptionUtils; +import com.sandwich.util.Strings; +import com.sandwich.util.io.directories.DirectoryManager; +import com.sandwich.util.io.filecompiler.CompilerConfig; +import com.sandwich.util.io.filecompiler.FileCompiler; public class KoanMethodRunner { + private static final String EXPECTED_PROPERTY_KEY = "expected"; + public static KoanMethodResult run(Object suite, KoanMethod koan){ try { Method method = koan.getMethod(); @@ -30,7 +36,7 @@ public static KoanMethodResult run(Object suite, KoanMethod koan){ if(tempException instanceof KoanIncompleteException){ t = (KoanIncompleteException)tempException; message = t.getMessage(); - if(message.contains(EXPECTED_LEFT + __ + EXPECTED_RIGHT)) { + if(message.contains(Strings.getMessage(EXPECTED_PROPERTY_KEY) + EXPECTED_LEFT + __ + EXPECTED_RIGHT)) { logExpectationOnWrongSideWarning(suite.getClass(), koan.getMethod()); } break; @@ -61,8 +67,10 @@ private static void logExpectationOnWrongSideWarning(Class firstFailingSuite, * @return */ static String getOriginalLineNumber(Throwable t, Class failingSuite){ - String[] lines = ExceptionUtils.convertToPopulatedStackTraceString(t).split(EOLS); - if(failingSuite != null){ + if(failingSuite != null && + CompilerConfig.getSuffix(FileCompiler.getSourceFileFromClass(DirectoryManager.getSourceDir(), failingSuite.getName()).getAbsolutePath()) + .equals(".java")){ + String[] lines = ExceptionUtils.convertToPopulatedStackTraceString(t).split(EOLS); for(int i = lines.length - 1; i >= 0; --i){ String line = lines[i]; if(line.contains(failingSuite.getName())){ diff --git a/koans-lib/src/com/sandwich/koan/runner/RunKoans.java b/lib/src/main/java/com/sandwich/koan/runner/RunKoans.java similarity index 52% rename from koans-lib/src/com/sandwich/koan/runner/RunKoans.java rename to lib/src/main/java/com/sandwich/koan/runner/RunKoans.java index 3857e521..b945537d 100755 --- a/koans-lib/src/com/sandwich/koan/runner/RunKoans.java +++ b/lib/src/main/java/com/sandwich/koan/runner/RunKoans.java @@ -1,5 +1,6 @@ package com.sandwich.koan.runner; +import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.ArrayList; @@ -8,54 +9,66 @@ import java.util.Map; import java.util.Map.Entry; +import com.sandwich.koan.ApplicationSettings; import com.sandwich.koan.Koan; +import com.sandwich.koan.KoanClassLoader; import com.sandwich.koan.KoanMethod; import com.sandwich.koan.cmdline.behavior.AbstractArgumentBehavior; +import com.sandwich.koan.constant.KoanConstants; import com.sandwich.koan.path.PathToEnlightenment; import com.sandwich.koan.path.PathToEnlightenment.Path; import com.sandwich.koan.path.xmltransformation.KoanElementAttributes; import com.sandwich.koan.result.KoanMethodResult; import com.sandwich.koan.result.KoanSuiteResult; import com.sandwich.koan.result.KoanSuiteResult.KoanResultBuilder; -import com.sandwich.koan.ui.ConsolePresenter; -import com.sandwich.koan.ui.SuitePresenter; -import com.sandwich.util.Counter; +import com.sandwich.koan.util.ApplicationUtils; +import com.sandwich.util.ExceptionUtils; import com.sandwich.util.KoanComparator; -import com.sandwich.util.io.DynamicClassLoader; +import com.sandwich.util.io.KoanSuiteCompilationListener; +import com.sandwich.util.io.classloader.DynamicClassLoader; +import com.sandwich.util.io.directories.DirectoryManager; +import com.sandwich.util.io.filecompiler.CompilationListener; +import com.sandwich.util.io.filecompiler.FileCompiler; public class RunKoans extends AbstractArgumentBehavior { - private SuitePresenter presenter; private Path pathToEnlightenment; public RunKoans(){ - this(null, null); // use defaults coded in getters + this(null); // use defaults coded in getters } - public RunKoans(SuitePresenter presenter, Path pathToEnlightenment){ - this.presenter = presenter; + public RunKoans(Path pathToEnlightenment){ this.pathToEnlightenment = pathToEnlightenment; } - public void run(String value) { - getPresenter().displayResult(runKoans()); - } + public void run(String... values) { + ApplicationUtils.getPresenter().clearMessages(); + KoanSuiteResult result = runKoans(); + ApplicationUtils.getPresenter().displayResult(result); + if (!ApplicationSettings.isInteractive()) { + // could overflow past 255 resulting in 0 (ie all koans succeed) + // or otherwise misleading # of failed koans + AppLauncher.exit(Math.min(255, result.getTotalNumberOfKoans() - result.getNumberPassing())); + } + } KoanSuiteResult runKoans() { - Counter successfull = new Counter(); List passingSuites = new ArrayList(); List failingSuites = new ArrayList(); String level = null; KoanMethodResult failure = null; - DynamicClassLoader loader = new DynamicClassLoader(); - Path pathToEnlightement = getPathToEnlightement(); - for (Entry>> packages : pathToEnlightement) { + DynamicClassLoader loader = KoanClassLoader.getInstance(); + Path pathToEnlightenment = getPathToEnlightenment(); + KoanSuiteCompilationListener compilationListener = new KoanSuiteCompilationListener(); + int successfull = 0; + for (Entry>> packages : pathToEnlightenment) { for (Entry> e : packages.getValue().entrySet()) { String name = e.getKey().substring(e.getKey().lastIndexOf('.')+1); if(failure == null){ - Object suite = constructSuite(loader, e.getKey()); + Object suite = safelyConstructSuite(loader, compilationListener, e); final List attributes = new ArrayList(e.getValue().values()); - final List methods = mergeJavaFilesMethodsAndThoseInXml(suite, attributes, pathToEnlightement.getOnlyMethodNameToRun()); + final List methods = mergeJavaFilesMethodsAndThoseInXml(suite, attributes, pathToEnlightenment.getOnlyMethodNameToRun()); Collections.sort(methods, new KoanComparator()); for (final KoanMethod koan : methods) { KoanMethodResult result = KoanMethodRunner.run(suite, koan); @@ -65,7 +78,7 @@ KoanSuiteResult runKoans() { failingSuites.add(name); break; }else{ - successfull.count(); + successfull++; } } if (failure == null) { @@ -77,11 +90,33 @@ KoanSuiteResult runKoans() { } } } - return new KoanResultBuilder() .level(level) - .numberPassing((int)successfull.getCount()) - .totalNumberOfKoanMethods(pathToEnlightement.getTotalNumberOfKoans()) - .methodResult(failure) - .passingCases(passingSuites).remainingCases(failingSuites).build(); + return new KoanResultBuilder() + .level(level) + .numberPassing(successfull) + .totalNumberOfKoanMethods(pathToEnlightenment.getTotalNumberOfKoans()) + .methodResult(failure).passingCases(passingSuites) + .remainingCases(failingSuites).build(); + } + + private Object safelyConstructSuite(DynamicClassLoader loader, + KoanSuiteCompilationListener compilationListener, + Entry> e) { + // this is written strangely so stack trace will always be the same when constructSuite fails + // this permits the app to only show the compilation failure once, despite the fact this + // is getting hit every second. + Object suite = null; + while(suite == null || compilationListener.isLastCompilationAttemptFailure()) { + suite = constructSuite(loader, e.getKey(), compilationListener); + if(compilationListener.isLastCompilationAttemptFailure()){ + if(!ApplicationSettings.isInteractive()){ + AppLauncher.exit(255); + } + try { + Thread.sleep(1000); + } catch (InterruptedException e1) {} + } + } + return suite; } private List mergeJavaFilesMethodsAndThoseInXml(Object suite, @@ -109,10 +144,10 @@ private boolean isMethodEligibleForRunning(String onlyMethodNameToRun, String na return onlyMethodNameToRun == null || onlyMethodNameToRun.equals(name); } - private Object constructSuite(DynamicClassLoader loader, String className) { + private Object constructSuite(DynamicClassLoader loader, String className, CompilationListener listener) { Object suite; try { - Class clazz = loader.loadClass(className); + Class clazz = loader.loadClass(className, listener); if(!clazz.isAnonymousClass()){ suite = clazz.newInstance(); }else{ @@ -131,21 +166,27 @@ private Object constructSuite(DynamicClassLoader loader, String className) { } } catch (Exception e1) { throw new RuntimeException(e1); + } catch (Error e1) { + if(e1.getClass() == Error.class && e1.getMessage().contains("Unresolved compilation problem")) { + File sourceFile = FileCompiler.getSourceFileFromClass( + DirectoryManager.getSourceDir(), className); + listener.compilationFailed( + sourceFile, new String[]{}, -1, + "Unable to load class \""+className+"\"." + KoanConstants.EOL + + ExceptionUtils.convertToPopulatedStackTraceString(e1), e1); + return null; // just consume this exception, this will have been logged and is handled + }else{ + throw new RuntimeException(e1); + } } return suite; } - private Path getPathToEnlightement() { + private Path getPathToEnlightenment() { if(pathToEnlightenment == null){ - pathToEnlightenment = PathToEnlightenment.getPathToEnlightment(); + pathToEnlightenment = PathToEnlightenment.getPathToEnlightenment(); } return pathToEnlightenment; } - - private SuitePresenter getPresenter(){ - if(presenter == null){ - presenter = new ConsolePresenter(); - } - return presenter; - } + } diff --git a/koans-lib/src/com/sandwich/koan/ui/AbstractSuitePresenter.java b/lib/src/main/java/com/sandwich/koan/ui/AbstractSuitePresenter.java similarity index 99% rename from koans-lib/src/com/sandwich/koan/ui/AbstractSuitePresenter.java rename to lib/src/main/java/com/sandwich/koan/ui/AbstractSuitePresenter.java index 2b959ec5..705dd1a9 100755 --- a/koans-lib/src/com/sandwich/koan/ui/AbstractSuitePresenter.java +++ b/lib/src/main/java/com/sandwich/koan/ui/AbstractSuitePresenter.java @@ -14,7 +14,7 @@ public void displayResult(KoanSuiteResult result) { displayPassingFailing(result); displayHeader(result); } - + abstract protected void displayHeader(KoanSuiteResult result); abstract protected void displayPassingFailing(KoanSuiteResult result); abstract protected void displayChart(KoanSuiteResult result); diff --git a/koans-lib/src/com/sandwich/koan/ui/ConsolePresenter.java b/lib/src/main/java/com/sandwich/koan/ui/ConsolePresenter.java similarity index 58% rename from koans-lib/src/com/sandwich/koan/ui/ConsolePresenter.java rename to lib/src/main/java/com/sandwich/koan/ui/ConsolePresenter.java index 024a3086..0463c6c2 100755 --- a/koans-lib/src/com/sandwich/koan/ui/ConsolePresenter.java +++ b/lib/src/main/java/com/sandwich/koan/ui/ConsolePresenter.java @@ -1,37 +1,41 @@ package com.sandwich.koan.ui; -import static com.sandwich.koan.constant.KoanConstants.ALL_SUCCEEDED; -import static com.sandwich.koan.constant.KoanConstants.CONQUERED; -import static com.sandwich.koan.constant.KoanConstants.ENCOURAGEMENT; import static com.sandwich.koan.constant.KoanConstants.EOL; -import static com.sandwich.koan.constant.KoanConstants.FAILING_SUITES; -import static com.sandwich.koan.constant.KoanConstants.INVESTIGATE_IN_THE; -import static com.sandwich.koan.constant.KoanConstants.KOAN; -import static com.sandwich.koan.constant.KoanConstants.OUT_OF; -import static com.sandwich.koan.constant.KoanConstants.PASSING_SUITES; -import static com.sandwich.koan.constant.KoanConstants.PROGRESS; import static com.sandwich.koan.constant.KoanConstants.PROGRESS_BAR_END; import static com.sandwich.koan.constant.KoanConstants.PROGRESS_BAR_START; import static com.sandwich.koan.constant.KoanConstants.PROGRESS_BAR_WIDTH; -import static com.sandwich.koan.constant.KoanConstants.WHATS_WRONG; import java.util.List; +import com.sandwich.koan.ApplicationSettings; import com.sandwich.koan.KoanMethod; import com.sandwich.koan.constant.KoanConstants; import com.sandwich.koan.result.KoanSuiteResult; +import com.sandwich.koan.util.ApplicationUtils; +import com.sandwich.util.Strings; public class ConsolePresenter extends AbstractSuitePresenter { + private static final int NUMBER_OF_LINES_TO_CLEAR_CONSOLE = 80; + + public void clearMessages(){ + for(int i = 0; i < NUMBER_OF_LINES_TO_CLEAR_CONSOLE; i++){ + ApplicationUtils.getPresenter().displayMessage(" "); + } + } + protected void displayHeader(KoanSuiteResult result){ } protected void displayPassingFailing(KoanSuiteResult result) { StringBuilder sb = new StringBuilder(); - appendLabeledClassesList(PASSING_SUITES, result.getPassingSuites(), sb); - appendLabeledClassesList(FAILING_SUITES, result.getRemainingSuites(), sb); - sb.append(EOL).append("Edit & save a koan to reload or enter '"+ KoanConstants.EXIT_CHARACTER +"' to exit"); - System.out.println(sb.toString()); + appendLabeledClassesList(Strings.getMessage("passing_suites")+":", result.getPassingSuites(), sb); + appendLabeledClassesList(Strings.getMessage("remaining_suites")+":", result.getRemainingSuites(), sb); + sb.append(EOL).append("Edit & save a koan").append( + ApplicationSettings.isInteractive() ? + " to test your progress, or enter '" + ApplicationSettings.getExitChar() +"' to exit." : + ", then rerun to test your progress"); + displayMessage(sb.toString()); } private void appendLabeledClassesList(String suiteType, List suites, StringBuilder sb) { @@ -49,12 +53,12 @@ private void appendLabeledClassesList(String suiteType, List suites, Str } protected void displayChart(KoanSuiteResult result) { - StringBuilder sb = new StringBuilder(KoanConstants.LEVEL).append(result.getLevel()).append(EOL); + StringBuilder sb = new StringBuilder(Strings.getMessage("level")).append(": ").append(result.getLevel()).append(EOL); int numberPassing = result.getNumberPassing(); int totalKoans = result.getTotalNumberOfKoans(); double percentPassing = ((double) numberPassing) / ((double) totalKoans); int percentScaledToFifty = (int) (percentPassing * PROGRESS_BAR_WIDTH); - sb.append(PROGRESS).append(" ").append(PROGRESS_BAR_START); + sb.append(Strings.getMessage("progress")).append(" ").append(PROGRESS_BAR_START); for (int i = 0; i < PROGRESS_BAR_WIDTH; i++) { if (i < percentScaledToFifty) { sb.append(KoanConstants.COMPLETE_CHAR); @@ -65,43 +69,44 @@ protected void displayChart(KoanSuiteResult result) { sb.append(PROGRESS_BAR_END); sb.append(' '); sb.append(numberPassing).append("/").append(totalKoans); - System.out.println(sb.toString()); + displayMessage(sb.toString()); } @Override protected void displayAllSuccess(KoanSuiteResult result) { - System.out.println(new StringBuilder(EOL).append(ALL_SUCCEEDED).toString()); + displayMessage(new StringBuilder(EOL).append(Strings.getMessage("all_koans_succeeded")).toString()); } @Override protected void displayOneOrMoreFailure(KoanSuiteResult result) { printSuggestion(result); String message = result.getMessage(); - StringBuilder sb = new StringBuilder( - message == null || message.length() == 0 || !result.displayIncompleteException() ? "" - : new StringBuilder(WHATS_WRONG).append( + StringBuilder sb = new StringBuilder(); + if (ApplicationSettings.isExpectationResultVisible()) { + sb.append(message == null || message.length() == 0 || !result.displayIncompleteException() ? "" + : new StringBuilder(Strings.getMessage("what_went_wrong")).append( + ": ").append( EOL).append( message).append( - EOL).append( EOL)); - if(KoanConstants.ENABLE_ENCOURAGEMENT){ // added noise to console output, and no real value + } + if(ApplicationSettings.isEncouragementEnabled()){ // added noise to console output, and no real value int totalKoans = result.getTotalNumberOfKoans(); int numberPassing = result.getNumberPassing(); sb.append( EOL).append( - CONQUERED).append( + Strings.getMessage("you_have_conquered")).append( " ").append( numberPassing).append( " ").append( - OUT_OF).append( + Strings.getMessage("out_of")).append( " ").append( totalKoans).append( " ").append( - KOAN).append( - totalKoans != 1 ? 's' : "").append( - "! ").append(ENCOURAGEMENT).append( - EOL); + totalKoans != 1 ? Strings.getMessage("koans") : Strings.getMessage("koan")).append( + "! ").append( + Strings.getMessage("encouragement")); } - System.out.print(sb.toString()); + displayMessage(sb.toString()); } protected void printSuggestion(KoanSuiteResult result) { @@ -110,14 +115,14 @@ protected void printSuggestion(KoanSuiteResult result) { buildInvestigateLine(sb, result.getFailingCase(),failedKoan.getMethod().getName()); sb.append(EOL).append(EOL); buildLineClue(sb, result); - System.out.println(sb.toString()); + displayMessage(sb.toString()); } private StringBuilder buildInvestigateLine(StringBuilder sb, String simpleName, String methodName) { return sb.append( - INVESTIGATE_IN_THE).append( - " ").append( + Strings.getMessage("investigate")).append( + ": ").append( simpleName).append( " class's ").append( methodName).append( @@ -126,10 +131,9 @@ private StringBuilder buildInvestigateLine(StringBuilder sb, String simpleName, private StringBuilder buildLineClue(StringBuilder sb, KoanSuiteResult result) { if(result.getLineNumber() != null && result.getLineNumber().trim().length() != 0){ - sb.append( "Line ").append( - result.getLineNumber()).append( - " may offer a clue as to how you may progress, now make haste!").append( - EOL); + sb.append( Strings.getMessage("line")).append(" ").append( + result.getLineNumber()).append(" ").append( + Strings.getMessage("may_offer_clue")); } return sb; } @@ -141,4 +145,12 @@ private StringBuilder buildLessonLine(KoanMethod failedKoan) { } return new StringBuilder(lesson).append(EOL).append(EOL); } + + public void displayError(String error) { + System.err.println(error); + } + + public void displayMessage(String message) { + System.out.println(message); + } } diff --git a/lib/src/main/java/com/sandwich/koan/ui/SuitePresenter.java b/lib/src/main/java/com/sandwich/koan/ui/SuitePresenter.java new file mode 100755 index 00000000..652783fe --- /dev/null +++ b/lib/src/main/java/com/sandwich/koan/ui/SuitePresenter.java @@ -0,0 +1,13 @@ +package com.sandwich.koan.ui; + +import com.sandwich.koan.result.KoanSuiteResult; +import com.sandwich.util.io.ui.ErrorPresenter; + +public interface SuitePresenter extends ErrorPresenter { + + public void displayResult(KoanSuiteResult result); + public void displayError(String error); + public void displayMessage(String error); + public void clearMessages(); + +} diff --git a/koans-lib/src/com/sandwich/koan/util/ApplicationUtils.java b/lib/src/main/java/com/sandwich/koan/util/ApplicationUtils.java similarity index 51% rename from koans-lib/src/com/sandwich/koan/util/ApplicationUtils.java rename to lib/src/main/java/com/sandwich/koan/util/ApplicationUtils.java index 0cc0f5ad..ab7f1855 100644 --- a/koans-lib/src/com/sandwich/koan/util/ApplicationUtils.java +++ b/lib/src/main/java/com/sandwich/koan/util/ApplicationUtils.java @@ -2,10 +2,14 @@ import java.io.File; +import com.sandwich.koan.ui.ConsolePresenter; +import com.sandwich.koan.ui.SuitePresenter; import com.sandwich.util.io.directories.DirectoryManager; public class ApplicationUtils { + private static SuitePresenterFactory suitePresenterFactory = new SuitePresenterFactory(); + static public boolean isFirstTimeAppHasBeenRun() { File dataDirectory = new File(DirectoryManager.getDataDir()); return !dataDirectory.exists(); @@ -14,5 +18,14 @@ static public boolean isFirstTimeAppHasBeenRun() { static public boolean isWindows(){ return System.getProperty("os.name").toLowerCase().contains("win"); } + + static public SuitePresenter getPresenter(){ + return suitePresenterFactory.create(); + } + public static class SuitePresenterFactory { + protected SuitePresenter create(){ + return new ConsolePresenter(); + } + } } diff --git a/koans-lib/src/com/sandwich/util/Assert.java b/lib/src/main/java/com/sandwich/util/Assert.java similarity index 95% rename from koans-lib/src/com/sandwich/util/Assert.java rename to lib/src/main/java/com/sandwich/util/Assert.java index b49c4736..6e4f62ae 100755 --- a/koans-lib/src/com/sandwich/util/Assert.java +++ b/lib/src/main/java/com/sandwich/util/Assert.java @@ -1,75 +1,75 @@ -package com.sandwich.util; - -import com.sandwich.koan.KoanIncompleteException; -import com.sandwich.koan.constant.KoanConstants; - -public class Assert { - - static final String EXPECTED = "expected:<"; - static final String END = ">"; - static final String BUT_WAS = "> but was:<"; - - public static void assertEquals(String msg, Object o0, Object o1){ - if(o0 == null && o1 != null){ - fail(msg, o0, o1); - } - if(o1 == null && o0 != null){ - fail(msg, o0, o1); - } - // not if o0 == o1 return, because equals may violate contract (though - // that's obviously strongly discouraged), but cannot invoke equals on - // null pointer w/o sacrificing functionality from anticipating failure - if(o1 == null && o0 == null){ - return; - } - if(!o0.equals(o1)){ - fail(msg, o0, o1); - } - } - - public static void assertEquals(Object o0, Object o1){ - assertEquals("", o0, o1); - } - - public static void assertTrue(Object t){ - assertEquals(true,t); - } - - public static void assertFalse(Object f){ - assertEquals(false,f); - } - - public static void assertNull(Object o){ - assertEquals(null, o); - } - - public static void assertNotNull(Object o){ - if(o == null){ - fail("something other than null",o); - } - } - - public static void assertSame(Object o0, Object o1){ - if(o0 != o1){ - fail("Are the same instance... ",o0,o1); - } - } - - public static void assertNotSame(Object o0, Object o1){ - if(o0 == o1){ - fail("Not the same instance... ",o0,o1); - } - } - - public static void fail(Object o0, Object o1) throws KoanIncompleteException { - fail("", o0, o1); - } - - public static void fail(String msg, Object o0, Object o1){ - fail(msg+(msg.length() == 0 ? "" : KoanConstants.EOL)+EXPECTED+o0+BUT_WAS+o1+END); - } - - public static void fail(String msg){ - throw new KoanIncompleteException(msg); - } -} +package com.sandwich.util; + +import com.sandwich.koan.KoanIncompleteException; +import com.sandwich.koan.constant.KoanConstants; + +public class Assert { + + static final String EXPECTED = "expected:<"; + static final String END = ">"; + static final String BUT_WAS = "> but was:<"; + + public static void assertEquals(String msg, Object o0, Object o1){ + if(o0 == null && o1 != null){ + fail(msg, o0, o1); + } + if(o1 == null && o0 != null){ + fail(msg, o0, o1); + } + // not if o0 == o1 return, because equals may violate contract (though + // that's obviously strongly discouraged), but cannot invoke equals on + // null pointer w/o sacrificing functionality from anticipating failure + if(o1 == null && o0 == null){ + return; + } + if(!o0.equals(o1)){ + fail(msg, o0, o1); + } + } + + public static void assertEquals(Object o0, Object o1){ + assertEquals("", o0, o1); + } + + public static void assertTrue(Object t){ + assertEquals(true,t); + } + + public static void assertFalse(Object f){ + assertEquals(false,f); + } + + public static void assertNull(Object o){ + assertEquals(null, o); + } + + public static void assertNotNull(Object o){ + if(o == null){ + fail("something other than null",o); + } + } + + public static void assertSame(Object o0, Object o1){ + if(o0 != o1){ + fail("Are the same instance... ",o0,o1); + } + } + + public static void assertNotSame(Object o0, Object o1){ + if(o0 == o1){ + fail("Not the same instance... ",o0,o1); + } + } + + public static void fail(Object o0, Object o1) throws KoanIncompleteException { + fail("", o0, o1); + } + + public static void fail(String msg, Object o0, Object o1){ + fail(msg+(msg.length() == 0 ? "" : KoanConstants.EOL)+EXPECTED+o0+BUT_WAS+o1+END); + } + + public static void fail(String msg){ + throw new KoanIncompleteException(msg); + } +} diff --git a/koans-lib/src/com/sandwich/util/Builder.java b/lib/src/main/java/com/sandwich/util/Builder.java similarity index 100% rename from koans-lib/src/com/sandwich/util/Builder.java rename to lib/src/main/java/com/sandwich/util/Builder.java diff --git a/koans-lib/src/com/sandwich/util/ExceptionUtils.java b/lib/src/main/java/com/sandwich/util/ExceptionUtils.java similarity index 100% rename from koans-lib/src/com/sandwich/util/ExceptionUtils.java rename to lib/src/main/java/com/sandwich/util/ExceptionUtils.java diff --git a/lib/src/main/java/com/sandwich/util/KoanComparator.java b/lib/src/main/java/com/sandwich/util/KoanComparator.java new file mode 100755 index 00000000..b41499ad --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/KoanComparator.java @@ -0,0 +1,40 @@ +package com.sandwich.util; + +import java.util.Comparator; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.sandwich.koan.KoanMethod; +import com.sandwich.util.io.directories.DirectoryManager; +import com.sandwich.util.io.filecompiler.FileCompiler; + +public class KoanComparator implements Comparator { + + public int compare(KoanMethod arg0, KoanMethod arg1) { + Class declaringClass0 = arg0.getMethod().getDeclaringClass(); + Class declaringClass1 = arg1.getMethod().getDeclaringClass(); + if(declaringClass0 != declaringClass1){ + Logger.getAnonymousLogger().severe("no idea how to handle comparing the classes: " + declaringClass0 + " and: "+declaringClass1); + return 0; + } + String contentsOfOriginalJavaFile = FileCompiler.getContentsOfJavaFile(DirectoryManager.getSourceDir(), declaringClass0.getName()); + String pattern = ".*\\s%s(\\(|\\s*\\))"; + Integer index0 = indexOfMatch(contentsOfOriginalJavaFile, String.format(pattern, arg0.getMethod().getName())); + Integer index1 = indexOfMatch(contentsOfOriginalJavaFile, String.format(pattern, arg1.getMethod().getName())); + return index0.compareTo(index1); + } + + /* + * TODO: This is utility code... + */ + private int indexOfMatch(String inputString, String pattern) { + Pattern p = Pattern.compile(pattern); + Matcher m = p.matcher(inputString); + if (m.find()) { + return m.start(); + } + return -1; + } + +} \ No newline at end of file diff --git a/lib/src/main/java/com/sandwich/util/Strings.java b/lib/src/main/java/com/sandwich/util/Strings.java new file mode 100644 index 00000000..dc328081 --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/Strings.java @@ -0,0 +1,82 @@ +package com.sandwich.util; +import static com.sandwich.koan.constant.KoanConstants.PERIOD; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.PropertyResourceBundle; +import java.util.ResourceBundle; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.sandwich.util.io.directories.DirectoryManager; + +public class Strings { + + private static final ResourceBundle MESSAGES_BUNDLE = getMessagesBundle(); + private static final String UNFOUND_PROP_VALUE_STRING = "!"; + + private Strings() { + } + + public static String getMessage(String key) { + try { + return MESSAGES_BUNDLE.getString(key); + } catch (MissingResourceException e) { + return new StringBuilder(UNFOUND_PROP_VALUE_STRING).append(key).append(UNFOUND_PROP_VALUE_STRING).toString(); + } + } + + public static String getMessage(Class clazz, String key){ + return Strings.getMessage(new StringBuilder(clazz.getSimpleName()).append(PERIOD).append(key).toString()); + } + + public static String[] getMessages(Class clazz, String key) { + String[] tmp = getMessage(clazz, key).split(","); + String[] trimmed = new String[tmp.length]; + for(int i = 0; i < tmp.length; i++){ + trimmed[i] = tmp[i].trim(); + } + return trimmed; + } + + private static ResourceBundle getMessagesBundle(){ + if(MESSAGES_BUNDLE == null){ + return createResourceBundle(); + } + return MESSAGES_BUNDLE; + } + + /** + * conditionally create messages bundle for proper locale, not on classpath so need to handle manually + * @return a resource bundle for default locale, or USA if default locale's language has no translated messages + */ + static ResourceBundle createResourceBundle() { + ResourceBundle temp = null; + try { + temp = new PropertyResourceBundle(new FileInputStream( + DirectoryManager.injectFileSystemSeparators( + DirectoryManager.getProjectI18nDir(), + new StringBuilder("messages_").append( + Locale.getDefault().getLanguage()).append( + ".properties").toString()))); + } catch(FileNotFoundException x) { + try { + Logger.getLogger(Strings.class.getName()).log(Level.INFO, "Your default language is not supported yet. "+x.getLocalizedMessage()); + temp = new PropertyResourceBundle(new FileInputStream( + DirectoryManager.injectFileSystemSeparators( + DirectoryManager.getProjectI18nDir(), "messages_en.properties"))); + } catch (Exception e) { + throw new RuntimeException(e); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return temp; + } + + public static boolean wasNotFound(String lesson) { + return lesson.startsWith(UNFOUND_PROP_VALUE_STRING) && lesson.endsWith(UNFOUND_PROP_VALUE_STRING); + } +} diff --git a/lib/src/main/java/com/sandwich/util/io/CopyFileOperation.java b/lib/src/main/java/com/sandwich/util/io/CopyFileOperation.java new file mode 100644 index 00000000..475bc200 --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/CopyFileOperation.java @@ -0,0 +1,39 @@ +package com.sandwich.util.io; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class CopyFileOperation extends SourceAndDestinationFileAction { + + public CopyFileOperation(File source, File destination) { + super(source, destination); + } + + public CopyFileOperation(String sourceDir, String destinationDir) { + super(sourceDir, destinationDir); + } + + public void sourceToDestination(File src, File dest) throws IOException { + InputStream in = new FileInputStream(src); + if(!dest.getParentFile().exists()){ + dest.getParentFile().mkdirs(); + } + OutputStream out = new FileOutputStream(dest); + + // Transfer bytes from in to out + byte[] buf = new byte[1024]; + int len; + + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + + in.close(); + out.close(); + } + +} diff --git a/koans-lib/src/com/sandwich/util/io/DataFileHelper.java b/lib/src/main/java/com/sandwich/util/io/DataFileHelper.java similarity index 54% rename from koans-lib/src/com/sandwich/util/io/DataFileHelper.java rename to lib/src/main/java/com/sandwich/util/io/DataFileHelper.java index 4dbe6ab0..99aef327 100644 --- a/koans-lib/src/com/sandwich/util/io/DataFileHelper.java +++ b/lib/src/main/java/com/sandwich/util/io/DataFileHelper.java @@ -1,10 +1,13 @@ package com.sandwich.util.io; +import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.io.UTFDataFormatException; /** * Handles persistence to/from a filesystem, and makes assumption instances of T @@ -17,28 +20,46 @@ public class DataFileHelper { private File dataFile; private T lastRetrieval; + private T defaultState; - public DataFileHelper(String absolutePath, T defaultState){ - dataFile = new File(absolutePath); + public DataFileHelper(File dataFile, T defaultState){ + this.dataFile = dataFile; if(!dataFile.exists()){ - save(defaultState); + dataFile.getParentFile().mkdirs(); + write(defaultState); } + this.defaultState = defaultState; Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { public void run() { if(lastRetrieval != null){ - save(lastRetrieval); + write(lastRetrieval); } } })); - } @SuppressWarnings("unchecked") - public T retrieve(){ + public T read(){ ObjectInputStream objectInputStream = null; try { - objectInputStream = new ObjectInputStream(new FileInputStream(getDataFile())); - return lastRetrieval = (T)objectInputStream.readObject(); + File dataFile = getDataFile(); + if(!dataFile.exists()){ + return null; + } + objectInputStream = new ObjectInputStream(new FileInputStream(dataFile)); + if(dataFile.exists()){ + try { + return lastRetrieval = (T)objectInputStream.readObject(); + }catch(UTFDataFormatException x){ + createNewFile(dataFile); + return defaultState; + }catch(EOFException x){ + createNewFile(dataFile); + return defaultState; + } + }else{ + return null; + } } catch (Exception e) { try{ if(objectInputStream != null){ @@ -48,10 +69,24 @@ public T retrieve(){ throw new RuntimeException(e2); } throw new RuntimeException(e); + } finally { + if(objectInputStream != null){ + try { + objectInputStream.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } } } - public void save(T state){ + private void createNewFile(File dataFile) throws IOException { + dataFile.delete(); + dataFile.createNewFile(); + write(defaultState); + } + + public void write(T state){ ObjectOutputStream objectOutputStream = null; try { objectOutputStream = new ObjectOutputStream(new FileOutputStream(getDataFile())); @@ -65,6 +100,14 @@ public void save(T state){ throw new RuntimeException(e2); } throw new RuntimeException(e); + } finally { + if(objectOutputStream != null){ + try { + objectOutputStream.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } } } diff --git a/lib/src/main/java/com/sandwich/util/io/ExistingFileAction.java b/lib/src/main/java/com/sandwich/util/io/ExistingFileAction.java new file mode 100644 index 00000000..fb6e6f10 --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/ExistingFileAction.java @@ -0,0 +1,24 @@ +package com.sandwich.util.io; + +import java.io.File; +import java.io.IOException; + +public abstract class ExistingFileAction extends FileOperation { + + public ExistingFileAction(String... strings) { + super(strings); + } + + public void onNull(File file) throws IOException { + throwNonExistentFileError(String.valueOf(file)); + } + + public void onNew(File file) throws IOException { + throwNonExistentFileError(file.getAbsolutePath()); + } + + private String throwNonExistentFileError(String path) { + throw new IllegalArgumentException("Source path must actually exist. ("+ path +")"); + } + +} diff --git a/lib/src/main/java/com/sandwich/util/io/FileAction.java b/lib/src/main/java/com/sandwich/util/io/FileAction.java new file mode 100644 index 00000000..e3e66b78 --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/FileAction.java @@ -0,0 +1,13 @@ +package com.sandwich.util.io; + +import java.io.File; +import java.io.IOException; + +public interface FileAction { + + void onFile(File file) throws IOException; + void onDirectory(File dir) throws IOException; + void onNull(File nullFile) throws IOException; + void onNew(File newFile) throws IOException; + +} diff --git a/koans-lib/src/com/sandwich/util/io/FileListener.java b/lib/src/main/java/com/sandwich/util/io/FileListener.java similarity index 100% rename from koans-lib/src/com/sandwich/util/io/FileListener.java rename to lib/src/main/java/com/sandwich/util/io/FileListener.java diff --git a/koans-lib/src/com/sandwich/util/io/FileMonitor.java b/lib/src/main/java/com/sandwich/util/io/FileMonitor.java similarity index 59% rename from koans-lib/src/com/sandwich/util/io/FileMonitor.java rename to lib/src/main/java/com/sandwich/util/io/FileMonitor.java index 756d4120..eb9f1415 100644 --- a/koans-lib/src/com/sandwich/util/io/FileMonitor.java +++ b/lib/src/main/java/com/sandwich/util/io/FileMonitor.java @@ -7,25 +7,25 @@ import java.util.Map; import java.util.Vector; -import com.sandwich.util.io.directories.DirectoryManager; +import com.sandwich.koan.ApplicationSettings; public class FileMonitor { - - public static String FILE_HASH_FILE_NAME = "file_hashes.dat"; private final List listeners = new Vector(); - private static DataFileHelper> fileHashesHelper = new DataFileHelper>( - getFileHashesDataFilePath(), new HashMap()); + private final DataFileHelper> fileHashesHelper; - private static Map fileHashesByDirectory = fileHashesHelper.retrieve(); + private final Map fileHashesByDirectory; final File fileSystemPath; - public FileMonitor(String fileSystemPath){ - this.fileSystemPath = new File(fileSystemPath); + public FileMonitor(File fileSystemPath, File dataFile) { + this.fileSystemPath = fileSystemPath; if(!this.fileSystemPath.exists()){ throw new IllegalArgumentException(fileSystemPath+ " cannot be found."); } + fileHashesHelper = new DataFileHelper>( + dataFile, new HashMap()); + fileHashesByDirectory = fileHashesHelper.read(); } public String getFilesystemPath() { @@ -50,13 +50,18 @@ synchronized void notifyListeners(){ for(String fileName : currentHashes.keySet()){ Long currentHash = currentHashes.get(fileName); Long previousHash = fileHashesByDirectory.get(fileName); - if(previousHash != null && !currentHash.equals(previousHash)){ - fileHashesByDirectory.put(fileName, currentHash); - File file = new File(fileName); - for(FileListener listener : listeners){ + + fileHashesByDirectory.put(fileName, currentHash); + File file = new File(fileName); + + for(FileListener listener : listeners){ + if(previousHash == null && currentHash != null){ + listener.newFile(file); + }else if(currentHash == null && previousHash != null){ + listener.fileDeleted(file); + }else if(!currentHash.equals(previousHash)){ listener.fileSaved(file); } - break; } } } catch (IOException e) { @@ -66,21 +71,13 @@ synchronized void notifyListeners(){ Map getFilesystemHashes() throws IOException { final HashMap fileHashes = new HashMap(); - FileUtils.forEachFile(fileSystemPath, fileSystemPath, new FileAction(){ - public File makeDestination(File dest, String fileInDirectory) { - return new File(dest, fileInDirectory); - } - public void sourceToDestination(File src, File dest) throws IOException { + new ForEachFileAction(ApplicationSettings.getMonitorIgnorePattern()){ + public void onFile(File src) throws IOException { fileHashes.put(src.getAbsolutePath(), src.lastModified()); } - }); + }.operate(fileSystemPath); return fileHashes; } - - private static String getFileHashesDataFilePath(){ - return new StringBuilder(DirectoryManager.getDataDir()) - .append(System.getProperty("file.separator")).append(FILE_HASH_FILE_NAME).toString(); - } public boolean isFileModifiedSinceLastPoll(String filePath, Long lastModified) { Long previousHash = fileHashesByDirectory.get(filePath); @@ -90,5 +87,13 @@ public boolean isFileModifiedSinceLastPoll(String filePath, Long lastModified) { public void updateFileSaveTime(File file) { fileHashesByDirectory.put(file.getAbsolutePath(), file.lastModified()); } + + public void writeChanges() { + try { + fileHashesHelper.write(getFilesystemHashes()); + } catch (IOException e) { + e.printStackTrace(); + } + } } diff --git a/lib/src/main/java/com/sandwich/util/io/FileMonitorFactory.java b/lib/src/main/java/com/sandwich/util/io/FileMonitorFactory.java new file mode 100644 index 00000000..eca510cd --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/FileMonitorFactory.java @@ -0,0 +1,75 @@ +package com.sandwich.util.io; + +import java.io.File; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; + +import com.sandwich.koan.ApplicationSettings; + +public class FileMonitorFactory { + + private static Map monitors = new LinkedHashMap(); + public static final int SLEEP_TIME_IN_MS = 500; + + static { + Runtime.getRuntime().addShutdownHook(new Thread(new Runnable(){ + public void run() { + for(FileMonitor monitor : monitors.values()){ + monitor.close(); + } + monitors.clear(); + } + })); + Thread pollingThread = new Thread(new Runnable(){ + public void run() { + do{ + if(ApplicationSettings.isInteractive()){ + try { + Thread.sleep(SLEEP_TIME_IN_MS); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + for(Entry filePathAndMonitor : monitors.entrySet()){ + FileMonitor monitor = filePathAndMonitor.getValue(); + if(monitor != null){ + monitor.notifyListeners(); + } + } + }while(ApplicationSettings.isInteractive()); + } + }); + pollingThread.setName("FileMonitorPolling"); + if (ApplicationSettings.isInteractive()) { + pollingThread.start(); + } + } + + public static FileMonitor getInstance(File monitoredFile, File dataFile) { + String key = monitoredFile.getAbsolutePath() + dataFile.getAbsolutePath(); + FileMonitor monitor = monitors.get(key); + if(monitor == null){ + monitor = new FileMonitor(monitoredFile, dataFile); + monitors.put(key, monitor); + } + return monitor; + } + + public static void removeInstance(FileMonitor monitor){ + monitor.close(); + monitors.remove(monitor.getFilesystemPath()); + } + + public static void removeInstance(String absolutePath) { + monitors.remove(absolutePath); + } + + public static void closeAll() { + for(FileMonitor monitor : monitors.values()){ + monitor.close(); + } + monitors.clear(); + } + +} diff --git a/lib/src/main/java/com/sandwich/util/io/FileOperation.java b/lib/src/main/java/com/sandwich/util/io/FileOperation.java new file mode 100644 index 00000000..1cea8894 --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/FileOperation.java @@ -0,0 +1,49 @@ +package com.sandwich.util.io; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +public abstract class FileOperation implements FileAction { + + private List ignoredPaths; + + public FileOperation(String... ignoredPaths){ + this(Arrays.asList(ignoredPaths)); + } + + public FileOperation(List ignoredPaths){ + this.ignoredPaths = ignoredPaths; + } + + public void operate(File file) throws IOException { + if(!isIgnored(file)){ + if(file == null){ + onNull(file); + }else if(!file.exists()){ + onNew(file); + }else if(file.isDirectory()){ + onDirectory(file); + }else{ + onFile(file); + } + } + } + + boolean isIgnored(File file){ + boolean ignore = false; + while(file != null){ + String end = file.getAbsolutePath().substring(file.getAbsolutePath().lastIndexOf(System.getProperty("file.separator")) + 1); + for(String pathToIgnore: ignoredPaths){ + ignore = end.matches(pathToIgnore); + if(ignore){ + return true; + } + } + file = file.getParentFile(); + } + return ignore; + } + +} diff --git a/lib/src/main/java/com/sandwich/util/io/FileUtils.java b/lib/src/main/java/com/sandwich/util/io/FileUtils.java new file mode 100644 index 00000000..d91bd9b9 --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/FileUtils.java @@ -0,0 +1,24 @@ +package com.sandwich.util.io; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +public class FileUtils { + + public static String readFileAsString(File file) { + byte[] buffer = new byte[(int) file.length()]; + BufferedInputStream f = null; + try { + f = new BufferedInputStream(new FileInputStream(file)); + f.read(buffer); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + if (f != null) try { f.close(); } catch (IOException ignored) { } + } + return new String(buffer); + } + +} diff --git a/lib/src/main/java/com/sandwich/util/io/ForEachFileAction.java b/lib/src/main/java/com/sandwich/util/io/ForEachFileAction.java new file mode 100644 index 00000000..cfd04179 --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/ForEachFileAction.java @@ -0,0 +1,18 @@ +package com.sandwich.util.io; + +import java.io.File; +import java.io.IOException; + +public abstract class ForEachFileAction extends ExistingFileAction { + + public ForEachFileAction(String... strings) { + super(strings); + } + + public void onDirectory(File dir) throws IOException { + for (String fileName : dir.list()) { + operate(new File(dir, fileName)); + } + } + +} diff --git a/lib/src/main/java/com/sandwich/util/io/KoanFileCompileAndRunListener.java b/lib/src/main/java/com/sandwich/util/io/KoanFileCompileAndRunListener.java new file mode 100644 index 00000000..da652a03 --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/KoanFileCompileAndRunListener.java @@ -0,0 +1,66 @@ +package com.sandwich.util.io; + +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.Map; + +import com.sandwich.koan.ApplicationSettings; +import com.sandwich.koan.cmdline.CommandLineArgument; +import com.sandwich.koan.cmdline.CommandLineArgumentRunner; +import com.sandwich.koan.constant.ArgumentType; +import com.sandwich.koan.constant.KoanConstants; +import com.sandwich.koan.util.ApplicationUtils; +import com.sandwich.util.io.classloader.DynamicClassLoader; +import com.sandwich.util.io.directories.DirectoryManager; +import com.sandwich.util.io.filecompiler.CompilerConfig; +import com.sandwich.util.io.filecompiler.FileCompiler; + +public class KoanFileCompileAndRunListener implements FileListener { + + private Map args = Collections.emptyMap(); + private KoanSuiteCompilationListener listener = new KoanSuiteCompilationListener(); + + public KoanFileCompileAndRunListener(Map args) throws IOException{ + this.args = args; + } + + public void fileSaved(File file) { + String absolutePath = file.getAbsolutePath(); + if(CompilerConfig.isSourceFile(absolutePath)){ + ApplicationUtils.getPresenter().displayMessage(KoanConstants.EOL+"loading: "+absolutePath); + File[] jars = new File(DirectoryManager.getProjectLibraryDir()).listFiles(); + String[] classPath = new String[jars.length]; + for (int i = 0; i < jars.length; i++) { + String jarPath = jars[i].getAbsolutePath(); + String jarPathLowerCase = jarPath.toLowerCase(); + if(jarPathLowerCase.endsWith(".jar") || jarPathLowerCase.endsWith(".war")){ + classPath[i] = jarPath; + } + } + try { + FileCompiler.compile(file, + new File(DirectoryManager.getBinDir()), + listener, + ApplicationSettings.getFileCompilationTimeoutInMs(), + classPath); + DynamicClassLoader.remove(FileCompiler.sourceToClass( + DirectoryManager.getSourceDir(), DirectoryManager.getBinDir(), file).toURI().toURL()); + if(!listener.isLastCompilationAttemptFailure()){ + ApplicationUtils.getPresenter().clearMessages(); + new CommandLineArgumentRunner(args).run(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + public void newFile(File file) { + + } + + public void fileDeleted(File file) { + + } +} diff --git a/lib/src/main/java/com/sandwich/util/io/KoanSuiteCompilationListener.java b/lib/src/main/java/com/sandwich/util/io/KoanSuiteCompilationListener.java new file mode 100644 index 00000000..1144cdf6 --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/KoanSuiteCompilationListener.java @@ -0,0 +1,29 @@ +package com.sandwich.util.io; + +import java.io.File; + +import com.sandwich.util.io.filecompiler.CompilationListener; +import com.sandwich.util.io.filecompiler.FileCompilerAction; + +public class KoanSuiteCompilationListener implements CompilationListener { + + private boolean lastCompilationAttemptFailed = false; + private String lastMessageShown = null; + + public void compilationFailed(File src, String[] command, int exitCode, String errorMessage, Throwable x) { + if(lastMessageShown == null || !errorMessage.trim().equals(lastMessageShown.trim())){ + FileCompilerAction.LOGGING_HANDLER.compilationFailed(src, command, exitCode, errorMessage, x); + } + lastMessageShown = errorMessage; + lastCompilationAttemptFailed = true; + } + + public void compilationSucceeded(File src, String[] command, String stdIo, Throwable x) { + lastMessageShown = null; // reset last failed compilation message + lastCompilationAttemptFailed = false; + } + + public boolean isLastCompilationAttemptFailure(){ + return lastCompilationAttemptFailed; + } +} diff --git a/lib/src/main/java/com/sandwich/util/io/SourceAndDestinationFileAction.java b/lib/src/main/java/com/sandwich/util/io/SourceAndDestinationFileAction.java new file mode 100644 index 00000000..8b57ba7d --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/SourceAndDestinationFileAction.java @@ -0,0 +1,39 @@ +package com.sandwich.util.io; + +import java.io.File; +import java.io.IOException; + +public abstract class SourceAndDestinationFileAction extends ForEachFileAction { + + private final File source; + private final File destination; + + public SourceAndDestinationFileAction(String sourceDir, String destinationDir){ + this(new File(sourceDir), new File(destinationDir)); + } + + public SourceAndDestinationFileAction(File source, File destination){ + this.source = assertIsDirectory(source); + this.destination = assertIsDirectory(destination); + } + + public void operate() throws IOException { + super.operate(source); + } + + public void onFile(File file) throws IOException { + String subDir = file.getAbsolutePath().replace(source.getAbsolutePath(), ""); + File dest = new File(destination.getAbsolutePath() + File.separator + subDir); + sourceToDestination(file, dest); + } + + abstract public void sourceToDestination(File src, File dest) throws IOException; + + private File assertIsDirectory(File file) { + if(file == null || !file.isDirectory()){ + throw new IllegalArgumentException(file + " is not a directory."); + } + return file; + } + +} diff --git a/lib/src/main/java/com/sandwich/util/io/StreamUtils.java b/lib/src/main/java/com/sandwich/util/io/StreamUtils.java new file mode 100644 index 00000000..1038ce97 --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/StreamUtils.java @@ -0,0 +1,20 @@ +package com.sandwich.util.io; + +import java.io.InputStream; +import java.util.NoSuchElementException; +import java.util.Scanner; + +public class StreamUtils { + + public static String convertStreamToString(InputStream stream) { + Scanner scanner = new Scanner(stream); + try { + return scanner.useDelimiter("\\A").next(); + } catch (NoSuchElementException e) { + return ""; + } finally { + scanner.close(); + } + } + +} diff --git a/lib/src/main/java/com/sandwich/util/io/classloader/DynamicClassLoader.java b/lib/src/main/java/com/sandwich/util/io/classloader/DynamicClassLoader.java new file mode 100644 index 00000000..354c5c93 --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/classloader/DynamicClassLoader.java @@ -0,0 +1,154 @@ +package com.sandwich.util.io.classloader; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import com.sandwich.util.io.filecompiler.CompilationListener; +import com.sandwich.util.io.filecompiler.CompilerConfig; +import com.sandwich.util.io.filecompiler.FileCompiler; +import com.sandwich.util.io.filecompiler.FileCompilerAction; + +public abstract class DynamicClassLoader extends ClassLoader { + + private static Map> classesByLocation = new HashMap>(); + private static Map, URL> locationByClass = new HashMap, URL>(); + + private final long timeout; + private final String binDir, sourceDir; + private final String[] classPath; + + public DynamicClassLoader(String binDir, String sourceDir, String[] classPath){ + this(binDir, sourceDir, classPath, ClassLoader.getSystemClassLoader()); + } + + public DynamicClassLoader(String binDir, String sourceDir, String[] classPath, ClassLoader parent) { + this(binDir, sourceDir, classPath, parent, 5000l); + } + + public DynamicClassLoader(String binDir, String sourceDir, + String[] classPath, ClassLoader parent, + long timeout) { + super(parent); + this.binDir = binDir; + this.sourceDir = sourceDir; + this.classPath = classPath; + this.timeout = timeout; + } + + public abstract boolean isFileModifiedSinceLastPoll(String sourcePath, long lastModified); + + public abstract void updateFileSavedTime(File sourceFile); + + public static void remove(URL url){ + String urlToString = url.toString().replace(FileCompiler.CLASS_SUFFIX, ""); + for(String suffix : CompilerConfig.getSupportedFileSuffixes()){ + urlToString.replace(suffix, ""); + } + for(Entry> entry : classesByLocation.entrySet()){ + if(entry.getKey().toString().contains(urlToString)){ + locationByClass.remove(entry.getValue()); + entry.setValue(null); + } + } + } + + public static void remove(Class clas){ + for(Entry, URL> entry : locationByClass.entrySet()){ + if(entry.getKey().getName().contains(clas.getName())){ + classesByLocation.remove(entry.getValue()); + entry.setValue(null); + } + } + } + + public Class loadClass(String className){ + return loadClass(className, FileCompilerAction.LOGGING_HANDLER); + } + + public Class loadClass(String className, CompilationListener listener){ + String fileSeparator = System.getProperty("file.separator"); + String fileName = binDir + fileSeparator + + className.replace(".", fileSeparator) + + FileCompiler.CLASS_SUFFIX; + File classFile = new File(fileName); + try { + // file may have never been compiled, go ahead and compile it now + File sourceFile = FileCompiler.classToSource(binDir, sourceDir, classFile); + if(classFile.exists()){ + String absolutePath = classFile.getAbsolutePath(); + boolean isAnonymous = absolutePath.contains("$"); + if(isFileModifiedSinceLastPoll(sourceFile.getAbsolutePath(), sourceFile.lastModified())){ + if(!isAnonymous){ + compile(fileName, sourceFile, timeout, listener); + } + } + return loadClass(classFile.toURI().toURL(), className); + } + try{ + return super.loadClass(className); + }catch(ClassNotFoundException x){ + compile(fileName, sourceFile, timeout, listener); + classFile = new File(fileName); + return loadClass(classFile.toURI().toURL(), className); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void compile(String fileName, File sourceFile, long timeout, CompilationListener listener) + throws IOException { + FileCompiler.compile(sourceFile, new File(binDir), listener, timeout, classPath); + updateFileSavedTime(sourceFile); + } + + public Class loadClass(URL url, String className){ + Class clazz = classesByLocation.get(url); + if(clazz != null){ + return clazz; + } + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + try { + URLConnection connection = url.openConnection(); + InputStream input = connection.getInputStream(); + int data = input.read(); + while(data != -1){ + buffer.write(data); + data = input.read(); + } + input.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + byte[] classData = buffer.toByteArray(); + clazz = defineClass(className, classData, 0, classData.length); + classesByLocation.put(url, clazz); + locationByClass.put(clazz, url); + return clazz; + } + + public long getTimeout() { + return timeout; + } + + public String getBinDir() { + return binDir; + } + + public String getSourceDir() { + return sourceDir; + } + + public String[] getClassPath() { + return classPath; + } + +} diff --git a/koans-lib/src/com/sandwich/util/io/directories/DirectoryManager.java b/lib/src/main/java/com/sandwich/util/io/directories/DirectoryManager.java similarity index 64% rename from koans-lib/src/com/sandwich/util/io/directories/DirectoryManager.java rename to lib/src/main/java/com/sandwich/util/io/directories/DirectoryManager.java index 5403b986..e3bac399 100755 --- a/koans-lib/src/com/sandwich/util/io/directories/DirectoryManager.java +++ b/lib/src/main/java/com/sandwich/util/io/directories/DirectoryManager.java @@ -7,7 +7,7 @@ public abstract class DirectoryManager { private DirectoryManager(){} - private static DirectorySet production = new Production(); + private static DirectorySet production = new ProductionDirectories(); private static DirectorySet instance = production; public static final String FILESYSTEM_SEPARATOR = System.getProperty("file.separator"); @@ -29,6 +29,13 @@ private static String constructMainDir(DirectorySet directories){ directories.getProjectDir()); } + public static String getConfigDir(){ + return injectFileSystemSeparators( instance.getBaseDir(), + production.getProjectDir(), + instance.getAppDir(), + instance.getConfigDir()); + } + public static String getSourceDir(){ return constructProjectDir(instance, instance.getSourceDir()); } @@ -38,12 +45,16 @@ public static String getProdSourceDir(){ } public static String getBinDir(){ - return constructProjectDir(instance, instance.getBinaryDir()); + return injectFileSystemSeparators( instance.getBaseDir(), + production.getProjectDir(), + instance.getAppDir(), + instance.getBinaryDir()); } public static String getDataDir() { return injectFileSystemSeparators( instance.getBaseDir(), - production.getProjectDir(), + production.getProjectDir(), + instance.getAppDir(), instance.getDataDir()); } @@ -55,13 +66,25 @@ private static String constructProjectDir(DirectorySet directories, String child public static String getProjectLibraryDir(){ return injectFileSystemSeparators( instance.getBaseDir(), - production.getProjectDir(), + production.getProjectDir(), + instance.getAppDir(), instance.getLibrariesDir()); } + public static String getProjectI18nDir(){ + return injectFileSystemSeparators( instance.getBaseDir(), + production.getProjectDir(), + instance.getAppDir(), + instance.getConfigDir(), + instance.getI18nDir()); + } + public static String getProjectDataSourceDir() { - return injectFileSystemSeparators( getDataDir(), - instance.getSourceDir()); + return injectFileSystemSeparators(getDataDir(), instance.getSourceDir()); + } + + public static String getDataFile() { + return injectFileSystemSeparators(getConfigDir(), "file_hashes.dat"); } /** @@ -86,11 +109,11 @@ public static String injectFileSystemSeparators(String...folders){ builder.append(folder); } String constructedFolder = builder.toString(); - if(System.getProperty("os.name").toLowerCase().contains("win") && constructedFolder.startsWith(FILESYSTEM_SEPARATOR)){ - constructedFolder = constructedFolder.substring(1); - } - if(ApplicationUtils.isWindows() && constructedFolder.contains("%20")){ - constructedFolder.replace("%20", " "); + if(ApplicationUtils.isWindows()){ + if(constructedFolder.startsWith(FILESYSTEM_SEPARATOR)){ + constructedFolder = constructedFolder.substring(1); + } + constructedFolder = constructedFolder.replace("%20", " "); } return constructedFolder; } diff --git a/lib/src/main/java/com/sandwich/util/io/directories/DirectorySet.java b/lib/src/main/java/com/sandwich/util/io/directories/DirectorySet.java new file mode 100755 index 00000000..cd7d2780 --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/directories/DirectorySet.java @@ -0,0 +1,59 @@ +package com.sandwich.util.io.directories; + +import java.io.File; + +abstract public class DirectorySet { + + private static final String BIN_DIR = "bin"; + private static final String APP_DIR = "app"; + private static final String LIB_DIR = "lib"; + private static final String DATA_DIR = "data"; + private static final String I18N_DIR = "i18n"; + private static final String CONFIG_DIR = "config"; + private static final String BASE_DIR = createBaseDir(); + + abstract String getSourceDir(); + abstract String getProjectDir(); + + public String getBaseDir(){ + return BASE_DIR; + } + + public String getBinaryDir(){ + return BIN_DIR; + } + + public String getLibrariesDir(){ + return LIB_DIR; + } + + public String getI18nDir(){ + return I18N_DIR; + } + + public String getAppDir(){ + return APP_DIR; + } + + public String getConfigDir() { + return CONFIG_DIR; + } + + public String getDataDir(){ + return DATA_DIR; + } + + private static String createBaseDir() { + String baseDir = System.getProperty("application.basedir"); + if(baseDir != null){ + if(baseDir.startsWith("\"")){ + baseDir = baseDir.substring(1); + } + if(baseDir.endsWith("\"")){ + baseDir = baseDir.substring(0, baseDir.length() - 1); + } + return new File(baseDir).getParentFile().getAbsolutePath(); + } + return new File("").getAbsoluteFile().getParentFile().getAbsolutePath(); + } +} diff --git a/koans-lib/src/com/sandwich/util/io/directories/Production.java b/lib/src/main/java/com/sandwich/util/io/directories/ProductionDirectories.java similarity index 72% rename from koans-lib/src/com/sandwich/util/io/directories/Production.java rename to lib/src/main/java/com/sandwich/util/io/directories/ProductionDirectories.java index 88330386..6f2ff90d 100644 --- a/koans-lib/src/com/sandwich/util/io/directories/Production.java +++ b/lib/src/main/java/com/sandwich/util/io/directories/ProductionDirectories.java @@ -1,7 +1,7 @@ package com.sandwich.util.io.directories; -public class Production extends DirectorySet { +public class ProductionDirectories extends DirectorySet { public String getProjectDir() { return "koans"; diff --git a/lib/src/main/java/com/sandwich/util/io/filecompiler/CompilationFailureLogger.java b/lib/src/main/java/com/sandwich/util/io/filecompiler/CompilationFailureLogger.java new file mode 100644 index 00000000..92559615 --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/filecompiler/CompilationFailureLogger.java @@ -0,0 +1,33 @@ +package com.sandwich.util.io.filecompiler; + +import java.io.File; + +import com.sandwich.util.io.ui.DefaultErrorPresenter; +import com.sandwich.util.io.ui.ErrorPresenter; + +public class CompilationFailureLogger implements CompilationListener { + + private ErrorPresenter presenter; + + public CompilationFailureLogger(){ + this(new DefaultErrorPresenter()); + } + + public CompilationFailureLogger(ErrorPresenter presenter){ + this.presenter = presenter; + } + + public void compilationFailed(File src, String[] command, int exitCode, String errorMessage, Throwable x) { + String lineSeparator = System.getProperty("line.separator"); + presenter.displayError( + lineSeparator + + "*****************************************************************" + lineSeparator + + "Compile Output:" + lineSeparator + + errorMessage.replace(lineSeparator, lineSeparator + " ") + lineSeparator + + "Compiling \"" + src.getAbsolutePath() + "\" failed." + lineSeparator + + "The exit status was: " + exitCode + lineSeparator + + "*****************************************************************" + lineSeparator + + lineSeparator); + } + public void compilationSucceeded(File src, String[] command, String stdIo, Throwable x) { } +} \ No newline at end of file diff --git a/lib/src/main/java/com/sandwich/util/io/filecompiler/CompilationListener.java b/lib/src/main/java/com/sandwich/util/io/filecompiler/CompilationListener.java new file mode 100644 index 00000000..687f063f --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/filecompiler/CompilationListener.java @@ -0,0 +1,8 @@ +package com.sandwich.util.io.filecompiler; + +import java.io.File; + +public interface CompilationListener { + void compilationFailed(File src, String[] command, int exitCode, String errorMessage, Throwable x); + void compilationSucceeded(File src, String[] command, String stdIo, Throwable x); +} diff --git a/lib/src/main/java/com/sandwich/util/io/filecompiler/CompilerConfig.java b/lib/src/main/java/com/sandwich/util/io/filecompiler/CompilerConfig.java new file mode 100644 index 00000000..4713069b --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/filecompiler/CompilerConfig.java @@ -0,0 +1,74 @@ +package com.sandwich.util.io.filecompiler; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Enumeration; +import java.util.List; +import java.util.ResourceBundle; + +public class CompilerConfig { + + private static final ResourceBundle commandBySuffixRB = ResourceBundle.getBundle("compilationcommands"); + + public static boolean isSourceFile(String fileName) { + // TODO when supporting java > 5, change to return resourcebundle.containsKey(... + Enumeration keys = commandBySuffixRB.getKeys(); + String suffix = getSuffix(fileName).toLowerCase(); + while(keys.hasMoreElements()){ + String key = keys.nextElement(); + if(key.equals(suffix)){ + return true; + } + } + return false; + } + + public static String[] getCompilationCommand(File src, String destinationPath, String classPath) { + String absolutePath = src.getAbsolutePath(); + String command = commandBySuffixRB.getString(getSuffix(absolutePath)); + if(command == null){ + throw new RuntimeException("Do not know how to compile " + absolutePath); + } + List splitCommand = Arrays.asList(command.split(" ")); + List commandSegments = new ArrayList(); + for(String segment : splitCommand){ + String lowerCaseSegment = segment.toLowerCase(); + if("${bindir}".equals(lowerCaseSegment)){ + commandSegments.add(destinationPath); + }else if("${classpath}".equals(lowerCaseSegment)){ + commandSegments.add(classPath); + }else if("${filename}".equals(lowerCaseSegment)){ + commandSegments.add(src.getAbsolutePath()); + }else{ + commandSegments.add(segment); + } + } + return commandSegments.toArray(new String[commandSegments.size()]); + } + + public static Collection getSupportedFileSuffixes() { + Enumeration keysEnumeration = commandBySuffixRB.getKeys(); + Collection keys = new ArrayList(); + while(keysEnumeration.hasMoreElements()){ + keys.add(keysEnumeration.nextElement()); + } + return keys; + } + + public static String getSuffix(String fileName) { + if(fileName != null){ + int periodIndex = fileName.lastIndexOf('.'); + if(periodIndex > -1){ + return fileName.substring(periodIndex).toLowerCase(); + } + } + return ""; + } + + public static boolean isSuffixSupported(String suffix) { + return getSupportedFileSuffixes().contains(suffix); + } + +} diff --git a/lib/src/main/java/com/sandwich/util/io/filecompiler/FileCompiler.java b/lib/src/main/java/com/sandwich/util/io/filecompiler/FileCompiler.java new file mode 100644 index 00000000..599d69bf --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/filecompiler/FileCompiler.java @@ -0,0 +1,118 @@ +package com.sandwich.util.io.filecompiler; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import com.sandwich.util.io.FileUtils; +import com.sandwich.util.io.ui.DefaultErrorPresenter; +import com.sandwich.util.io.ui.ErrorPresenter; + +public class FileCompiler { + + private static final Map sourceFileToClassFile = new HashMap(); + private static final Map classFileToSourceFile = new HashMap(); + + private static final String DOLLAR_SIGN = "$"; + public static final String CLASS_SUFFIX = ".class"; + + public static void compile(String src, String bin) throws IOException { + compile(new DefaultErrorPresenter(), new File(src), new File(bin)); + } + + public static void compile(ErrorPresenter errorPresenter, String src, String bin) throws IOException { + compile(errorPresenter, new File(src), new File(bin)); + } + + public static void compile(ErrorPresenter errorPresenter, File src, File bin, + final String...classpath) throws IOException { + compile(src, bin, new CompilationFailureLogger(errorPresenter), classpath); + } + + public static void compile(File src, final File bin, + CompilationListener listener, String[] classpath) throws IOException { + compile(src, bin, listener, 5000l, classpath); + } + + public static void compile(File src, final File bin, final CompilationListener listener, + final long timeout, final String[] classpath) throws IOException { + if(!bin.exists()){ + if(!bin.mkdir()){ + System.err.println("Was unable to create: "+bin); + System.exit(-231); + } + } + new FileCompilerAction(bin, listener, timeout, classpath).operate(src);; + String srcPath = src.getAbsolutePath(); + String classPath = srcPath; + for(String suffix : CompilerConfig.getSupportedFileSuffixes()){ + if(classPath.endsWith(suffix)){ + classPath = classPath.replace(suffix, CLASS_SUFFIX); + } + } + sourceFileToClassFile.put(srcPath, classPath); + classFileToSourceFile.put(classPath, srcPath); + } + + public static String getContentsOfJavaFile(String sourceDir, String className) { + return FileUtils.readFileAsString(getSourceFileFromClass(sourceDir, className)); + } + + public static File getSourceFileFromClass(String sourceDir, String className) { + if(className.contains(DOLLAR_SIGN)){ + className = className.substring(0, className.indexOf(DOLLAR_SIGN)); + } + + File possibleSourceFile = new File(sourceDir); + File sourceFile = null; + for(String folder : className.split("\\.")){ + possibleSourceFile = new File(possibleSourceFile, folder); + } + + for(String suffix : CompilerConfig.getSupportedFileSuffixes()){ + File file = new File(possibleSourceFile.getAbsolutePath() + suffix); + if(file.exists()){ + sourceFile = file; + break; + } + } + if (sourceFile == null || !sourceFile.exists()) { + throw new IllegalArgumentException(new FileNotFoundException( + sourceFile == null ? null : sourceFile.getAbsolutePath() + " does not exist")); + } + return sourceFile; + } + + public static File sourceToClass(String sourceDir, String binDir, File file) { + //C:\Users\sandwich\Development\koans\koans\app\bin\beginner\AboutKoans.class + String classPath = file.getAbsolutePath().replace(sourceDir, binDir); + for(String suffix : CompilerConfig.getSupportedFileSuffixes()){ + classPath = classPath.replace(suffix, ""); + } + return new File(classPath + CLASS_SUFFIX); + } + + public static File classToSource(String binDir, String sourceDir, File file) { + return classToSource(binDir, sourceDir, file.getAbsolutePath()); + } + + public static File classToClassFile(Class clazz) { + String className = clazz.getName(); + String path = className.replace(".", System.getProperty("file.separator")) + CLASS_SUFFIX; + return new File(ClassLoader.getSystemResource(path).toString().substring(5)); + } + + public static File classToSource(String binDir, String sourceDir, String absolutePath) { + String sourcePath = absolutePath.replace(binDir, sourceDir).replace(CLASS_SUFFIX, ""); + for(String suffix : CompilerConfig.getSupportedFileSuffixes()){ + File file = new File(sourcePath + suffix); + if(file.exists()){ + return file; + } + } + return new File(sourcePath); + } + +} diff --git a/lib/src/main/java/com/sandwich/util/io/filecompiler/FileCompilerAction.java b/lib/src/main/java/com/sandwich/util/io/filecompiler/FileCompilerAction.java new file mode 100644 index 00000000..979693d6 --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/filecompiler/FileCompilerAction.java @@ -0,0 +1,109 @@ +package com.sandwich.util.io.filecompiler; + +import java.io.File; +import java.io.IOException; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import com.sandwich.util.ExceptionUtils; +import com.sandwich.util.io.ForEachFileAction; +import com.sandwich.util.io.StreamUtils; + +public class FileCompilerAction extends ForEachFileAction { + + private final String destinationPath; + private final String[] classPaths; + private CompilationListener compilationListener; + private final long timeout; + public static final CompilationListener LOGGING_HANDLER = new CompilationFailureLogger(); + + public FileCompilerAction(File destination, CompilationListener errorHandler, String...classPaths){ + this(destination, errorHandler, 1000, classPaths); + } + + public FileCompilerAction(File destination, + CompilationListener errorHandler, long timeout, String[] classPaths) { + if(destination == null){ + throw new IllegalArgumentException("the destination path is required"); + } + this.destinationPath = destination.getAbsolutePath(); + this.classPaths = classPaths; + this.compilationListener = errorHandler == null ? LOGGING_HANDLER : errorHandler; + this.timeout = timeout; + } + + public void onFile(File src) throws IOException { + String fileName = src.getName(); + if (CompilerConfig.isSourceFile(fileName)) { + String[] command = CompilerConfig.getCompilationCommand(src, destinationPath, getClasspath()); + try{ + Process p = Runtime.getRuntime().exec(command); + try { + executeWithTimeout(src, command, p, timeout); + } catch (IllegalThreadStateException x) { + compilationListener.compilationFailed(src, command, + 255, "Compilation took longer than "+timeout+" ms.\n" + + "It is likely that the compiler has locked up compiling this file.\n" + + "Please revert your last change and try something different.", x); + } catch (Exception x) { + x.printStackTrace(); + compilationListener.compilationFailed(src, command, + p.exitValue(), StreamUtils.convertStreamToString(p.getErrorStream()), x); + } + }catch(IOException x){ + if(x.getMessage().contains("Cannot run program")){ + String commandString = ""; + for(String segment : command){ + commandString += segment + " "; + } + commandString = commandString.trim(); + compilationListener.compilationFailed(src, command, 2, + "Cannot execute:" + System.getProperty("line.separator") + + commandString + System.getProperty("line.separator") + + "Please check that the appropriate compiler (" + command[0] + ") is installed, is executable and is listed in your PATH environment variable value.", x); + } + } + } + } + + private void executeWithTimeout(final File src, final String[] command, + final Process p, final long timeout) throws InterruptedException { + ExecutorService executor = Executors.newCachedThreadPool(); + Callable task = new Callable() { + public Object call() { + try { + if (p.waitFor() != 0) { + compilationListener.compilationFailed(src, command, p + .exitValue(), StreamUtils.convertStreamToString(p.getErrorStream()), null); + } else { + compilationListener.compilationSucceeded(src, command, + StreamUtils.convertStreamToString(p.getInputStream()), null); + } + } catch (InterruptedException e) { + compilationListener.compilationFailed(src, command, p.exitValue(), + ExceptionUtils.convertToPopulatedStackTraceString(e), null); + } + return null; + } + }; + Future future = executor.submit(task); + try { + future.get(timeout, TimeUnit.MILLISECONDS); + } catch (Exception e) { + compilationListener.compilationFailed(src, command, p.exitValue(), + ExceptionUtils.convertToPopulatedStackTraceString(e), null); + } + } + + private String getClasspath() { + String classPath = ""; + for(String jar : classPaths) { + classPath += jar + File.pathSeparatorChar; + } + return classPath; + } + +} diff --git a/lib/src/main/java/com/sandwich/util/io/ui/DefaultErrorPresenter.java b/lib/src/main/java/com/sandwich/util/io/ui/DefaultErrorPresenter.java new file mode 100644 index 00000000..9c1ee6aa --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/ui/DefaultErrorPresenter.java @@ -0,0 +1,9 @@ +package com.sandwich.util.io.ui; + +public class DefaultErrorPresenter implements ErrorPresenter { + + public void displayError(String error) { + System.err.println(error); + } + +} diff --git a/lib/src/main/java/com/sandwich/util/io/ui/ErrorPresenter.java b/lib/src/main/java/com/sandwich/util/io/ui/ErrorPresenter.java new file mode 100644 index 00000000..19fedc16 --- /dev/null +++ b/lib/src/main/java/com/sandwich/util/io/ui/ErrorPresenter.java @@ -0,0 +1,7 @@ +package com.sandwich.util.io.ui; + +public interface ErrorPresenter { + + void displayError(String error); + +} diff --git a/lib/src/test/java/com/sandwich/koan/AnonymousInnerClassTest.java b/lib/src/test/java/com/sandwich/koan/AnonymousInnerClassTest.java new file mode 100644 index 00000000..a1344021 --- /dev/null +++ b/lib/src/test/java/com/sandwich/koan/AnonymousInnerClassTest.java @@ -0,0 +1,24 @@ +package com.sandwich.koan; + +import java.util.Arrays; + +import org.junit.Test; + +import com.sandwich.koan.cmdline.CommandLineArgumentRunner; +import com.sandwich.koan.path.CommandLineTestCase; + +public class AnonymousInnerClassTest extends CommandLineTestCase{ + + @Test + public void testAnonymousInnerClassIsCoolToUseAsKoan() throws Exception { + final String definitelyAUniqueString = "meh1294120240912049"; + stubAllKoans(Arrays.asList(new Object(){ + @Koan public void printMsg(){ + System.out.println(definitelyAUniqueString); + } + })); + new CommandLineArgumentRunner().run(); + assertSystemOutContains(definitelyAUniqueString); + } + +} diff --git a/koans-tests/data/src/com/sandwich/koan/KoansResultTest.java b/lib/src/test/java/com/sandwich/koan/KoansResultTest.java old mode 100644 new mode 100755 similarity index 90% rename from koans-tests/data/src/com/sandwich/koan/KoansResultTest.java rename to lib/src/test/java/com/sandwich/koan/KoansResultTest.java index b2011ebd..8d4dadfb --- a/koans-tests/data/src/com/sandwich/koan/KoansResultTest.java +++ b/lib/src/test/java/com/sandwich/koan/KoansResultTest.java @@ -6,13 +6,14 @@ import org.junit.Test; +import com.sandwich.koan.path.CommandLineTestCase; import com.sandwich.koan.result.KoanMethodResult; import com.sandwich.koan.result.KoanSuiteResult; import com.sandwich.koan.result.KoanSuiteResult.KoanResultBuilder; import com.sandwich.koan.suite.OneFailingKoan; -public class KoansResultTest { - +public class KoansResultTest extends CommandLineTestCase { + @Test public void testToString() throws Exception { KoanResultBuilder builder = new KoanResultBuilder() diff --git a/lib/src/test/java/com/sandwich/koan/LocaleSwitchingTestCase.java b/lib/src/test/java/com/sandwich/koan/LocaleSwitchingTestCase.java new file mode 100644 index 00000000..8b63385f --- /dev/null +++ b/lib/src/test/java/com/sandwich/koan/LocaleSwitchingTestCase.java @@ -0,0 +1,60 @@ +package com.sandwich.koan; + +import static org.junit.Assert.fail; + +import java.util.Locale; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +import org.junit.After; +import org.junit.Before; + +public class LocaleSwitchingTestCase { + + private Locale defaultLocale; + + @Before + public void setUp(){ + this.defaultLocale = Locale.getDefault(); + } + + @After + public void tearDown(){ + Locale.setDefault(defaultLocale); + } + + protected void assertLogged(String loggerName, final LoggerExpectation loggerExpectation) { + Logger logger = Logger.getLogger(loggerName); + final boolean[] called = new boolean[]{false}; + final Handler handler = new Handler(){ + @Override public void close() throws SecurityException {} + @Override public void flush() {} + @Override public void publish(final LogRecord record) { + loggerExpectation.logCalled(record); + called[0] = true; + } + }; + logger.addHandler(handler); + try{ + loggerExpectation.invokeImplementation(); + if(loggerExpectation.isLogCallRequired() && !called[0]){ + fail("The logger was never called, it should have been."); + } + }finally{ + logger.removeHandler(handler); + } + } + + protected class LoggerExpectation { + protected void logCalled(LogRecord record){ + fail("Unexpected logging event: "+record); + } + protected boolean isLogCallRequired(){ + return true; + } + protected void invokeImplementation(){ + fail("No invocation definition defined."); + } + } +} diff --git a/koans-tests/data/src/com/sandwich/koan/TestUtils.java b/lib/src/test/java/com/sandwich/koan/TestUtils.java old mode 100644 new mode 100755 similarity index 100% rename from koans-tests/data/src/com/sandwich/koan/TestUtils.java rename to lib/src/test/java/com/sandwich/koan/TestUtils.java diff --git a/koans-tests/data/src/com/sandwich/koan/TestUtilsTest.java b/lib/src/test/java/com/sandwich/koan/TestUtilsTest.java old mode 100644 new mode 100755 similarity index 88% rename from koans-tests/data/src/com/sandwich/koan/TestUtilsTest.java rename to lib/src/test/java/com/sandwich/koan/TestUtilsTest.java index a5a66b8c..19ccd88a --- a/koans-tests/data/src/com/sandwich/koan/TestUtilsTest.java +++ b/lib/src/test/java/com/sandwich/koan/TestUtilsTest.java @@ -1,20 +1,17 @@ package com.sandwich.koan; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; - import org.easymock.EasyMock; +import org.junit.Ignore; import org.junit.Test; import com.sandwich.koan.TestUtils.ArgRunner; import com.sandwich.koan.TestUtils.TwoObjectAssertion; +import com.sandwich.koan.path.CommandLineTestCase; -public class TestUtilsTest { +public class TestUtilsTest extends CommandLineTestCase { @Test public void testEqualsContractEnforcement_integerIdentity_happyPath() throws Exception { @@ -150,38 +147,30 @@ public int hashCode(){ } } - @Test(expected=AssertionError.class, timeout=1000) + @Test(expected=AssertionError.class, timeout=2000) public void testEqualsConcurrency_concurrentAccessFails() throws Exception { - final PrintStream temp = System.err; - try { - ByteArrayOutputStream sysErr = new ByteArrayOutputStream(); - System.setErr(new PrintStream(sysErr)); - TestUtils.doSimultaneouslyAndRepetitively(new TwoObjectAssertion() { - public void assertOn(String msg, Object o0, Object o1) { - assertEquals(msg, o0, o1); - } - }, IllegalMonitorStateException.class, - new Runnable() { - public void run() { - waste(10); - } - }, new Runnable() { - public void run() { - waste(11); - } - }, new Runnable() { - public void run() { - waste(3); - } - }); - String errors = sysErr.toString(); - assertTrue(errors.contains("Thread-1\" java.lang.IllegalMonitorStateException")); - assertTrue(errors.contains("Thread-2\" java.lang.IllegalMonitorStateException")); - assertTrue(errors.contains("Thread-3\" java.lang.IllegalMonitorStateException")); - assertFalse(errors.contains("Thread-4")); - } finally { - System.setErr(new PrintStream(temp)); - } + TestUtils.doSimultaneouslyAndRepetitively(new TwoObjectAssertion() { + public void assertOn(String msg, Object o0, Object o1) { + assertEquals(msg, o0, o1); + } + }, IllegalMonitorStateException.class, + new Runnable() { + public void run() { + waste(10); + } + }, new Runnable() { + public void run() { + waste(11); + } + }, new Runnable() { + public void run() { + waste(3); + } + }); + assertSystemErrContains("Thread-1\" java.lang.IllegalMonitorStateException"); + assertSystemErrContains("Thread-2\" java.lang.IllegalMonitorStateException"); + assertSystemErrContains("Thread-3\" java.lang.IllegalMonitorStateException"); + assertSystemErrContains("Thread-4"); } @Test(expected=java.lang.AssertionError.class, timeout=500) @@ -209,7 +198,7 @@ public void run() { }); } - @Test + @Test @Ignore // disk/os access causing random failures at this low a deviation in timing public void testEqualsConcurrency() throws Exception { TestUtils.doSimultaneouslyAndRepetitively(new TwoObjectAssertion() { public void assertOn(String msg, Object o0, Object o1) { @@ -230,7 +219,7 @@ public void run() { }); } - @Test + @Test @Ignore // disk/os access causing random failures at this low a deviation in timing public void testEqualsConcurrency_II() throws Exception { TestUtils.doSimultaneouslyAndRepetitively(new TwoObjectAssertion() { public void assertOn(String msg, Object o0, Object o1) { @@ -389,7 +378,7 @@ public void testForEachLine_emptyString() throws Exception { } @Test - public void testForEachLine_nothingThreeNewLinesSeperatedBy1Space() throws Exception { + public void testForEachLine_nothingThreeNewLinesSeparatedBy1Space() throws Exception { @SuppressWarnings("unchecked") ArgRunner runner = EasyMock.createStrictMock(ArgRunner.class); @@ -412,7 +401,7 @@ public void testForEachLine_nothingThreeNewLinesSeperatedBy1Space() throws Excep } @Test - public void testForEachLine_nothingThreeNewLinesSeperatedBy1SpaceThen2Spaces() throws Exception { + public void testForEachLine_nothingThreeNewLinesSeparatedBy1SpaceThen2Spaces() throws Exception { @SuppressWarnings("unchecked") ArgRunner runner = EasyMock.createStrictMock(ArgRunner.class); diff --git a/koans-tests/test/com/sandwich/koan/cmdline/CommandLineArgumentBuilderTest.java b/lib/src/test/java/com/sandwich/koan/cmdline/CommandLineArgumentBuilderTest.java similarity index 66% rename from koans-tests/test/com/sandwich/koan/cmdline/CommandLineArgumentBuilderTest.java rename to lib/src/test/java/com/sandwich/koan/cmdline/CommandLineArgumentBuilderTest.java index 5098f8da..49de606f 100755 --- a/koans-tests/test/com/sandwich/koan/cmdline/CommandLineArgumentBuilderTest.java +++ b/lib/src/test/java/com/sandwich/koan/cmdline/CommandLineArgumentBuilderTest.java @@ -2,7 +2,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import java.util.Map; import java.util.Map.Entry; @@ -20,19 +19,6 @@ public void testNoArguments() throws Exception { assertEquals(ArgumentType.RUN_KOANS, new CommandLineArgumentBuilder().entrySet().iterator().next().getKey()); } - @Test - public void testUnanticipatedArgument_yieldsMethodArg_constructedImplicitly() throws Exception { - String value = - "if string isn't a known command line arg (ArgumentType) or class - assume its a method"; - Entry anticipatedResult = - new SimpleEntry(ArgumentType.METHOD_ARG, - new CommandLineArgument(ArgumentType.METHOD_ARG, value)); - Map commandLineArgs = - new CommandLineArgumentBuilder(value); - assertEquals(1, commandLineArgs.size()); - assertEquals(anticipatedResult, commandLineArgs.entrySet().iterator().next()); - } - @Test public void testMethodArg_constructedExplicitly() throws Exception { String value = "someMethodName"; @@ -65,15 +51,4 @@ public void testClassArg_constructedExplicitly() throws Exception { assertTrue(commandLineArgs.containsKey(ArgumentType.RUN_KOANS)); } - @Test - public void testMultipleMethodOrUnknownArgs_throwsException() throws Exception { - String value = "someMethodName"; - try{ - new CommandLineArgumentBuilder(ArgumentType.METHOD_ARG.args().iterator().next(), value, value); - fail(); - }catch(IllegalArgumentException x){ - - } - } - } diff --git a/koans-tests/data/src/com/sandwich/koan/constant/ArgumentTypeTest.java b/lib/src/test/java/com/sandwich/koan/constant/ArgumentTypeTest.java old mode 100644 new mode 100755 similarity index 96% rename from koans-tests/data/src/com/sandwich/koan/constant/ArgumentTypeTest.java rename to lib/src/test/java/com/sandwich/koan/constant/ArgumentTypeTest.java index 48ec575a..f667955c --- a/koans-tests/data/src/com/sandwich/koan/constant/ArgumentTypeTest.java +++ b/lib/src/test/java/com/sandwich/koan/constant/ArgumentTypeTest.java @@ -22,7 +22,7 @@ public void testClassPrecedesMethod() throws Exception { assertEquals(1, classVsMethod.indexOf(ArgumentType.CLASS_ARG)); Collections.sort(classVsMethod); // now - because of comparable impl was applied, class precedes method - this is necessary - // @ see KaonSuiteRunner.run() + // @ see KoanSuiteRunner.run() assertEquals(1, classVsMethod.indexOf(ArgumentType.METHOD_ARG)); assertEquals(0, classVsMethod.indexOf(ArgumentType.CLASS_ARG)); } diff --git a/lib/src/test/java/com/sandwich/koan/path/CommandLineTestCase.java b/lib/src/test/java/com/sandwich/koan/path/CommandLineTestCase.java new file mode 100755 index 00000000..0f80b9e0 --- /dev/null +++ b/lib/src/test/java/com/sandwich/koan/path/CommandLineTestCase.java @@ -0,0 +1,254 @@ +package com.sandwich.koan.path; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.PrintStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; + +import com.sandwich.koan.Koan; +import com.sandwich.koan.KoanClassLoader; +import com.sandwich.koan.KoanIncompleteException; +import com.sandwich.koan.TestUtils; +import com.sandwich.koan.TestUtils.ArgRunner; +import com.sandwich.koan.constant.ArgumentType; +import com.sandwich.koan.path.PathToEnlightenment.Path; +import com.sandwich.koan.path.xmltransformation.FakeXmlToPathTransformer; +import com.sandwich.koan.path.xmltransformation.KoanElementAttributes; +import com.sandwich.koan.runner.RunKoans; +import com.sandwich.koan.ui.ConsolePresenter; +import com.sandwich.koan.ui.SuitePresenter; +import com.sandwich.koan.util.ApplicationUtils; +import com.sandwich.koan.util.ApplicationUtils.SuitePresenterFactory; +import com.sandwich.util.io.KoanSuiteCompilationListener; +import com.sandwich.util.io.classloader.DynamicClassLoader; +import com.sandwich.util.io.directories.DirectoryManager; +import com.sandwich.util.io.directories.ProductionDirectories; +import com.sandwich.util.io.directories.UnitTestDirectories; + +public abstract class CommandLineTestCase { + + private PrintStream out; + private PrintStream err; + private ByteArrayOutputStream outBytes; + private ByteArrayOutputStream errBytes; + + @BeforeClass + public static void deleteFileHashesAfterJVMExit(){ + new File(DirectoryManager.getDataFile()).deleteOnExit(); + } + + @Before + public void setUp() throws Exception { + DirectoryManager.setDirectorySet(new UnitTestDirectories()); + resetClassLoader(); + out = System.out; + err = System.err; + TestUtils.setValue("behavior", new RunKoans(), ArgumentType.RUN_KOANS); + PathToEnlightenment.xmlToPathTransformer = new FakeXmlToPathTransformer(); + PathToEnlightenment.theWay = PathToEnlightenment.createPath(); + clearSystemOutputs(); + stubPresenter(new ConsolePresenter()); + } + + @After + public void tearDown() throws Exception { + DirectoryManager.setDirectorySet(new ProductionDirectories()); + setRealPath(); + if(out != null){ + System.setOut(out); + } + if(err != null){ + System.setErr(err); + } + resetClassLoader(); + } + + protected void setRealPath(){ + PathToEnlightenment.xmlToPathTransformer = null; + PathToEnlightenment.theWay = PathToEnlightenment.createPath(); + } + + protected void stubPresenter(final SuitePresenter presenter) + throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { + Field f = ApplicationUtils.class.getDeclaredField("suitePresenterFactory"); + boolean isAccessible = f.isAccessible(); + try { + f.setAccessible(true); + f.set(ApplicationUtils.class, new SuitePresenterFactory(){ + @Override public SuitePresenter create(){ + return presenter; + } + }); + } finally { + f.setAccessible(isAccessible); + } + } + + protected Path stubAllKoans(String packageName, List path){ + Path oldKoans = PathToEnlightenment.getPathToEnlightenment(); + Map> tempSuitesAndMethods = + new LinkedHashMap>(); + DynamicClassLoader loader = KoanClassLoader.getInstance(); + for(String suite : path){ + Map methodsByName = new LinkedHashMap(); + KoanSuiteCompilationListener listener = new KoanSuiteCompilationListener(); + for(Method m : loader.loadClass(suite, listener).getMethods()){ + if(m.getAnnotation(Koan.class) != null){ + methodsByName.put(m.getName(), new KoanElementAttributes(m.getName(), "", m.getDeclaringClass().getName())); + } + } + tempSuitesAndMethods.put(suite, methodsByName); + } + Map>> stubbedPath = + new LinkedHashMap>>(); + stubbedPath.put(packageName, tempSuitesAndMethods); + PathToEnlightenment.theWay = new Path(null,stubbedPath); + return oldKoans; + } + + public Path stubAllKoans(List path){ + List classes = new ArrayList(); + for(Object o : path){ + String className; + if(o instanceof Class){ + className = ((Class)o).getName(); + }else{ + className = o.getClass().getName(); + } + classes.add(className); + } + return stubAllKoans("Test", classes); + } + + public void clearSystemOutputs(){ + outBytes = new ByteArrayOutputStream(); + errBytes = new ByteArrayOutputStream(); + System.setOut(new PrintStream(outBytes)); + System.setErr(new PrintStream(errBytes)); + } + + public void assertSystemOutEquals(String expectation){ + assertEquals(expectation, outBytes); + } + + public void assertSystemErrEquals(String expectation){ + assertEquals(expectation, errBytes); + } + + private void assertEquals(String expectation, ByteArrayOutputStream bytes){ + expectation = expectation == null ? "" : expectation; + String bytesString = String.valueOf(bytes); + if(!expectation.equals(bytesString)){ + throw new KoanIncompleteException("expected: <"+expectation+"> but found: <"+bytesString+">"); + } + } + + public void assertSystemOutContains(String expectation){ + assertContains(true, expectation, outBytes); + } + + public void assertSystemOutDoesntContain(String expectation){ + assertContains(false, expectation, outBytes); + } + + public void assertSystemErrContains(String expectation){ + assertContains(true, expectation, errBytes); + } + + public void assertSystemErrDoesntContain(String expectation){ + assertContains(false, expectation, errBytes); + } + + private void assertContains(boolean assertContains, String expectation, ByteArrayOutputStream bytes) { + String consoleOutput = String.valueOf(bytes.toString()); + boolean containsTheSubstring = consoleOutput.contains(expectation); + if(assertContains && !containsTheSubstring || !assertContains && containsTheSubstring){ + throw new KoanIncompleteException(new StringBuilder( + "<").append( + expectation).append( + "> ").append( + (assertContains ? "wasn't" : "was")).append( + " found in: " ).append( + "<").append( + consoleOutput).append( + ">").toString()); + } + } + + public void assertSystemOutLineEquals(final int lineNumber, final String lineText){ + assertLineEquals(lineNumber, lineText, false, outBytes); + } + + public void assertSystemErrLineEquals(final int lineNumber, final String lineText){ + assertLineEquals(lineNumber, lineText, false, errBytes); + } + + public void assertLineEquals(final int lineNumber, final String lineText, final boolean trimLinesString, ByteArrayOutputStream bytes) { + final int[] onLine = new int[]{0}; + final boolean[] found = new boolean[]{false}; + String bytesString = String.valueOf(bytes); + TestUtils.forEachLine(bytesString, new ArgRunner(){ + public void run(String s){ + if(onLine[0] == lineNumber){ + if(trimLinesString){ + s = s.trim(); + } + Assert.assertEquals(lineText, s); + found[0] = true; + } + onLine[0]++; + } + }); + if(!found[0]){ + throw new KoanIncompleteException(lineText+" was expected, but not found in: "+bytesString); + } + } + + public void resetClassLoader() { + Constructor constructor = null; + Field fileMonitorField = null; + boolean consWasAccessible = false; + boolean monitorWasAccessible = false; + try { + constructor = KoanClassLoader.class.getDeclaredConstructor(); + consWasAccessible = constructor.isAccessible(); + constructor.setAccessible(true); + fileMonitorField = KoanClassLoader.class.getDeclaredField("instance"); + monitorWasAccessible = fileMonitorField.isAccessible(); + fileMonitorField.setAccessible(true); + fileMonitorField.set(KoanClassLoader.class, constructor.newInstance()); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } finally { + if(constructor != null){ + constructor.setAccessible(consWasAccessible); + } + if(fileMonitorField != null){ + fileMonitorField.setAccessible(monitorWasAccessible); + } + } + } +} diff --git a/lib/src/test/java/com/sandwich/koan/path/PathToEnlightenmentTest.java b/lib/src/test/java/com/sandwich/koan/path/PathToEnlightenmentTest.java new file mode 100644 index 00000000..3122f38c --- /dev/null +++ b/lib/src/test/java/com/sandwich/koan/path/PathToEnlightenmentTest.java @@ -0,0 +1,44 @@ +package com.sandwich.koan.path; + +import static org.junit.Assert.assertNotNull; + +import java.util.Locale; + +import org.junit.Test; + +import com.sandwich.koan.LocaleSwitchingTestCase; + +public class PathToEnlightenmentTest extends LocaleSwitchingTestCase { + + @Test + public void testFallsBackToEnglishXmlWhenNoXmlForLocaleIsFound(){ + Locale.setDefault(Locale.CHINA); + PathToEnlightenment.xmlToPathTransformer = null; + assertNotNull(PathToEnlightenment.getXmlToPathTransformer()); + } + + @Test + public void testEnglishXmlWhenXmlForLocaleIsFound_eventIsNotLogged(){ + Locale.setDefault(Locale.US); + PathToEnlightenment.xmlToPathTransformer = null; + assertLogged(PathToEnlightenment.class.getName(), new RBSensitiveLoggerExpectation(){ + protected boolean isLogCallRequired(){ + return false; + } + }); + } + + @Test + public void testEnglishXmlWhenXmlForLocaleIsFound(){ + Locale.setDefault(Locale.US); + PathToEnlightenment.xmlToPathTransformer = null; + assertNotNull(PathToEnlightenment.getXmlToPathTransformer()); + } + + private class RBSensitiveLoggerExpectation extends LoggerExpectation { + @Override + protected void invokeImplementation() { + PathToEnlightenment.getXmlToPathTransformer(); + } + } +} diff --git a/koans-tests/data/src/com/sandwich/koan/path/XmlVariableInjectorTest.java b/lib/src/test/java/com/sandwich/koan/path/RbVariableInjectorTest.java old mode 100644 new mode 100755 similarity index 65% rename from koans-tests/data/src/com/sandwich/koan/path/XmlVariableInjectorTest.java rename to lib/src/test/java/com/sandwich/koan/path/RbVariableInjectorTest.java index 0b12af7f..36791dd7 --- a/koans-tests/data/src/com/sandwich/koan/path/XmlVariableInjectorTest.java +++ b/lib/src/test/java/com/sandwich/koan/path/RbVariableInjectorTest.java @@ -7,35 +7,25 @@ import org.junit.Test; -import com.sandwich.koan.path.xmltransformation.XmlVariableInjector; +import com.sandwich.koan.path.xmltransformation.RbVariableInjector; import com.sandwich.koan.suite.OneFailingKoan; -public class XmlVariableInjectorTest { +public class RbVariableInjectorTest extends CommandLineTestCase { @Test public void construction_nullMethod() throws Exception { try{ - new XmlVariableInjector("", null); + new RbVariableInjector("", null); fail("why construct w/ null method?"); }catch(IllegalArgumentException t){ // this is ok - we want this! } } -// @Test -// public void construction_nullLesson() throws Exception { -// try{ -// new XmlVariableInjector(null, Object.class.getDeclaredMethod("equals", Object.class)); -// fail("why construct w/ null lesson?"); -// }catch(IllegalArgumentException t){ -// // this is ok - we want this! -// } -// } - @Test public void injectInputVariables_filePath() throws Exception { String lesson = "meh ${file_path}"; - String result = new XmlVariableInjector(lesson, OneFailingKoan.class.getDeclaredMethod("koanMethod")) + String result = new RbVariableInjector(lesson, OneFailingKoan.class.getDeclaredMethod("koanMethod")) .injectLessonVariables(); String firstPkgName = "com"; // just inspect anything beyond the root of the project @@ -48,7 +38,7 @@ public void injectInputVariables_filePath() throws Exception { @Test public void injectInputVariables_fileName() throws Exception { String lesson = "meh ${file_name}"; - String result = new XmlVariableInjector(lesson, OneFailingKoan.class.getDeclaredMethod("koanMethod")) + String result = new RbVariableInjector(lesson, OneFailingKoan.class.getDeclaredMethod("koanMethod")) .injectLessonVariables(); assertEquals("meh OneFailingKoan", result); } @@ -56,7 +46,7 @@ public void injectInputVariables_fileName() throws Exception { @Test public void injectInputVariables_methodName() throws Exception { String lesson = "meh ${method_name}"; - String result = new XmlVariableInjector(lesson, OneFailingKoan.class.getDeclaredMethod("koanMethod")) + String result = new RbVariableInjector(lesson, OneFailingKoan.class.getDeclaredMethod("koanMethod")) .injectLessonVariables(); assertEquals("meh koanMethod", result); } @@ -64,7 +54,7 @@ public void injectInputVariables_methodName() throws Exception { @Test public void nothingToInject() throws Exception { String lesson = " meh ea asdwdw s "; - assertSame(lesson, new XmlVariableInjector(lesson, + assertSame(lesson, new RbVariableInjector(lesson, OneFailingKoan.class.getDeclaredMethod("koanMethod")).injectLessonVariables()); } } diff --git a/koans-tests/data/src/com/sandwich/koan/path/xmltransformation/FakeXmlToPathTransformer.java b/lib/src/test/java/com/sandwich/koan/path/xmltransformation/FakeXmlToPathTransformer.java similarity index 100% rename from koans-tests/data/src/com/sandwich/koan/path/xmltransformation/FakeXmlToPathTransformer.java rename to lib/src/test/java/com/sandwich/koan/path/xmltransformation/FakeXmlToPathTransformer.java diff --git a/koans-tests/data/src/com/sandwich/koan/runner/AppLauncherTest.java b/lib/src/test/java/com/sandwich/koan/runner/AppLauncherTest.java similarity index 100% rename from koans-tests/data/src/com/sandwich/koan/runner/AppLauncherTest.java rename to lib/src/test/java/com/sandwich/koan/runner/AppLauncherTest.java diff --git a/koans-tests/test/com/sandwich/koan/runner/AppReadinessForDeploymentTest.java b/lib/src/test/java/com/sandwich/koan/runner/AppReadinessForDeploymentTest.java similarity index 72% rename from koans-tests/test/com/sandwich/koan/runner/AppReadinessForDeploymentTest.java rename to lib/src/test/java/com/sandwich/koan/runner/AppReadinessForDeploymentTest.java index fc06ead5..ae156b72 100755 --- a/koans-tests/test/com/sandwich/koan/runner/AppReadinessForDeploymentTest.java +++ b/lib/src/test/java/com/sandwich/koan/runner/AppReadinessForDeploymentTest.java @@ -1,7 +1,6 @@ package com.sandwich.koan.runner; import static com.sandwich.koan.constant.KoanConstants.EXPECTATION_LEFT_ARG; -import static com.sandwich.koan.constant.KoanConstants.__; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; @@ -13,45 +12,65 @@ import java.util.logging.LogRecord; import java.util.logging.Logger; +import org.junit.After; +import org.junit.Before; import org.junit.Test; -import com.sandwich.koan.Koan; import com.sandwich.koan.KoanMethod; import com.sandwich.koan.cmdline.CommandLineArgumentBuilder; import com.sandwich.koan.cmdline.CommandLineArgumentRunner; -import com.sandwich.koan.constant.KoanConstants; import com.sandwich.koan.path.CommandLineTestCase; import com.sandwich.koan.path.PathToEnlightenment; import com.sandwich.koan.path.xmltransformation.KoanElementAttributes; import com.sandwich.koan.result.KoanSuiteResult; import com.sandwich.koan.suite.BlowUpOnLineEleven; import com.sandwich.koan.suite.BlowUpOnLineTen; -import com.sandwich.koan.suite.OneFailingKoan; import com.sandwich.koan.suite.OnePassingKoan; +import com.sandwich.koan.suite.TwoFailingKoans; +import com.sandwich.koan.suite.WrongExpectationOrderKoan; import com.sandwich.koan.ui.SuitePresenter; +import com.sandwich.util.Strings; import com.sandwich.util.io.directories.DirectoryManager; -import com.sandwich.util.io.directories.Production; +import com.sandwich.util.io.directories.ProductionDirectories; /** - * Anything that absoutely has to happen before bundling client jar - to be sure: + * Anything that absolutely has to happen before bundling client jar - to be sure: * - all koans fail by default * - necessary aspects of app presentation are preserved * - progression through koans (the sequence of koans) is consistent */ public class AppReadinessForDeploymentTest extends CommandLineTestCase { + @Before + public void setUp() throws Exception { + super.setUp(); + resetHandlers(); + } + + @After + public void tearDown() throws Exception { + super.tearDown(); + resetHandlers(); + } + + private void resetHandlers(){ + for(Handler handler : Logger.getLogger(CommandLineArgumentRunner.class.getSimpleName()).getHandlers()){ + Logger.getLogger(CommandLineArgumentRunner.class.getSimpleName()).removeHandler(handler); + } + } + @Test public void testMainMethodWithClassNameArg_qualifiedWithPkgName() throws Throwable { stubAllKoans(Arrays.asList(new OnePassingKoan())); new CommandLineArgumentRunner().run(); - assertSystemOutContains(KoanConstants.PASSING_SUITES+" "+OnePassingKoan.class.getSimpleName()); + assertSystemOutContains(Strings.getMessage("passing_suites")+": "+OnePassingKoan.class.getSimpleName()); } @Test public void testMainMethodWithClassNameArg_classSimpleName() throws Throwable { stubAllKoans(Arrays.asList(new OnePassingKoan())); new CommandLineArgumentRunner().run(); - assertSystemOutContains(KoanConstants.PASSING_SUITES+" "+OnePassingKoan.class.getSimpleName()); + assertSystemOutContains(Strings.getMessage("passing_suites")+": "+OnePassingKoan.class.getSimpleName()); } @Test @@ -63,15 +82,10 @@ public void testMainMethodWithClassNameArg_classNameAndMethod() throws Throwable assertSystemOutContains("0/2"); } - public static class TwoFailingKoans extends OneFailingKoan { - @Koan - public void koanTwo(){assertEquals(true, false);} - } - @Test public void testGetKoans() throws Exception { stubAllKoans(Arrays.asList(new OnePassingKoan())); - Map> koans = PathToEnlightenment.getPathToEnlightment().iterator().next().getValue(); + Map> koans = PathToEnlightenment.getPathToEnlightenment().iterator().next().getValue(); assertEquals(1, koans.size()); Entry> entry = koans.entrySet().iterator().next(); assertEquals(OnePassingKoan.class.getName(), entry.getKey()); @@ -79,39 +93,46 @@ public void testGetKoans() throws Exception { KoanMethod.getInstance(entry.getValue().get("koan")).getMethod().toString()); } - @Test /** Ensures that koans are ready for packaging & distribution */ + @Test public void testKoanSuiteRunner_firstKoanFail() throws Exception { - setRealPath(); final KoanSuiteResult[] result = new KoanSuiteResult[]{null}; - final SuitePresenter presenter = new SuitePresenter(){ - public void displayResult(KoanSuiteResult actualAppResult) { - // don't display, capture them so we can analyze and ensure first failure is reported - result[0] = actualAppResult; - } - }; + stubPresenter(new SuitePresenter(){ + public void displayResult(KoanSuiteResult actualAppResult) { + // don't display, capture them so we can analyze and ensure first failure is reported + result[0] = actualAppResult; + } + public void displayError(String error) {fail();} + public void displayMessage(String msg) {fail();} + public void clearMessages() {} + }); doAsIfInProd(new Runnable(){ public void run(){ - new RunKoans(presenter, PathToEnlightenment.getPathToEnlightment()).run(null); + new RunKoans(PathToEnlightenment.getPathToEnlightenment()).run(new String[0]); } }); - String firstSuiteClassRan = PathToEnlightenment.getPathToEnlightment() + String firstSuiteClassRan = PathToEnlightenment.getPathToEnlightenment() .iterator().next().getValue().entrySet().iterator().next().getKey(); assertEquals(result[0].getFailingCase(), firstSuiteClassRan.substring(firstSuiteClassRan.lastIndexOf(".") + 1)); } - - @Test /** Ensures that koans are ready for packaging & distribution */ + + @Test public void testKoanSuiteRunner_allKoansFail() throws Exception { setRealPath(); final KoanSuiteResult[] result = new KoanSuiteResult[]{null}; - final SuitePresenter presenter = new SuitePresenter(){ + stubPresenter(new SuitePresenter(){ public void displayResult(KoanSuiteResult actualAppResult) { // don't display, capture them so we can analyze and ensure first failure is reported result[0] = actualAppResult; } - }; + public void displayError(String error) { + fail(); + } + public void displayMessage(String msg) {fail();} + public void clearMessages() {} + }); doAsIfInProd(new Runnable(){ public void run(){ - new RunKoans(presenter, PathToEnlightenment.getPathToEnlightment()).run(null); + new RunKoans(PathToEnlightenment.getPathToEnlightenment()).run(new String[0]); } }); String message = "Not all koans need solving! Each should ship in a failing state."; @@ -121,7 +142,9 @@ public void run(){ } private void doAsIfInProd(Runnable runnable) { - DirectoryManager.setDirectorySet(new Production()); + DirectoryManager.setDirectorySet(new ProductionDirectories()); + resetClassLoader(); + setRealPath(); runnable.run(); } @@ -142,7 +165,7 @@ public void testLineExceptionIsThrownAtIsHintedAtEvenIfThrownFromSuperClass() th } @Test - public void testWarningFromPlacingExpecationOnWrongSide() throws Throwable { + public void testWarningFromPlacingExpectationOnWrongSide() throws Throwable { final String[] message = new String[1]; stubAllKoans(Arrays.asList(new WrongExpectationOrderKoan())); Logger.getLogger(CommandLineArgumentRunner.class.getSimpleName()).addHandler( @@ -169,7 +192,7 @@ public void publish(LogRecord arg0) { } @Test - public void testNoWarningFromPlacingExpecationOnRightSide() + public void testNoWarningFromPlacingExpectationOnRightSide() throws Throwable { stubAllKoans(Arrays.asList(new OnePassingKoan())); Logger.getLogger(CommandLineArgumentRunner.class.getSimpleName()).addHandler( @@ -184,16 +207,9 @@ public void flush() { @Override public void publish(LogRecord arg0) { - fail("No logging necessary when koan passes, otherwise - logging is new, adjust accordingly."); + fail("No logging necessary when koan passes, otherwise - logging is new, adjust accordingly.\n"+arg0.getMessage()); } }); new CommandLineArgumentRunner().run(); } - - public static class WrongExpectationOrderKoan { - @Koan - public void expectationOnLeft() { - com.sandwich.util.Assert.assertEquals(__, false); - } - } } diff --git a/koans-tests/data/src/com/sandwich/koan/runner/CommandLineTestCaseTest.java b/lib/src/test/java/com/sandwich/koan/runner/CommandLineTestCaseTest.java old mode 100644 new mode 100755 similarity index 77% rename from koans-tests/data/src/com/sandwich/koan/runner/CommandLineTestCaseTest.java rename to lib/src/test/java/com/sandwich/koan/runner/CommandLineTestCaseTest.java index ada9a778..797a7cdf --- a/koans-tests/data/src/com/sandwich/koan/runner/CommandLineTestCaseTest.java +++ b/lib/src/test/java/com/sandwich/koan/runner/CommandLineTestCaseTest.java @@ -27,7 +27,7 @@ public void setUp(){ } @After - public void tearDown(){ + public void tearDown() throws Exception{ testCase.tearDown(); // really important - will remove regular System.out if commented! testCase = null; } @@ -54,15 +54,7 @@ public void testThatConsoleIsAttachedToSystem() throws Exception { } @Test - public void testThatAssertSystemOutLineEquals_withTrimStringArg() throws Exception { - testCase.setUp(); - System.out.println(" hello \n world! "); - testCase.assertSystemOutLineEquals(0, "hello", true); - testCase.assertSystemOutLineEquals(1, "world!", true); - } - - @Test - public void testThatTearDownDetachesDummiedConsoleFromSystem(){ + public void testThatTearDownDetachesDummiedConsoleFromSystem() throws Exception{ PrintStream originalConsole = System.out; testCase.setUp(); PrintStream fakeConsole = System.out; @@ -73,25 +65,25 @@ public void testThatTearDownDetachesDummiedConsoleFromSystem(){ @Test public void testThatStubAllKoansStubsAllKoansReference() throws Exception { - Path oldKoans = PathToEnlightenment.getPathToEnlightment(); + Path oldKoans = PathToEnlightenment.getPathToEnlightenment(); List newKoans = Collections.emptyList(); testCase.stubAllKoans(newKoans); - assertNotSame(oldKoans, PathToEnlightenment.getPathToEnlightment()); + assertNotSame(oldKoans, PathToEnlightenment.getPathToEnlightenment()); // number of suites - assertEquals(0, PathToEnlightenment.getPathToEnlightment() + assertEquals(0, PathToEnlightenment.getPathToEnlightenment() .iterator().next().getValue().size()); } @Test public void testTestCaseRestoresAllKoansReference() throws Exception { - Path oldKoans = PathToEnlightenment.getPathToEnlightment(); + Path oldKoans = PathToEnlightenment.getPathToEnlightenment(); List newKoans = Collections.emptyList(); testCase.stubAllKoans(newKoans); - assertNotSame(oldKoans, PathToEnlightenment.getPathToEnlightment()); + assertNotSame(oldKoans, PathToEnlightenment.getPathToEnlightenment()); testCase.tearDown(); // creates all new instance - assertNotSame(oldKoans, PathToEnlightenment.getPathToEnlightment()); - assertEquals(oldKoans, PathToEnlightenment.getPathToEnlightment()); + assertNotSame(oldKoans, PathToEnlightenment.getPathToEnlightenment()); + assertEquals(oldKoans, PathToEnlightenment.getPathToEnlightenment()); } @Test @@ -99,12 +91,12 @@ public void testClearSysout() throws Exception { testCase.setUp(); System.out.print("!"); testCase.assertSystemOutLineEquals(0, "!"); - testCase.clearSysout(); + testCase.clearSystemOutputs(); testCase.assertSystemOutLineEquals(0, ""); } @Test - public void testSystemOutEquals_freshStart(){ + public void testSystemOutEquals_freshStart() throws Exception{ testCase.setUp(); testCase.assertSystemOutEquals(""); } diff --git a/koans-tests/test/com/sandwich/koan/runner/ConsolePresenterTest.java b/lib/src/test/java/com/sandwich/koan/runner/ConsolePresenterTest.java similarity index 73% rename from koans-tests/test/com/sandwich/koan/runner/ConsolePresenterTest.java rename to lib/src/test/java/com/sandwich/koan/runner/ConsolePresenterTest.java index 748f9818..86b5c929 100755 --- a/koans-tests/test/com/sandwich/koan/runner/ConsolePresenterTest.java +++ b/lib/src/test/java/com/sandwich/koan/runner/ConsolePresenterTest.java @@ -1,33 +1,24 @@ package com.sandwich.koan.runner; -import static com.sandwich.koan.constant.KoanConstants.ALL_SUCCEEDED; import static com.sandwich.koan.constant.KoanConstants.COMPLETE_CHAR; -import static com.sandwich.koan.constant.KoanConstants.CONQUERED; -import static com.sandwich.koan.constant.KoanConstants.ENCOURAGEMENT; import static com.sandwich.koan.constant.KoanConstants.EOL; -import static com.sandwich.koan.constant.KoanConstants.FAILING_SUITES; import static com.sandwich.koan.constant.KoanConstants.INCOMPLETE_CHAR; -import static com.sandwich.koan.constant.KoanConstants.INVESTIGATE_IN_THE; -import static com.sandwich.koan.constant.KoanConstants.KOAN; -import static com.sandwich.koan.constant.KoanConstants.OUT_OF; -import static com.sandwich.koan.constant.KoanConstants.PASSING_SUITES; -import static com.sandwich.koan.constant.KoanConstants.PROGRESS; import static com.sandwich.koan.constant.KoanConstants.PROGRESS_BAR_START; import static com.sandwich.koan.constant.KoanConstants.PROGRESS_BAR_WIDTH; -import static com.sandwich.koan.constant.KoanConstants.WHATS_WRONG; import java.util.Arrays; import org.junit.Test; -import com.sandwich.koan.cmdline.CommandLineArgumentRunner; +import com.sandwich.koan.ApplicationSettings; import com.sandwich.koan.cmdline.CommandLineArgumentBuilder; -import com.sandwich.koan.constant.KoanConstants; +import com.sandwich.koan.cmdline.CommandLineArgumentRunner; import com.sandwich.koan.path.CommandLineTestCase; import com.sandwich.koan.suite.OneFailingKoan; import com.sandwich.koan.suite.OneFailingKoanDifferentName; import com.sandwich.koan.suite.OnePassingKoan; import com.sandwich.koan.suite.OnePassingKoanDifferentName; +import com.sandwich.util.Strings; public class ConsolePresenterTest extends CommandLineTestCase { @@ -36,8 +27,8 @@ public void hintPresentation() throws Throwable { stubAllKoans(Arrays.asList(new OneFailingKoanDifferentName())); new CommandLineArgumentRunner(new CommandLineArgumentBuilder()).run(); assertSystemOutContains(new StringBuilder( - INVESTIGATE_IN_THE).append( - " ").append( + Strings.getMessage("investigate")).append( + ": ").append( OneFailingKoanDifferentName.class.getSimpleName()).append( " class's ").append( OneFailingKoanDifferentName.class.getDeclaredMethod("koanMethod").getName()).append( @@ -47,19 +38,19 @@ public void hintPresentation() throws Throwable { @Test // uncomment enableEncouragement @ the top of ConsolePresenter class public void encouragement() throws Throwable { - if(KoanConstants.ENABLE_ENCOURAGEMENT){ + if(ApplicationSettings.isEncouragementEnabled()){ stubAllKoans(Arrays.asList(new Class[] { OneFailingKoan.class })); new CommandLineArgumentRunner().run(); assertSystemOutContains(new StringBuilder( - CONQUERED).append( + Strings.getMessage("you_have_conquered")).append( " 0 ").append( - OUT_OF).append( + Strings.getMessage("out_of")).append( " 1 ").append( - KOAN).append( + Strings.getMessage("koan")).append( "! ").append( - ENCOURAGEMENT).toString()); - assertSystemOutDoesntContain(ALL_SUCCEEDED); + Strings.getMessage("encouragement")).toString()); + assertSystemOutDoesntContain(Strings.getMessage("all_koans_succeeded")); } } @@ -68,38 +59,38 @@ public void testOneHundredPercentSuccessReward() throws Throwable { stubAllKoans(Arrays.asList(new Class[] { OnePassingKoan.class })); new CommandLineArgumentRunner().run(); - assertSystemOutContains(ALL_SUCCEEDED); - assertSystemOutDoesntContain(CONQUERED); - assertSystemOutDoesntContain(ENCOURAGEMENT); + assertSystemOutContains(Strings.getMessage("all_koans_succeeded")); + assertSystemOutDoesntContain(Strings.getMessage("you_have_conquered")); + assertSystemOutDoesntContain(Strings.getMessage("encouragement")); } @Test public void passingSuites() throws Throwable { stubAllKoans(Arrays.asList(new OnePassingKoan(), new OnePassingKoanDifferentName())); new CommandLineArgumentRunner().run(); - assertSystemOutContains(PASSING_SUITES+" OnePassingKoan, OnePassingKoanDifferentName"); + assertSystemOutContains(Strings.getMessage("passing_suites")+": OnePassingKoan, OnePassingKoanDifferentName"); } @Test public void failingSuites() throws Throwable { stubAllKoans(Arrays.asList(new OneFailingKoan(), new OneFailingKoanDifferentName())); new CommandLineArgumentRunner().run(); - assertSystemOutContains(FAILING_SUITES+" OneFailingKoan, OneFailingKoanDifferentName"); + assertSystemOutContains(Strings.getMessage("remaining_suites")+": OneFailingKoan, OneFailingKoanDifferentName"); } @Test public void failingAndPassingSuites() throws Throwable { stubAllKoans(Arrays.asList(new OnePassingKoan(), new OneFailingKoan())); new CommandLineArgumentRunner().run(); - assertSystemOutContains(PASSING_SUITES+" OnePassingKoan"+EOL+ - FAILING_SUITES+" OneFailingKoan"); + assertSystemOutContains(Strings.getMessage("passing_suites")+": OnePassingKoan"+EOL+ + Strings.getMessage("remaining_suites")+": OneFailingKoan"); } @Test public void progressAllPassing() throws Throwable { stubAllKoans(Arrays.asList(new OnePassingKoan())); new CommandLineArgumentRunner().run(); - StringBuilder sb = new StringBuilder(PROGRESS).append(" ") + StringBuilder sb = new StringBuilder(Strings.getMessage("progress")).append(" ") .append(PROGRESS_BAR_START); for(int i = 0; i < PROGRESS_BAR_WIDTH; i++){ // 100% success sb.append(COMPLETE_CHAR); @@ -113,7 +104,7 @@ public void progressAllFailing() throws Throwable { stubAllKoans(Arrays.asList(new OneFailingKoan(), new OneFailingKoanDifferentName())); new CommandLineArgumentRunner().run(); StringBuilder sb = new StringBuilder( - PROGRESS).append(" ").append( + Strings.getMessage("progress")).append(" ").append( PROGRESS_BAR_START); for(int i = 0; i < PROGRESS_BAR_WIDTH; i++){ // 100% failed sb.append(INCOMPLETE_CHAR); @@ -127,7 +118,7 @@ public void progressFiftyFifty() throws Throwable { stubAllKoans(Arrays.asList(new OnePassingKoan(), new OneFailingKoan())); new CommandLineArgumentRunner().run(); StringBuilder sb = new StringBuilder( - PROGRESS).append(" ").append( + Strings.getMessage("progress")).append(" ").append( PROGRESS_BAR_START); for(int i = 0; i < PROGRESS_BAR_WIDTH / 2; i++){ // 50% succeeded sb.append(COMPLETE_CHAR); @@ -144,7 +135,8 @@ public void whatWentWrongExplanation() throws Throwable { stubAllKoans(Arrays.asList(new OneFailingKoan())); new CommandLineArgumentRunner().run(); assertSystemOutContains(new StringBuilder( - WHATS_WRONG).append( + Strings.getMessage("what_went_wrong")).append( + ": ").append( EOL).append( "expected: but was:").toString()); } diff --git a/koans-tests/test/com/sandwich/koan/runner/ui/AbstractSuitePresenterTest.java b/lib/src/test/java/com/sandwich/koan/runner/ui/AbstractSuitePresenterTest.java similarity index 83% rename from koans-tests/test/com/sandwich/koan/runner/ui/AbstractSuitePresenterTest.java rename to lib/src/test/java/com/sandwich/koan/runner/ui/AbstractSuitePresenterTest.java index 1e22df3c..4b97e38d 100755 --- a/koans-tests/test/com/sandwich/koan/runner/ui/AbstractSuitePresenterTest.java +++ b/lib/src/test/java/com/sandwich/koan/runner/ui/AbstractSuitePresenterTest.java @@ -8,13 +8,14 @@ import org.junit.Test; import com.sandwich.koan.KoanMethod; +import com.sandwich.koan.path.CommandLineTestCase; import com.sandwich.koan.result.KoanMethodResult; import com.sandwich.koan.result.KoanSuiteResult; import com.sandwich.koan.result.KoanSuiteResult.KoanResultBuilder; import com.sandwich.koan.suite.OneFailingKoan; import com.sandwich.koan.ui.AbstractSuitePresenter; -public class AbstractSuitePresenterTest { +public class AbstractSuitePresenterTest extends CommandLineTestCase { @Test public void testForwardingOneHundredPercentSuccess() throws Exception { @@ -39,6 +40,15 @@ public void displayHeader(KoanSuiteResult result) { public void displayOneOrMoreFailure(KoanSuiteResult result) { fail(); } + public void displayError(String error) { + fail(); + } + public void displayMessage(String error) { + fail(); + } + public void clearMessages() { + fail(); + } }; KoanSuiteResult kr = new KoanResultBuilder().build(); @@ -69,6 +79,15 @@ public void displayHeader(KoanSuiteResult result) { public void displayAllSuccess(KoanSuiteResult result) { fail(); } + public void displayError(String error) { + fail(); + } + public void displayMessage(String error) { + fail(); + } + public void clearMessages() { + fail(); + } }; KoanSuiteResult kr = new KoanResultBuilder().remainingCases(Arrays.asList(OneFailingKoan.class.getSimpleName()) diff --git a/koans-tests/data/src/com/sandwich/koan/suite/BlowUpOnLineEleven.java b/lib/src/test/java/com/sandwich/koan/suite/BlowUpOnLineEleven.java old mode 100644 new mode 100755 similarity index 82% rename from koans-tests/data/src/com/sandwich/koan/suite/BlowUpOnLineEleven.java rename to lib/src/test/java/com/sandwich/koan/suite/BlowUpOnLineEleven.java index 3d6753c3..44231df4 --- a/koans-tests/data/src/com/sandwich/koan/suite/BlowUpOnLineEleven.java +++ b/lib/src/test/java/com/sandwich/koan/suite/BlowUpOnLineEleven.java @@ -4,7 +4,7 @@ import com.sandwich.koan.KoanIncompleteException; public class BlowUpOnLineEleven { - // gotta put it on line 11 thus the two spaces here + // gotta put it on line 11 thus the two lines here // @Koan public void blowUpOnLineEleven() { diff --git a/koans-tests/test/com/sandwich/koan/suite/BlowUpOnLineEleven.java b/lib/src/test/java/com/sandwich/koan/suite/BlowUpOnLineTen.java similarity index 56% rename from koans-tests/test/com/sandwich/koan/suite/BlowUpOnLineEleven.java rename to lib/src/test/java/com/sandwich/koan/suite/BlowUpOnLineTen.java index 3d6753c3..d5b80735 100755 --- a/koans-tests/test/com/sandwich/koan/suite/BlowUpOnLineEleven.java +++ b/lib/src/test/java/com/sandwich/koan/suite/BlowUpOnLineTen.java @@ -3,12 +3,10 @@ import com.sandwich.koan.Koan; import com.sandwich.koan.KoanIncompleteException; -public class BlowUpOnLineEleven { - // gotta put it on line 11 thus the two spaces here - // +public class BlowUpOnLineTen { + // gotta blow up on line 10 thus the comment @Koan - public void blowUpOnLineEleven() { + public void blowUpOnLineTen(){ throw new KoanIncompleteException(null); } - } diff --git a/koans-tests/data/src/com/sandwich/koan/suite/OneFailingKoan.java b/lib/src/test/java/com/sandwich/koan/suite/OneFailingKoan.java old mode 100644 new mode 100755 similarity index 100% rename from koans-tests/data/src/com/sandwich/koan/suite/OneFailingKoan.java rename to lib/src/test/java/com/sandwich/koan/suite/OneFailingKoan.java diff --git a/koans-tests/data/src/com/sandwich/koan/suite/OneFailingKoanDifferentName.java b/lib/src/test/java/com/sandwich/koan/suite/OneFailingKoanDifferentName.java old mode 100644 new mode 100755 similarity index 96% rename from koans-tests/data/src/com/sandwich/koan/suite/OneFailingKoanDifferentName.java rename to lib/src/test/java/com/sandwich/koan/suite/OneFailingKoanDifferentName.java index 28da00ca..64666e0d --- a/koans-tests/data/src/com/sandwich/koan/suite/OneFailingKoanDifferentName.java +++ b/lib/src/test/java/com/sandwich/koan/suite/OneFailingKoanDifferentName.java @@ -5,7 +5,7 @@ import com.sandwich.koan.Koan; public class OneFailingKoanDifferentName extends OneFailingKoan { - @Koan() + @Koan @Override public void koanMethod() { assertEquals(true, false); diff --git a/koans-tests/data/src/com/sandwich/koan/suite/OnePassingKoan.java b/lib/src/test/java/com/sandwich/koan/suite/OnePassingKoan.java old mode 100644 new mode 100755 similarity index 100% rename from koans-tests/data/src/com/sandwich/koan/suite/OnePassingKoan.java rename to lib/src/test/java/com/sandwich/koan/suite/OnePassingKoan.java diff --git a/koans-tests/data/src/com/sandwich/koan/suite/OnePassingKoanDifferentName.java b/lib/src/test/java/com/sandwich/koan/suite/OnePassingKoanDifferentName.java old mode 100644 new mode 100755 similarity index 61% rename from koans-tests/data/src/com/sandwich/koan/suite/OnePassingKoanDifferentName.java rename to lib/src/test/java/com/sandwich/koan/suite/OnePassingKoanDifferentName.java index 5a76b430..fa1c90cf --- a/koans-tests/data/src/com/sandwich/koan/suite/OnePassingKoanDifferentName.java +++ b/lib/src/test/java/com/sandwich/koan/suite/OnePassingKoanDifferentName.java @@ -1,5 +1,10 @@ package com.sandwich.koan.suite; +import com.sandwich.koan.Koan; + public class OnePassingKoanDifferentName extends OnePassingKoan { + @Koan + public void koan() { } + } diff --git a/koans-tests/test/com/sandwich/koan/suite/OneFailingKoan.java b/lib/src/test/java/com/sandwich/koan/suite/TwoFailingKoans.java old mode 100755 new mode 100644 similarity index 66% rename from koans-tests/test/com/sandwich/koan/suite/OneFailingKoan.java rename to lib/src/test/java/com/sandwich/koan/suite/TwoFailingKoans.java index e16e4296..a64b6576 --- a/koans-tests/test/com/sandwich/koan/suite/OneFailingKoan.java +++ b/lib/src/test/java/com/sandwich/koan/suite/TwoFailingKoans.java @@ -4,7 +4,12 @@ import com.sandwich.koan.Koan; -public class OneFailingKoan { +public class TwoFailingKoans { + @Koan + public void koanTwo() { + assertEquals(true, false); + } + @Koan public void koanMethod() { assertEquals(true, false); diff --git a/lib/src/test/java/com/sandwich/koan/suite/WrongExpectationOrderKoan.java b/lib/src/test/java/com/sandwich/koan/suite/WrongExpectationOrderKoan.java new file mode 100644 index 00000000..32138fe5 --- /dev/null +++ b/lib/src/test/java/com/sandwich/koan/suite/WrongExpectationOrderKoan.java @@ -0,0 +1,12 @@ +package com.sandwich.koan.suite; + +import static com.sandwich.koan.constant.KoanConstants.__; + +import com.sandwich.koan.Koan; + +public class WrongExpectationOrderKoan { + @Koan + public void expectationOnLeft() { + com.sandwich.util.Assert.assertEquals(__, false); + } +} \ No newline at end of file diff --git a/koans-tests/test/com/sandwich/util/KoanComparatorTest.java b/lib/src/test/java/com/sandwich/util/KoanComparatorTest.java similarity index 53% rename from koans-tests/test/com/sandwich/util/KoanComparatorTest.java rename to lib/src/test/java/com/sandwich/util/KoanComparatorTest.java index 78473088..8b709d23 100755 --- a/koans-tests/test/com/sandwich/util/KoanComparatorTest.java +++ b/lib/src/test/java/com/sandwich/util/KoanComparatorTest.java @@ -15,12 +15,16 @@ public class KoanComparatorTest extends CommandLineTestCase { + interface Caps { + public String capitalize(String name); + } + @Test public void testThatKomparatorBombsWhenNotFound() throws Exception { Method m = new Object(){ - @SuppressWarnings("unused") @Koan public void someMethod(){} + @Koan public void someMethod(){} }.getClass().getDeclaredMethod("someMethod"); - KoanComparator comparator = new KoanComparator("meh"); + KoanComparator comparator = new KoanComparator(); try{ comparator.compare(KoanMethod.getInstance("2",m), KoanMethod.getInstance("1",m)); }catch(RuntimeException fileNotFound){} @@ -29,15 +33,31 @@ public void testThatKomparatorBombsWhenNotFound() throws Exception { @Test public void testComparatorRanksByOrder() throws Exception { Class clazz = new Object(){ - @SuppressWarnings("unused") @Koan public void someMethodOne(){} - @SuppressWarnings("unused") @Koan public void someMethodTwo(){} + @Koan public void someMethodOne(){} + @Koan public void someMethodTwo(){} }.getClass(); KoanMethod m1 = KoanMethod.getInstance("",clazz.getDeclaredMethod("someMethodOne")); KoanMethod m2 = KoanMethod.getInstance("",clazz.getDeclaredMethod("someMethodTwo")); List methods = Arrays.asList(m2,m1); - Collections.sort(methods, new KoanComparator("someMethodOne","someMethodTwo")); + Collections.sort(methods, new KoanComparator()); assertSame(m1,methods.get(0)); assertSame(m2,methods.get(1)); } + + @Test + public void testVariableNamesArentConfusedAsKoanMethodsWhenSorting() throws Exception { + Class clazz = new Object(){ + @SuppressWarnings("unused") + String foo; + @Koan public void bar(){} + @Koan public void foo(){} + }.getClass(); + KoanMethod m1 = KoanMethod.getInstance("", clazz.getDeclaredMethod("bar")); + KoanMethod m2 = KoanMethod.getInstance("", clazz.getDeclaredMethod("foo")); + List methods = Arrays.asList(m2,m1); + Collections.sort(methods, new KoanComparator()); + assertSame(m1,methods.get(0)); + assertSame(m2,methods.get(1)); + } } diff --git a/koans-lib/src/com/sandwich/util/SimpleEntry.java b/lib/src/test/java/com/sandwich/util/SimpleEntry.java similarity index 94% rename from koans-lib/src/com/sandwich/util/SimpleEntry.java rename to lib/src/test/java/com/sandwich/util/SimpleEntry.java index c27e16ab..816e0498 100755 --- a/koans-lib/src/com/sandwich/util/SimpleEntry.java +++ b/lib/src/test/java/com/sandwich/util/SimpleEntry.java @@ -1,68 +1,68 @@ -package com.sandwich.util; - -import java.util.Map.Entry; - -/** - * Why on earth this wasn't around when JDK5 was released is just as bad as - * string.isEmpty()'s absence jeesh! - * - * @param - * @param - */ -public class SimpleEntry implements Entry{ - - private K k; - private V v; - - public SimpleEntry(K k, V v){ - this.k = k; - this.v = v; - } - - public K getKey() { - return k; - } - - public V getValue() { - return v; - } - - public V setValue(V value) { - V tmp = v; - v = value; - return tmp; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((k == null) ? 0 : k.hashCode()); - result = prime * result + ((v == null) ? 0 : v.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if(!Entry.class.isAssignableFrom(SimpleEntry.class)){ - return false; - } - Entry other = (Entry) obj; - if (k == null) { - if (other.getKey() != null) - return false; - } else if (!k.equals(other.getKey())) - return false; - if (v == null) { - if (other.getValue() != null) - return false; - } else if (!v.equals(other.getValue())) - return false; - return true; - } - -} +package com.sandwich.util; + +import java.util.Map.Entry; + +/** + * Why on earth this wasn't around when JDK5 was released is just as bad as + * string.isEmpty()'s absence jeesh! + * + * @param + * @param + */ +public class SimpleEntry implements Entry{ + + private K k; + private V v; + + public SimpleEntry(K k, V v){ + this.k = k; + this.v = v; + } + + public K getKey() { + return k; + } + + public V getValue() { + return v; + } + + public V setValue(V value) { + V tmp = v; + v = value; + return tmp; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((k == null) ? 0 : k.hashCode()); + result = prime * result + ((v == null) ? 0 : v.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if(!Entry.class.isAssignableFrom(SimpleEntry.class)){ + return false; + } + Entry other = (Entry) obj; + if (k == null) { + if (other.getKey() != null) + return false; + } else if (!k.equals(other.getKey())) + return false; + if (v == null) { + if (other.getValue() != null) + return false; + } else if (!v.equals(other.getValue())) + return false; + return true; + } + +} diff --git a/lib/src/test/java/com/sandwich/util/StringsTest.java b/lib/src/test/java/com/sandwich/util/StringsTest.java new file mode 100644 index 00000000..54d2ab7c --- /dev/null +++ b/lib/src/test/java/com/sandwich/util/StringsTest.java @@ -0,0 +1,63 @@ +package com.sandwich.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.Locale; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +import org.junit.Test; + +import com.sandwich.koan.LocaleSwitchingTestCase; + +public class StringsTest extends LocaleSwitchingTestCase { + + @Test + public void testStringsBundleInitializationWithNonDefaultLocale() throws Exception { + Locale.setDefault(Locale.CHINA); + assertNotNull(Strings.createResourceBundle()); + } + + @Test + public void testFallsBackToEnglishXmlWhenNoXmlForLocaleIsFound_eventIsLogged(){ + Locale.setDefault(Locale.CHINA); + assertLogged(Strings.class.getName(), new RBSensitiveLoggerExpectation(){ + @Override + protected void logCalled(LogRecord record) { + assertEquals(Level.INFO, record.getLevel()); + String expectation0 = "Your default language is not supported yet. "; + String msg = record.getMessage(); + assertTrue(msg.startsWith(expectation0)); + // c:\meh\always funny stuff %$#\messages_ze.properties or /User/nix machine/messages_ze.properties... + assertTrue(msg.contains("messages_zh.properties")); + } + }); + } + + @Test + public void testEnglishXmlWhenXmlForLocaleIsFound_eventIsNotLogged(){ + Locale.setDefault(Locale.US); + assertLogged(Strings.class.getName(), new RBSensitiveLoggerExpectation(){ + @Override + protected boolean isLogCallRequired() { + return false; + } + }); + } + + @Test + public void testStringsBundleInitializationWithDefaultLocale() throws Exception { + Locale.setDefault(Locale.US); + assertNotNull(Strings.createResourceBundle()); + } + + private class RBSensitiveLoggerExpectation extends LoggerExpectation { + @Override + protected void invokeImplementation() { + Strings.createResourceBundle(); + } + } + +} diff --git a/lib/src/test/java/com/sandwich/util/io/DirectoryManagerTest.java b/lib/src/test/java/com/sandwich/util/io/DirectoryManagerTest.java new file mode 100644 index 00000000..e051dd29 --- /dev/null +++ b/lib/src/test/java/com/sandwich/util/io/DirectoryManagerTest.java @@ -0,0 +1,69 @@ +package com.sandwich.util.io; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.sandwich.koan.path.CommandLineTestCase; +import com.sandwich.util.io.directories.DirectoryManager; + + +public class DirectoryManagerTest extends CommandLineTestCase { + + private static final String sep = System.getProperty("file.separator"); + private static final String start = + System.getProperty("os.name").toLowerCase().contains("windows") ? + "" : sep; + + @Test + public void testFileSeparatorInjection_happyPath() throws Exception { + assertEquals(start+"home"+sep+"wilford"+sep+"liberty", DirectoryManager.injectFileSystemSeparators( + "home", "wilford", "liberty")); + } + + @Test + public void testFileSeparatorInjection_separatorsAtBeginning() throws Exception { + assertEquals(start+"home"+sep+"wilford"+sep+"liberty", DirectoryManager.injectFileSystemSeparators( + sep+"home", sep+"wilford", sep+"liberty")); + } + + @Test + public void testFileSeparatorInjection_separatorsAtEnding() throws Exception { + assertEquals(start+"home"+sep+"wilford"+sep+"liberty", DirectoryManager.injectFileSystemSeparators( + "home"+sep, "wilford"+sep, "liberty"+sep)); + } + + @Test + public void testFileSeparatorInjection_separatorsAtBeginningAndEnding() throws Exception { + assertEquals(start+"home"+sep+"wilford"+sep+"liberty", DirectoryManager.injectFileSystemSeparators( + "home"+sep, "wilford"+sep, sep+"liberty"+sep)); + } + + @Test + public void testFileSeparatorInjection_separatorsInMiddle() throws Exception { + assertEquals(start+"home"+sep+"wilford"+sep+"liberty", DirectoryManager.injectFileSystemSeparators( + "home", "wilford"+sep+"liberty")); + } + + @Test + public void testWindowsFileSystemPathWithSpaces() throws Exception { + String osName = System.getProperty("os.name"); + try{ + System.setProperty("os.name", "Windows ME"); + assertEquals("Documents and Settings", DirectoryManager.injectFileSystemSeparators("Documents%20and%20Settings")); + }finally{ + System.setProperty("os.name", osName); + } + } + + @Test + public void testNonWindowsFileSystemPathWithSpaces() throws Exception { + String osName = System.getProperty("os.name"); + try{ + System.setProperty("os.name", "Not MS"); + assertEquals(sep+"Documents%20and%20Settings", DirectoryManager.injectFileSystemSeparators("Documents%20and%20Settings")); + }finally{ + System.setProperty("os.name", osName); + } + } +} diff --git a/lib/src/test/java/com/sandwich/util/io/FileOperationTest.java b/lib/src/test/java/com/sandwich/util/io/FileOperationTest.java new file mode 100644 index 00000000..f24a8b5a --- /dev/null +++ b/lib/src/test/java/com/sandwich/util/io/FileOperationTest.java @@ -0,0 +1,96 @@ +package com.sandwich.util.io; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; + +import org.junit.Test; + +import com.sandwich.util.io.directories.DirectoryManager; + +public class FileOperationTest { + + @Test + public void givenAnExistingFileNoFilterIsNotIgnored() throws Exception { + assertFalse(new FileOperation() { + public void onDirectory(File dir) throws IOException {} + public void onFile(File file) throws IOException {} + public void onNull(File nullFile) throws IOException {} + public void onNew(File newFile) throws IOException {} + }.isIgnored(new File(DirectoryManager.getBinDir()))); + } + + @Test + public void givenAnExistingFileWithNonMatchingFilterIsNotIgnored() throws Exception { + assertFalse(new FileOperation("nowayimpartofafoldername") { + public void onDirectory(File dir) throws IOException {} + public void onFile(File file) throws IOException {} + public void onNull(File nullFile) throws IOException {} + public void onNew(File newFile) throws IOException {} + }.isIgnored(new File(DirectoryManager.getBinDir()))); + } + + @Test + public void givenAnExistingFileWithMatchingFilterIsIgnored() throws Exception { + assertTrue(new FileOperation("bin") { + public void onDirectory(File dir) throws IOException {} + public void onFile(File file) throws IOException {} + public void onNull(File nullFile) throws IOException {} + public void onNew(File newFile) throws IOException {} + }.isIgnored(new File(DirectoryManager.getBinDir()))); + } + + @Test + public void givenAnExistingFileWithFilterMatchingOnParentFolderIsIgnored() throws Exception { + assertTrue(new FileOperation("app") { + public void onDirectory(File dir) throws IOException {} + public void onFile(File file) throws IOException {} + public void onNull(File nullFile) throws IOException {} + public void onNew(File newFile) throws IOException {} + }.isIgnored(new File(DirectoryManager.getBinDir()))); + } + + @Test + public void givenAnExistingFileWithPartiallyMatchingFilterIsNotIgnored() throws Exception { + assertFalse(new FileOperation("bi") { + public void onDirectory(File dir) throws IOException {} + public void onFile(File file) throws IOException {} + public void onNull(File nullFile) throws IOException {} + public void onNew(File newFile) throws IOException {} + }.isIgnored(new File(DirectoryManager.getBinDir()))); + } + + @Test + public void givenAnExistingFileWithRegexMatchingFilterIsIgnored() throws Exception { + assertTrue(new FileOperation("b.n") { + public void onDirectory(File dir) throws IOException {} + public void onFile(File file) throws IOException {} + public void onNull(File nullFile) throws IOException {} + public void onNew(File newFile) throws IOException {} + }.isIgnored(new File(DirectoryManager.getBinDir()))); + } + + @Test + public void givenAnExistingFileWithRegexMatchingFilterIsIgnored_compliment() throws Exception { + assertFalse(new FileOperation("nowayimpart..afoldername") { + public void onDirectory(File dir) throws IOException {} + public void onFile(File file) throws IOException {} + public void onNull(File nullFile) throws IOException {} + public void onNew(File newFile) throws IOException {} + }.isIgnored(new File(DirectoryManager.getBinDir()))); + } + + @Test + public void givenIntellijFoldersSpecificallyFileInIdeaFolderAreIgnored() throws Exception { + // not sure why idea ide's would be a problem... TODO: testing this specifically smells like a bigger problem... + assertTrue(new FileOperation(".*\\.idea") { + public void onDirectory(File dir) throws IOException {} + public void onFile(File file) throws IOException {} + public void onNull(File nullFile) throws IOException {} + public void onNew(File newFile) throws IOException {} + }.isIgnored(new File("/java-dojo/labs/01-koans/java-koans\\koans.idea/workspace.xml___jb_old___"))); + } + +} diff --git a/lib/src/test/java/com/sandwich/util/io/directories/UnitTestDirectories.java b/lib/src/test/java/com/sandwich/util/io/directories/UnitTestDirectories.java new file mode 100644 index 00000000..11e15f3e --- /dev/null +++ b/lib/src/test/java/com/sandwich/util/io/directories/UnitTestDirectories.java @@ -0,0 +1,17 @@ +package com.sandwich.util.io.directories; + + + +public class UnitTestDirectories extends ProductionDirectories { + + public String getSourceDir() { + return "java"; + } + + public String getProjectDir() { + String sep = System.getProperty("file.separator"); + return super.getLibrariesDir() + sep + + "src" + sep + "test"; + } + +}