You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2009/08/12 16:10:01 UTC
svn commit: r803520 - in /sling/trunk/contrib:
launchpad/testing/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/scala/
launchpad/testing/src/test/resources/integration-test/scala/
scripting/scala/engine/ scripting/scala/engine/src/main...
Author: bdelacretaz
Date: Wed Aug 12 14:10:00 2009
New Revision: 803520
URL: http://svn.apache.org/viewvc?rev=803520&view=rev
Log:
SLING-1053 - Scala scripting tests, contributed by Michael Duerig, thanks!
Modified:
sling/trunk/contrib/launchpad/testing/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/scala/ScalaScriptingTest.java
sling/trunk/contrib/launchpad/testing/src/test/resources/integration-test/scala/rendering-test.scs
sling/trunk/contrib/scripting/scala/engine/README.txt
sling/trunk/contrib/scripting/scala/engine/src/main/java/org/apache/sling/scripting/scala/engine/ScalaScriptEngine.java
sling/trunk/contrib/scripting/scala/engine/src/main/java/org/apache/sling/scripting/scala/engine/ScalaScriptEngineFactory.java
sling/trunk/contrib/scripting/scala/engine/src/test/java/org/apache/sling/scripting/scala/ScalaScriptEngineTest.java
sling/trunk/contrib/scripting/scala/engine/src/test/resources/scripts/simple.scs
sling/trunk/contrib/scripting/scala/interpreter/ (props changed)
sling/trunk/contrib/scripting/scala/interpreter/README.txt
sling/trunk/contrib/scripting/scala/interpreter/src/main/scala/org/apache/sling/scripting/scala/interpreter/ScalaInterpreter.scala
sling/trunk/contrib/scripting/scala/interpreter/src/test/scala/org/apache/sling/scripting/scala/interpreter/InterpreterTest.scala
Modified: sling/trunk/contrib/launchpad/testing/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/scala/ScalaScriptingTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/launchpad/testing/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/scala/ScalaScriptingTest.java?rev=803520&r1=803519&r2=803520&view=diff
==============================================================================
--- sling/trunk/contrib/launchpad/testing/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/scala/ScalaScriptingTest.java (original)
+++ sling/trunk/contrib/launchpad/testing/src/test/java/org/apache/sling/launchpad/webapp/integrationtest/scala/ScalaScriptingTest.java Wed Aug 12 14:10:00 2009
@@ -16,6 +16,9 @@
*/
package org.apache.sling.launchpad.webapp.integrationtest.scala;
+import java.util.HashMap;
+import java.util.Map;
+
import org.apache.sling.commons.testing.integration.HttpTestBase;
import org.apache.sling.servlets.post.SlingPostConstants;
@@ -29,6 +32,15 @@
final String testRootPath = HTTP_BASE_URL + "/" + getClass().getSimpleName() + "/" + System.currentTimeMillis();
testRootUrl = testClient.createNode(testRootPath + SlingPostConstants.DEFAULT_CREATE_SUFFIX, null);
+
+ Map<String, String> nodeProperties = new HashMap<String, String>();
+ nodeProperties.put("jcr:primaryType", "nt:folder");
+
+ // fixme: this is a workaround for the post servlet returning 200 instead of 302
+ // if the path is /var/classes
+ nodeProperties.put(":redirect", "*.json");
+
+ testClient.createNode(HTTP_BASE_URL + "/var/classes", nodeProperties);
testNode = new TestNode(testRootPath + "/test", null);
}
Modified: sling/trunk/contrib/launchpad/testing/src/test/resources/integration-test/scala/rendering-test.scs
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/launchpad/testing/src/test/resources/integration-test/scala/rendering-test.scs?rev=803520&r1=803519&r2=803520&view=diff
==============================================================================
--- sling/trunk/contrib/launchpad/testing/src/test/resources/integration-test/scala/rendering-test.scs (original)
+++ sling/trunk/contrib/launchpad/testing/src/test/resources/integration-test/scala/rendering-test.scs Wed Aug 12 14:10:00 2009
@@ -16,13 +16,18 @@
* specific language governing permissions and limitations
* under the License.
*/
+package nt.unstructured {
+ object html {
+ import html_Bindings._
-val html = <h1>Scala rendering result</h1>
-<p>
- text value using resource.adaptTo:{ resource.adaptTo(classOf[javax.jcr.Node]).getProperty("text").getValue().getString() }
-</p>
-<p>
- text value using currentNode:{ currentNode.getProperty("text").getValue().getString() }
-</p>;
-
-println(html)
+ println {
+ <h1>Scala rendering result</h1>
+ <p>
+ text value using resource.adaptTo:{ resource.adaptTo(classOf[javax.jcr.Node]).getProperty("text").getValue().getString() }
+ </p>
+ <p>
+ text value using currentNode:{ currentNode.getProperty("text").getValue().getString() }
+ </p>;
+ }
+ }
+}
\ No newline at end of file
Modified: sling/trunk/contrib/scripting/scala/engine/README.txt
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/scala/engine/README.txt?rev=803520&r1=803519&r2=803520&view=diff
==============================================================================
--- sling/trunk/contrib/scripting/scala/engine/README.txt (original)
+++ sling/trunk/contrib/scripting/scala/engine/README.txt Wed Aug 12 14:10:00 2009
@@ -19,7 +19,7 @@
the Apache Software Foundation. If you have Subversion installed,
you can checkout the latest source using the following command:
- svn checkout http://svn.apache.org/repos/asf/sling/trunk/scripting/velocity
+ svn checkout http://svn.apache.org/repos/asf/sling/trunk/contrib/scripting/scala/engine/
See the Subversion documentation for other source control features.
Modified: sling/trunk/contrib/scripting/scala/engine/src/main/java/org/apache/sling/scripting/scala/engine/ScalaScriptEngine.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/scala/engine/src/main/java/org/apache/sling/scripting/scala/engine/ScalaScriptEngine.java?rev=803520&r1=803519&r2=803520&view=diff
==============================================================================
--- sling/trunk/contrib/scripting/scala/engine/src/main/java/org/apache/sling/scripting/scala/engine/ScalaScriptEngine.java (original)
+++ sling/trunk/contrib/scripting/scala/engine/src/main/java/org/apache/sling/scripting/scala/engine/ScalaScriptEngine.java Wed Aug 12 14:10:00 2009
@@ -48,11 +48,14 @@
import org.apache.sling.scripting.scala.interpreter.ScalaInterpreter;
import org.apache.sling.scripting.scala.interpreter.JcrFS.JcrNode;
import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import scala.tools.nsc.io.AbstractFile;
import scala.tools.nsc.reporters.Reporter;
public class ScalaScriptEngine extends AbstractSlingScriptEngine {
+ private static final Logger log = LoggerFactory.getLogger(ScalaScriptEngine.class);
+
public static final String NL = System.getProperty("line.separator");
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
@@ -73,8 +76,24 @@
TypeHints typeHints = new TypeHints(bindings);
final ScalaBindings scalaBindings = new ScalaBindings();
- for (Object name : bindings.keySet()) {
- scalaBindings.put((String) name, bindings.get(name), typeHints.get(name));
+ for (String name : bindings.keySet()) {
+ if (name == null) {
+ log.debug("Bindings contain null key. skipping");
+ continue;
+ }
+
+ Object value = bindings.get(name);
+ if (value == null) {
+ log.debug("{} has null value. skipping", name);
+ continue;
+ }
+ Class<?> typeHint = typeHints.getType(name);
+ if (typeHint == null) {
+ log.debug("{} has no type hint. skipping");
+ continue;
+ }
+
+ scalaBindings.put(makeIdentifier(name), value, typeHint);
}
final JcrNode script = getScriptSource(scriptHelper);
@@ -179,12 +198,12 @@
}
private static String getScriptName(SlingScriptHelper scriptHelper) {
- String path = scriptHelper.getScript().getScriptResource().getPath();
+ String path = getRelativePath(scriptHelper.getScript().getScriptResource());
if (path.endsWith(".scala")) {
path = path.substring(0, path.length() - 6);
}
- if (path.startsWith("/")) {
- path = path.substring(1);
+ else if (path.endsWith(".scs")) {
+ path = path.substring(0, path.length() - 4);
}
String[] parts = path.split("/");
@@ -197,6 +216,23 @@
return scriptName.toString();
}
+ private static String getRelativePath(Resource scriptResource) {
+ String path = scriptResource.getPath();
+ String[] searchPath = scriptResource.getResourceResolver().getSearchPath();
+
+ for (int i = 0; i < searchPath.length; i++) {
+ if (path.startsWith(searchPath[i])) {
+ path = path.substring(searchPath[i].length());
+ break;
+ }
+ }
+
+ if (path.startsWith("/")) {
+ path = path.substring(1);
+ }
+ return path;
+ }
+
/**
* Converts the given identifier to a legal Java/Scala identifier
* @param identifier Identifier to convert
Modified: sling/trunk/contrib/scripting/scala/engine/src/main/java/org/apache/sling/scripting/scala/engine/ScalaScriptEngineFactory.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/scala/engine/src/main/java/org/apache/sling/scripting/scala/engine/ScalaScriptEngineFactory.java?rev=803520&r1=803519&r2=803520&view=diff
==============================================================================
--- sling/trunk/contrib/scripting/scala/engine/src/main/java/org/apache/sling/scripting/scala/engine/ScalaScriptEngineFactory.java (original)
+++ sling/trunk/contrib/scripting/scala/engine/src/main/java/org/apache/sling/scripting/scala/engine/ScalaScriptEngineFactory.java Wed Aug 12 14:10:00 2009
@@ -160,17 +160,23 @@
AbstractFile[] bundleFs = new AbstractFile[bundles.length];
for (int k = 0; k < bundles.length; k++) {
URL url = bundles[k].getResource("/");
- if ("file".equals(url.getProtocol())) {
- try {
- bundleFs[k] = new PlainFile(new File(url.toURI()));
+ if (url == null) {
+ url = bundles[k].getResource("");
+ }
+
+ if (url != null) { // FIXME: log null values
+ if ("file".equals(url.getProtocol())) {
+ try {
+ bundleFs[k] = new PlainFile(new File(url.toURI()));
+ }
+ catch (URISyntaxException e) {
+ throw initCause(new IllegalArgumentException("Can't determine url of bundle " + k), e);
+ }
}
- catch (URISyntaxException e) {
- throw initCause(new IllegalArgumentException("Can't determine url of bundle " + k), e);
+ else {
+ bundleFs[k] = BundleFS.create(bundles[k]);
}
}
- else {
- bundleFs[k] = BundleFS.create(bundles[k]);
- }
}
return bundleFs;
}
Modified: sling/trunk/contrib/scripting/scala/engine/src/test/java/org/apache/sling/scripting/scala/ScalaScriptEngineTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/scala/engine/src/test/java/org/apache/sling/scripting/scala/ScalaScriptEngineTest.java?rev=803520&r1=803519&r2=803520&view=diff
==============================================================================
--- sling/trunk/contrib/scripting/scala/engine/src/test/java/org/apache/sling/scripting/scala/ScalaScriptEngineTest.java (original)
+++ sling/trunk/contrib/scripting/scala/engine/src/test/java/org/apache/sling/scripting/scala/ScalaScriptEngineTest.java Wed Aug 12 14:10:00 2009
@@ -36,6 +36,7 @@
import scala.tools.nsc.io.AbstractFile;
public class ScalaScriptEngineTest extends ScalaTestBase {
+ private final static String SCRIPT_NAME = "a.Testi";
@Override
protected void setUp() throws Exception {
@@ -43,7 +44,7 @@
}
public void testEvalString() throws ScriptException, InvocationTargetException {
- String code = "print(1 + 2)";
+ String code = "package a { object Testi { print(1 + 2)}}";
assertEquals("3", evalScala(code));
}
@@ -66,11 +67,19 @@
assertTrue("Expecting InvocationTargetException", false);
}
catch (ScriptException e) {
- Throwable inner = e.getCause();
- assertEquals("Inner exception is InterpreterException", InterpreterException.class, inner.getClass());
- inner = inner.getCause();
- assertEquals("Inner inner exception is java.lang.Error", Error.class, inner.getClass());
- assertEquals("Inner inner exception message is \"" + err + "\"", err, inner.getMessage());
+ // expected
+ }
+ }
+
+ public void testDefaultPackage() {
+ String code = "package a { object Testi { print(1 + 2)}}";
+ try {
+ evalScala("Testi", code, new ScalaBindings());
+ }
+ catch (ScriptException e) {
+ Throwable cause = e.getCause();
+ assertNotNull("Script exception has a cause", cause);
+ assertTrue("Script exception cause is InterpreterException", cause instanceof InterpreterException);
}
}
@@ -82,7 +91,7 @@
public void testNodeAccess() throws RepositoryException, NamingException, ScriptException, InvocationTargetException {
Node n = getTestRootNode();
- String code = "print(n.getPath)";
+ String code = "package a { import Testi_Bindings._; object Testi { print(n.getPath)}}";
ScalaBindings bindings = new ScalaBindings();
bindings.put("n", n, Node.class);
assertEquals(n.getPath(), evalScala(code, bindings));
@@ -99,12 +108,14 @@
AbstractFile src = srcDir.fileNamed("Testi");
PrintWriter writer = new PrintWriter(src.output());
- writer.print("print(msg + \": \" + time)");
+ writer.print("package a { import Testi_Bindings._; object Testi { print(msg + \": \" + time)}}");
writer.close();
- for(String name : new String[]{"org.apache.sling.scripting.scala.interpreter.Testi", "Testi"}) {
- evalScala(name, src, bindings);
- }
+ evalScala(SCRIPT_NAME, src, bindings);
+ }
+
+ protected String createScriptName() {
+ return SCRIPT_NAME;
}
}
Modified: sling/trunk/contrib/scripting/scala/engine/src/test/resources/scripts/simple.scs
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/scala/engine/src/test/resources/scripts/simple.scs?rev=803520&r1=803519&r2=803520&view=diff
==============================================================================
--- sling/trunk/contrib/scripting/scala/engine/src/test/resources/scripts/simple.scs (original)
+++ sling/trunk/contrib/scripting/scala/engine/src/test/resources/scripts/simple.scs Wed Aug 12 14:10:00 2009
@@ -1 +1,7 @@
-print(1 + 2)
\ No newline at end of file
+package a {
+ import Testi_Bindings._
+
+ object Testi {
+ print(1 + 2)
+ }
+}
\ No newline at end of file
Propchange: sling/trunk/contrib/scripting/scala/interpreter/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Wed Aug 12 14:10:00 2009
@@ -2,3 +2,4 @@
.project
target
.settings
+a
Modified: sling/trunk/contrib/scripting/scala/interpreter/README.txt
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/scala/interpreter/README.txt?rev=803520&r1=803519&r2=803520&view=diff
==============================================================================
--- sling/trunk/contrib/scripting/scala/interpreter/README.txt (original)
+++ sling/trunk/contrib/scripting/scala/interpreter/README.txt Wed Aug 12 14:10:00 2009
@@ -25,7 +25,7 @@
the Apache Software Foundation. If you have Subversion installed,
you can checkout the latest source using the following command:
- svn checkout http://svn.apache.org/repos/asf/sling/trunk/scripting/velocity
+ svn checkout http://svn.apache.org/repos/asf/sling/trunk/contrib/scripting/scala/interpreter/
See the Subversion documentation for other source control features.
Modified: sling/trunk/contrib/scripting/scala/interpreter/src/main/scala/org/apache/sling/scripting/scala/interpreter/ScalaInterpreter.scala
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/scala/interpreter/src/main/scala/org/apache/sling/scripting/scala/interpreter/ScalaInterpreter.scala?rev=803520&r1=803519&r2=803520&view=diff
==============================================================================
--- sling/trunk/contrib/scripting/scala/interpreter/src/main/scala/org/apache/sling/scripting/scala/interpreter/ScalaInterpreter.scala (original)
+++ sling/trunk/contrib/scripting/scala/interpreter/src/main/scala/org/apache/sling/scripting/scala/interpreter/ScalaInterpreter.scala Wed Aug 12 14:10:00 2009
@@ -104,36 +104,40 @@
* @param code source code of the script
* @param bindings bindings to be passed to the script
* @return a valid Scala source
+ * @throws InterpreterException
*/
+ @throws(classOf[InterpreterException])
protected def preProcess(name: String, code: String, bindings: Bindings): String = {
def bind(a: (String, Argument[_])) =
- "val " + a._1 + " = bindings.getValue(\"" + a._1 + "\").asInstanceOf[" + a._2.getType.getName + "]"
+ "lazy val " + a._1 + " = bindings.getValue(\"" + a._1 + "\").asInstanceOf[" + a._2.getType.getName + "]"
val compounds = packetize(name)
def packageDeclaration =
if (compounds.size > 1) compounds.init.mkString("package ", ".", "") + NL
- else ""
+ else throw new InterpreterException("Default package not allowed: " + name)
def className = compounds.last
- packageDeclaration +
- "object " + className + " {" + NL +
- " def main(bindings: org.apache.sling.scripting.scala.interpreter.Bindings," + NL +
- " stdIn: java.io.InputStream," + NL +
- " stdOut: java.io.OutputStream) {" + NL +
- " def run() {" + NL +
- bindings.map(bind).mkString("", NL, NL) +
- code + "" + NL +
- " return" + NL +
- " }" + NL +
- " Console.withIn(stdIn) {" + NL +
- " Console.withOut(stdOut) {" + NL +
- " run" + NL +
- " stdOut.flush" + NL +
+ code + NL +
+ packageDeclaration + " {" + NL +
+ " object " + className + "_Bindings { " + NL +
+ " var bindings: org.apache.sling.scripting.scala.interpreter.Bindings = null" + NL +
+ bindings.map(bind).mkString("", NL, NL) +
+ " } " + NL +
+ " object " + className + "Runner {" + NL +
+ " def main(bindings: org.apache.sling.scripting.scala.interpreter.Bindings," + NL +
+ " stdIn: java.io.InputStream," + NL +
+ " stdOut: java.io.OutputStream) {" + NL +
+ className + "_Bindings.bindings = bindings" + NL +
+ " Console.withIn(stdIn) {" + NL +
+ " Console.withOut(stdOut) {" + NL +
+ className + NL +
+ " stdOut.flush" + NL +
+ " }" + NL +
" }" + NL +
" }" + NL +
- " }" + NL +
+ " }" + NL +
"}" + NL
}
@@ -319,7 +323,7 @@
def execute(name: String, bindings: Bindings, in: Option[InputStream], out: Option[OutputStream]): Reporter = {
try {
val classLoader = new AbstractFileClassLoader(compiler.genJVM.outputDir, parentClassLoader)
- val script = Class.forName(name, true, classLoader)
+ val script = Class.forName(name + "Runner", true, classLoader)
val initMethod = (script
.getDeclaredMethods
.toList
Modified: sling/trunk/contrib/scripting/scala/interpreter/src/test/scala/org/apache/sling/scripting/scala/interpreter/InterpreterTest.scala
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/scala/interpreter/src/test/scala/org/apache/sling/scripting/scala/interpreter/InterpreterTest.scala?rev=803520&r1=803519&r2=803520&view=diff
==============================================================================
--- sling/trunk/contrib/scripting/scala/interpreter/src/test/scala/org/apache/sling/scripting/scala/interpreter/InterpreterTest.scala (original)
+++ sling/trunk/contrib/scripting/scala/interpreter/src/test/scala/org/apache/sling/scripting/scala/interpreter/InterpreterTest.scala Wed Aug 12 14:10:00 2009
@@ -17,7 +17,7 @@
import junit.framework.TestCase
import junit.framework.Assert.{assertEquals, assertFalse}
import scala.tools.nsc.Settings
-import scala.tools.nsc.io.VirtualDirectory
+import scala.tools.nsc.io.PlainFile
import scala.tools.nsc.reporters.ConsoleReporter
import org.apache.sling.scripting.scala.Utils.valueOrElse
import java.io.PrintWriter
@@ -65,7 +65,8 @@
}
def testScalaInterpreter {
- val outdir = new VirtualDirectory("outdir", None)
+ // xxx: Virtual file seems not to work here
+ val outdir = new PlainFile(new java.io.File("."))
val interpreter = new ScalaInterpreter(settings, reporter(settings), outdir)
val bindings = new ScalaBindings
@@ -73,17 +74,15 @@
bindings.put("msg", "Hello world", classOf[String])
bindings.put("time", time, classOf[java.util.Date])
- val code = "print(msg + \": \" + time)"
+ val code = "package a { import Testi_Bindings._; object Testi { print(msg + \": \" + time)}}"
+ val name = "a.Testi"
- // todo fix: uncomment when trac-1618 is fixed
- for (name <- /*"org.apache.sling.scripting.scala.interpreter.Testi"::*/"Testi"::Nil) {
- val out = new java.io.ByteArrayOutputStream
- val result = interpreter.interprete(name, code, bindings, None, Some(out))
- assertFalse(result.hasErrors)
- assertEquals("Hello world: " + time, out.toString)
- }
+ val out = new java.io.ByteArrayOutputStream
+ val result = interpreter.interprete(name, code, bindings, None, Some(out))
+ assertFalse(result.hasErrors)
+ assertEquals("Hello world: " + time, out.toString)
}
-
+
def testScalaInterpreterWithJcr {
val appNode = testRoot.addNode("app", "nt:folder")
testRoot.save()
@@ -98,19 +97,20 @@
bindings.put("msg", "Hello world", classOf[String])
bindings.put("time", time, classOf[java.util.Date])
+ val code = "package a { import Testi_Bindings._; object Testi { print(msg + \": \" + time)}}"
+ val name = "a.Testi"
+
val src = srcDir.fileNamed("Testi")
val writer = new PrintWriter(src.output)
- writer.print("print(msg + \": \" + time)")
+ writer.print(code)
writer.close
- for (name <- "org.apache.sling.scripting.scala.interpreter.Testi"::"Testi"::Nil) {
- val out = new java.io.ByteArrayOutputStream
- val result = interpreter.interprete(name, src, bindings, None, Some(out))
- assertFalse(result.hasErrors)
- assertEquals("Hello world: " + time, out.toString)
- }
+ val out = new java.io.ByteArrayOutputStream
+ val result = interpreter.interprete(name, src, bindings, None, Some(out))
+ assertFalse(result.hasErrors)
+ assertEquals("Hello world: " + time, out.toString)
}
-
+
def testCompileExecute {
val appNode = testRoot.addNode("app", "nt:folder")
testRoot.save()
@@ -125,21 +125,22 @@
bindings.put("msg", "Hello world", classOf[String])
bindings.put("time", time, classOf[java.util.Date])
+ val code = "package a { import Testi_Bindings._; object Testi { print(msg + \": \" + time)}}"
+ val name = "a.Testi"
+
val src = srcDir.fileNamed("Testi")
val writer = new PrintWriter(src.output)
- writer.print("print(msg + \": \" + time)")
+ writer.print(code)
writer.close
- for (name <- "org.apache.sling.scripting.scala.interpreter.Testi"::"Testi"::Nil) {
- val out = new java.io.ByteArrayOutputStream
- var result = interpreter.compile(name, src, bindings)
- assertFalse(result.hasErrors)
+ val out = new java.io.ByteArrayOutputStream
+ var result = interpreter.compile(name, src, bindings)
+ assertFalse(result.hasErrors)
- result = interpreter.execute(name, bindings, None, Some(out))
- assertFalse(result.hasErrors)
+ result = interpreter.execute(name, bindings, None, Some(out))
+ assertFalse(result.hasErrors)
- assertEquals("Hello world: " + time, out.toString)
- }
+ assertEquals("Hello world: " + time, out.toString)
}
}