From 8c40aa58afff5003b71525ad0770b35db5e54fff Mon Sep 17 00:00:00 2001 From: James Strachan Date: Wed, 4 Jan 2012 12:59:37 +0000 Subject: [PATCH] added working test casres for groovy style markup builders --- stdlib/ktSrc/JavaUtilMap.kt | 9 ++ templatelib/src/TemplateHtml.kt | 80 +++++++++++++++-- templatelib/test/TemplateHtmlTest.kt | 127 +++++++++++++++++---------- testlib/test/Test.kt | 11 +++ 4 files changed, 175 insertions(+), 52 deletions(-) diff --git a/stdlib/ktSrc/JavaUtilMap.kt b/stdlib/ktSrc/JavaUtilMap.kt index 4f303618309..b2749cb3060 100644 --- a/stdlib/ktSrc/JavaUtilMap.kt +++ b/stdlib/ktSrc/JavaUtilMap.kt @@ -1,6 +1,7 @@ package std.util import java.util.Map as JMap +import java.util.Map.Entry as JEntry // Map APIs @@ -12,6 +13,14 @@ val JMap<*,*>.size : Int val JMap<*,*>.empty : Boolean get() = isEmpty() +/** Returns the key of the entry */ +val JEntry.key : K + get() = getKey() + +/** Returns the value of the entry */ +val JEntry.value : V + get() = getValue() + /** Returns the value for the given key or returns the result of the defaultValue function if there was no entry for the given key */ inline fun java.util.Map.getOrElse(key: K, defaultValue: ()-> V) : V { val current = this.get(key) diff --git a/templatelib/src/TemplateHtml.kt b/templatelib/src/TemplateHtml.kt index bd870fd3e07..955070382ea 100644 --- a/templatelib/src/TemplateHtml.kt +++ b/templatelib/src/TemplateHtml.kt @@ -12,9 +12,16 @@ trait Factory { fun create() : T } -abstract class Element +abstract class Element { + abstract fun appendTo(builder: StringBuilder): Unit -class TextElement(val text : String) : Element +} + +class TextElement(val text : String) : Element { + override fun appendTo(builder: StringBuilder) { + builder.append(text) + } +} abstract class Tag(val name : String) : Element { val children = ArrayList() @@ -22,11 +29,46 @@ abstract class Tag(val name : String) : Element { protected fun initTag(init : T.()-> Unit) : T where class object T : Factory { - val tag = T.create() + val tag = try { + T.create() + } catch (e: NullPointerException) { + val typeName = javaClass.getName() + throw UnsupportedOperationException("No class object create() method for $typeName") + } tag.init() children.add(tag) return tag } + + override fun appendTo(builder: StringBuilder): Unit { + builder.append("<") + builder.append(name) + if (!attributes.isEmpty()) { + for (e in attributes.entrySet()) { + if (e != null) { + builder.append(" ${e.key}=\"${e.value}\"") + } + } + } + if (children.isEmpty()) { + builder.append("/>") + } else { + builder.append(">") + for (c in children) { + c.appendTo(builder) + } + builder.append("<") + builder.append(name) + builder.append(">") + } + + } + + fun toString(): String { + val builder = StringBuilder() + appendTo(builder) + return builder.toString().sure() + } } abstract class TagWithText(name : String) : Tag(name) { @@ -53,7 +95,11 @@ class Head() : TagWithText("head") { fun title(init : Title.()-> Unit) = initTag(init) } -class Title() : TagWithText("title") +class Title() : TagWithText("title") { + class object : Factory { + override fun create() = Title() + } +} abstract class BodyTag(name : String) : TagWithText(name) { } @@ -68,20 +114,38 @@ class Body() : BodyTag("body") { fun h1(init : H1.()-> Unit) = initTag(init) fun a(href : String) { - a(href) {} + a(href, {}) } fun a(href : String, init : A.()-> Unit) { val a = initTag(init) a.href = href + a.attributes.put("href", href) } } -class B() : BodyTag("b") -class P() : BodyTag("p") -class H1() : BodyTag("h1") +class B() : BodyTag("b") { + class object : Factory<B> { + override fun create() = B() + } +} + +class P() : BodyTag("p") { + class object : Factory<P> { + override fun create() = P() + } +} +class H1() : BodyTag("h1") { + class object : Factory<H1> { + override fun create() = H1() + } +} class A() : BodyTag("a") { + class object : Factory<A> { + override fun create() = A() + } + var href: String? = null /* TODO this doesn't compile diff --git a/templatelib/test/TemplateHtmlTest.kt b/templatelib/test/TemplateHtmlTest.kt index 3367087c072..2191dffd246 100644 --- a/templatelib/test/TemplateHtmlTest.kt +++ b/templatelib/test/TemplateHtmlTest.kt @@ -8,60 +8,89 @@ import std.util.* import std.test.* import java.util.* -/* - TODO generates compiler error - see: http://youtrack.jetbrains.net/issue/KT-866 - val justBody = body { - +"Hello world" - } + +"Hello world" +} fun result(args : List<String>) = - html { - head { - title {+"XML encoding with Kotlin"} - } - body { - h1 {+"XML encoding with Kotlin"} - p {+"this format can be used as an alternative markup to XML"} +html { + head { + title {+"XML encoding with Kotlin"} + } + body { + h1 {+"XML encoding with Kotlin"} + p {+"this format can be used as an alternative markup to XML"} - // an element with attributes and text content + // an element with attributes and text content + a(href = "http://jetbrains.com/kotlin") {+"Kotlin"} + + // mixed content + p { + +"This is some" + b {+"mixed"} + +"text. For more see the" a(href = "http://jetbrains.com/kotlin") {+"Kotlin"} + +"project" + } + p {+"some text"} - // mixed content - p { - +"This is some" - b {+"mixed"} - +"text. For more see the" - a(href = "http://jetbrains.com/kotlin") {+"Kotlin"} - +"project" - } - p {+"some text"} - - // content generated by - p { - for (arg in args) - +arg - } + // content generated by + p { + for (arg in args) + +arg } } -*/ +} + +/** Create a bad element which doesn't have a class object create() method */ +class BadElem() : BodyTag("bad") { +} + +/** Creates a bad body to test out badly defined elements */ +class BadBody() : BodyTag("badBody") { + fun bad(init : BadElem.()-> Unit) = initTag(init) +} + +fun badBody(init: BadBody.()-> Unit): BadBody { + val elem = BadBody() + elem.init() + return elem +} class TemplateHtmlTest() : TestSupport() { - fun testNoneCompileYet() { - } - -/* - TODO: compiler bug - see: http://youtrack.jetbrains.net/issue/KT-866 fun testJustBody() { - println(justBody) + assertEquals("<body>Hello world<body>", justBody.toString()) + } + + fun testEmbeddedSimpleBody() { + val e = body { + +"body with text" + } + assertEquals("<body>body with text<body>", e.toString()) + } + + fun testEmbeddedBodyWithNestedBold() { + val e = body { + b{ + +"this is bold" + } + } + assertEquals("<body><b>this is bold<b><body>", e.toString()) + } + + fun testEmbeddedBodyWithNestedLinks() { + val e = body { + a("http://jetbrains.com/kotlin") { + +"link text" + } + } + assertEquals("<body><a href=\"http://jetbrains.com/kotlin\">link text<a><body>", e.toString()) } fun testHtmlFunction() { - val text = result(arrayList("a", "b", "c")) - println(text) + val e = result(arrayList("a", "b", "c")) + assertEquals("""<html><head><title>XML encoding with Kotlin<title><head><body><h1>XML encoding with Kotlin<h1><p>this format can be used as an alternative markup to XML<p><a href="http://jetbrains.com/kotlin">Kotlin<a><b>mixed<b><a href="http://jetbrains.com/kotlin">Kotlin<a><p>This is sometext. For more see theproject<p><p>some text<p><p>abc<p><body><html>""", e.toString()) } fun testEmbeddedFunction() { @@ -69,11 +98,21 @@ class TemplateHtmlTest() : TestSupport() { head { title {+"XML encoding with Kotlin"} } - body { - a("http://jetbrains.com/kotlin") - } + body { + a("http://jetbrains.com/kotlin") + } + } + assertEquals("<html><head><title>XML encoding with Kotlin<title><head><body><a href=\"http://jetbrains.com/kotlin\"/><body><html>", e.toString()) + } + + fun testBadlyDefinedElement() { + failsWith<UnsupportedOperationException>{ + val e = badBody { + bad{ + +"Bad Element Text" + } + } + println("bad body: $e") } - println(e) } -*/ } \ No newline at end of file diff --git a/testlib/test/Test.kt b/testlib/test/Test.kt index d6e0a26e9c3..25620696712 100644 --- a/testlib/test/Test.kt +++ b/testlib/test/Test.kt @@ -102,6 +102,17 @@ fun fails(block: ()-> Any) { block() Assert.fail("Expected an exception to be thrown") } catch (e: Exception) { + println("Caught excepted exception: $e") + // OK + } +} + +fun <T: Exception> failsWith(block: ()-> Any) { + try { + block() + Assert.fail("Expected an exception to be thrown") + } catch (e: T) { + println("Caught excepted exception: $e") // OK } }