diff --git a/libraries/examples/browser-example-with-library/pom.xml b/libraries/examples/browser-example-with-library/pom.xml
index e617c0dc727..9711598e1f6 100644
--- a/libraries/examples/browser-example-with-library/pom.xml
+++ b/libraries/examples/browser-example-with-library/pom.xml
@@ -25,6 +25,12 @@
kotlin-js-library-example
${project.version}
+
+ org.jetbrains.kotlin
+ kotlin.test.jvm
+ ${project.version}
+ test
+
diff --git a/libraries/examples/browser-example/pom.xml b/libraries/examples/browser-example/pom.xml
index 311e84498d7..0e307f4a03c 100644
--- a/libraries/examples/browser-example/pom.xml
+++ b/libraries/examples/browser-example/pom.xml
@@ -20,6 +20,12 @@
kotlin-js-library
${project.version}
+
+ org.jetbrains.kotlin
+ kotlin.test.jvm
+ ${project.version}
+ test
+
diff --git a/libraries/kotlin.test/js/pom.xml b/libraries/kotlin.test/js/pom.xml
new file mode 100644
index 00000000000..e6a4e3213c6
--- /dev/null
+++ b/libraries/kotlin.test/js/pom.xml
@@ -0,0 +1,90 @@
+
+
+
+ kotlin.test
+ org.jetbrains.kotlin
+ 0.1-SNAPSHOT
+
+ 4.0.0
+
+ kotlin.test.js
+
+
+ src/main/kotlin
+ src/test/kotlin
+
+
+
+ maven-dependency-plugin
+ 2.10
+
+
+
+ unpack-dependencies
+
+ generate-test-resources
+
+ test
+ jar
+ ${project.basedir}/target/test-js
+ *.js
+ *.meta.js
+
+
+
+
+
+
+ com.github.eirslett
+ frontend-maven-plugin
+
+
+
+ install node and npm
+
+ none
+
+ install-node-and-npm
+
+
+
+
+
+
+ npm install
+
+ none
+
+ npm
+
+
+
+ install
+
+
+
+
+ javascript tests
+
+ karma
+
+
+ none
+
+ ${project.basedir}/src/test/karma/karma.conf.js
+
+
+
+
+
+ v0.12.7
+ 2.14.4
+
+ ${project.basedir}/src/test/karma
+
+
+
+
+
diff --git a/libraries/kotlin.test/js/src/test/karma/karma.conf.js b/libraries/kotlin.test/js/src/test/karma/karma.conf.js
new file mode 100644
index 00000000000..8c41577ab37
--- /dev/null
+++ b/libraries/kotlin.test/js/src/test/karma/karma.conf.js
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+module.exports = function (config) {
+ config.set({
+ frameworks: ['qunit'],
+ reporters: ['progress', 'junit'],
+ files: [
+ '../../../target/test-js/kotlin.js',
+ '../../../target/test-js/*.js',
+ '../../../target/classes/*.js'
+ ],
+ exclude: [],
+ port: 9876,
+ runnerPort: 9100,
+ colors: true,
+ autoWatch: false,
+ browsers: [
+ 'PhantomJS'
+ ],
+ captureTimeout: 5000,
+ //singleRun: false,
+ singleRun: true,
+ reportSlowerThan: 500,
+
+ junitReporter: {
+ outputFile: '../../../target/reports/test-results.xml',
+ suite: ''
+ },
+ preprocessors: {
+ '**/*.js': ['sourcemap']
+ }
+ }
+ )
+};
\ No newline at end of file
diff --git a/libraries/kotlin.test/js/src/test/karma/package.json b/libraries/kotlin.test/js/src/test/karma/package.json
new file mode 100644
index 00000000000..0e19fa9b39a
--- /dev/null
+++ b/libraries/kotlin.test/js/src/test/karma/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "karma-tests",
+ "version": "0.0.0",
+ "devDependencies": {
+ "karma": "*",
+ "qunitjs": "*",
+ "karma-qunit": "*",
+ "karma-junit-reporter": "~0.2",
+ "karma-sourcemap-loader": "~0.3",
+ "karma-teamcity-reporter": "*",
+ "karma-phantomjs-launcher": "*",
+ "phantomjs": "1.9.13"
+ }
+}
\ No newline at end of file
diff --git a/libraries/kotlin.test/jvm/pom.xml b/libraries/kotlin.test/jvm/pom.xml
new file mode 100644
index 00000000000..193b8736020
--- /dev/null
+++ b/libraries/kotlin.test/jvm/pom.xml
@@ -0,0 +1,107 @@
+
+
+
+ kotlin.test
+ org.jetbrains.kotlin
+ 0.1-SNAPSHOT
+
+ 4.0.0
+
+ kotlin.test.jvm
+
+
+
+ junit
+ junit
+ (4,5)
+
+
+ org.jetbrains.kotlin
+ kotlin.test.shared
+ ${project.version}
+
+
+
+ org.jetbrains.kotlin
+ kotlin-js-library
+
+
+
+
+
+
+ src/main/kotlin
+ src/test/kotlin
+
+
+
+ maven-assembly-plugin
+
+
+ package
+
+ single
+
+
+
+
+
+ src/assembly/sources.xml
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 2.6
+
+ true
+ ${project.build.outputDirectory}
+
+
+
+ ${user.name}
+
+ JetBrains s.r.o.
+ ${project.version}
+
+
+
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ ${project.basedir}/src/main/kotlin
+ ${project.basedir}/src/main/kotlin.jvm
+
+
+
+
+
+ test-compile
+ test-compile
+
+ test-compile
+
+
+
+ ${project.basedir}/src/test/kotlin
+ ${project.basedir}/src/test/kotlin.jvm
+
+
+
+
+
+
+
+
diff --git a/libraries/kotlin.test/jvm/src/assembly/sources.xml b/libraries/kotlin.test/jvm/src/assembly/sources.xml
new file mode 100644
index 00000000000..0b85e85e290
--- /dev/null
+++ b/libraries/kotlin.test/jvm/src/assembly/sources.xml
@@ -0,0 +1,46 @@
+
+ sources
+
+ jar
+
+
+ /
+
+
+
+ true
+ unix
+
+ ../shared/src/main/kotlin
+
+
+
+
+ **/*.kt
+
+
+
+ true
+ unix
+
+ ../shared/src/main/kotlin.jvm
+
+
+
+ **/*.kt
+
+
+
+ true
+ unix
+
+ src/main/kotlin
+
+
+
+ **/*.kt
+
+
+
+
\ No newline at end of file
diff --git a/libraries/kotlin.test/jvm/src/main/kotlin/JUnitSupport.kt b/libraries/kotlin.test/jvm/src/main/kotlin/JUnitSupport.kt
new file mode 100644
index 00000000000..68e78497a2e
--- /dev/null
+++ b/libraries/kotlin.test/jvm/src/main/kotlin/JUnitSupport.kt
@@ -0,0 +1,41 @@
+package kotlin.test.junit
+
+import org.junit.*
+import kotlin.test.*
+
+class JUnitContributor : AsserterContributor {
+ override fun contribute(): Asserter? {
+ for (stackFrame in currentStackTrace()) {
+ @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+ val className = stackFrame.className as java.lang.String
+
+ if (className.startsWith("org.junit.") || className.startsWith("junit.")) {
+ return JUnitAsserter
+ }
+ }
+
+ return null
+ }
+}
+
+object JUnitAsserter : Asserter {
+ override fun assertEquals(message : String?, expected : Any?, actual : Any?) {
+ Assert.assertEquals(message, expected, actual)
+ }
+
+ override fun assertNotEquals(message : String?, illegal : Any?, actual : Any?) {
+ Assert.assertNotEquals(message, illegal, actual)
+ }
+
+ override fun assertNotNull(message : String?, actual : Any?) {
+ Assert.assertNotNull(message ?: "actual value is null", actual)
+ }
+
+ override fun assertNull(message : String?, actual : Any?) {
+ Assert.assertNull(message ?: "actual value is not null", actual)
+ }
+
+ override fun fail(message : String?) {
+ Assert.fail(message)
+ }
+}
diff --git a/libraries/kotlin.test/jvm/src/main/resources/META-INF/services/kotlin.test.AsserterContributor b/libraries/kotlin.test/jvm/src/main/resources/META-INF/services/kotlin.test.AsserterContributor
new file mode 100644
index 00000000000..e32ef6c66d9
--- /dev/null
+++ b/libraries/kotlin.test/jvm/src/main/resources/META-INF/services/kotlin.test.AsserterContributor
@@ -0,0 +1 @@
+kotlin.test.junit.JUnitContributor
\ No newline at end of file
diff --git a/libraries/kotlin.test/jvm/src/test/kotlin/JUnitContributorTest.kt b/libraries/kotlin.test/jvm/src/test/kotlin/JUnitContributorTest.kt
new file mode 100644
index 00000000000..a9c04819ed6
--- /dev/null
+++ b/libraries/kotlin.test/jvm/src/test/kotlin/JUnitContributorTest.kt
@@ -0,0 +1,27 @@
+package kotlinx.testing.tests
+
+import org.junit.*
+import java.util.concurrent.*
+
+class JUnitContributorTest {
+ @Test
+ fun smokeTest() {
+ Assert.assertEquals("JUnitAsserter", asserter.`class`.simpleName)
+ }
+
+ @Test
+ fun `should fail to contribute if it was run outside of junit`() {
+ @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+ val q = ArrayBlockingQueue(1)
+
+ Thread {
+ q.put(asserter)
+ }.start()
+
+ Assert.assertEquals("DefaultAsserter", q.take().`class`.simpleName)
+ }
+
+ @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+ private val asserter: java.lang.Object
+ get() = Class.forName("kotlin.test.TestAssertionsKt").getMethod("getAsserter").invoke(null) as java.lang.Object
+}
diff --git a/libraries/kotlin.test/pom.xml b/libraries/kotlin.test/pom.xml
new file mode 100644
index 00000000000..ca4e10db239
--- /dev/null
+++ b/libraries/kotlin.test/pom.xml
@@ -0,0 +1,114 @@
+
+
+ 4.0.0
+
+
+ org.jetbrains.kotlin
+ kotlin-project
+ 0.1-SNAPSHOT
+
+
+ kotlin.test
+ pom
+
+
+ shared
+ jvm
+
+
+
+
+
+ Sergey Mashkov
+ JetBrains s.r.o.
+
+ developer
+
+
+
+
+
+
+
+
+
+
+ org.jetbrains.kotlin
+ kotlin-runtime
+ ${project.version}
+
+
+ org.jetbrains.kotlin
+ kotlin-stdlib
+ ${project.version}
+
+
+ org.jetbrains.kotlin
+ kotlin-reflect
+ ${project.version}
+
+
+ org.jetbrains.kotlin
+ kotlin-js-library
+ ${project.version}
+
+
+ junit
+ junit
+ 4.12
+
+
+
+
+
+
+
+
+ maven-resources-plugin
+ 2.7
+
+
+
+ maven-assembly-plugin
+ 2.6
+
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+ ${project.version}
+
+
+
+ maven-surefire-plugin
+ 2.18.1
+
+ 2C
+ true
+
+
+
+
+ com.github.eirslett
+ frontend-maven-plugin
+ 0.0.26
+
+
+
+
+
+
+
+ kotlin-jvm
+
+ true
+
+
+
+ kotlin-js
+
+ false
+
+
+
+
diff --git a/libraries/kotlin.test/shared/pom.xml b/libraries/kotlin.test/shared/pom.xml
new file mode 100644
index 00000000000..72f58fc2d1b
--- /dev/null
+++ b/libraries/kotlin.test/shared/pom.xml
@@ -0,0 +1,148 @@
+
+
+
+ kotlin.test
+ org.jetbrains.kotlin
+ 0.1-SNAPSHOT
+
+ 4.0.0
+
+ kotlin.test.shared
+
+
+ src/main/kotlin
+ src/test/kotlin
+
+
+
+ maven-assembly-plugin
+
+
+ package
+
+ single
+
+
+
+
+
+ src/assembly/sources.xml
+
+
+
+
+
+
+
+
+ kotlin-js
+
+ false
+
+
+
+
+ org.jetbrains.kotlin
+ kotlin-js-library
+
+
+
+
+
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+
+
+
+ test-compile-js
+ test-compile
+
+ test-js
+
+
+
+ ${project.basedir}/src/test/kotlin
+ ${project.basedir}/src/test/kotlin.js
+
+
+
+
+
+ js-compile
+ compile
+
+ js
+
+
+ ${project.build.outputDirectory}/${project.artifactId}.js
+
+ ${project.basedir}/src/main/kotlin
+ ${project.basedir}/src/main/kotlin.js
+
+
+
+
+
+
+
+
+
+ kotlin-jvm
+
+ true
+
+
+
+
+ org.jetbrains.kotlin
+ kotlin-runtime
+
+
+ junit
+ junit
+
+
+
+
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ ${project.basedir}/src/main/kotlin
+ ${project.basedir}/src/main/kotlin.jvm
+
+
+
+
+
+ test-compile
+ test-compile
+
+ test-compile
+
+
+
+ ${project.basedir}/src/test/kotlin
+ ${project.basedir}/src/test/kotlin.jvm
+
+
+
+
+
+
+
+
+
+
diff --git a/libraries/kotlin.test/shared/src/assembly/sources.xml b/libraries/kotlin.test/shared/src/assembly/sources.xml
new file mode 100644
index 00000000000..09920bce308
--- /dev/null
+++ b/libraries/kotlin.test/shared/src/assembly/sources.xml
@@ -0,0 +1,46 @@
+
+ sources
+
+ jar
+
+
+ /
+
+
+
+ true
+ unix
+
+ src/main/kotlin
+
+
+
+
+ **/*.kt
+
+
+
+ true
+ unix
+
+ src/main/kotlin.jvm
+
+
+
+ **/*.kt
+
+
+
+ true
+ unix
+
+ src/main/kotlin.js
+
+
+
+ **/*.kt
+
+
+
+
\ No newline at end of file
diff --git a/libraries/kotlin.test/shared/src/main/kotlin.jvm/kotlin/test/AsserterLookup.kt b/libraries/kotlin.test/shared/src/main/kotlin.jvm/kotlin/test/AsserterLookup.kt
new file mode 100644
index 00000000000..961c00404e5
--- /dev/null
+++ b/libraries/kotlin.test/shared/src/main/kotlin.jvm/kotlin/test/AsserterLookup.kt
@@ -0,0 +1,53 @@
+package kotlin.test
+
+import java.util.*
+import java.util.concurrent.atomic.*
+import java.util.concurrent.locks.*
+
+private val inited = AtomicBoolean()
+private val lock = ReentrantLock()
+private val contributors = ArrayList()
+
+internal fun lookup(): Asserter {
+ initContributorsIfNeeded()
+
+ for (contributor in contributors) {
+ val asserter = contributor.contribute()
+ if (asserter != null) {
+ return asserter
+ }
+ }
+
+ return DefaultAsserter()
+}
+
+private fun initContributors() {
+ contributors.clear()
+ @Suppress("DEPRECATION")
+ val loader = ServiceLoader.load(AsserterContributor::class.javaClass)
+
+ for (contributor in loader) {
+ if (contributor != null) {
+ contributors.add(contributor)
+ }
+ }
+}
+
+private fun initContributorsIfNeeded() {
+ if (!inited.get()) {
+ lock.withLock {
+ if (inited.compareAndSet(false, true)) {
+ initContributors()
+ }
+ }
+ }
+}
+
+private inline fun Lock.withLock(block: () -> Unit) {
+ lockInterruptibly()
+ try {
+ block()
+ } finally {
+ unlock()
+ }
+}
\ No newline at end of file
diff --git a/libraries/kotlin.test/shared/src/main/kotlin.jvm/kotlin/test/TestAssertionsJVM.kt b/libraries/kotlin.test/shared/src/main/kotlin.jvm/kotlin/test/TestAssertionsJVM.kt
new file mode 100644
index 00000000000..babc601baa7
--- /dev/null
+++ b/libraries/kotlin.test/shared/src/main/kotlin.jvm/kotlin/test/TestAssertionsJVM.kt
@@ -0,0 +1,68 @@
+@file:kotlin.jvm.JvmMultifileClass
+@file:kotlin.jvm.JvmName("TestAssertionsKt")
+package kotlin.test
+
+import kotlin.jvm.internal.*
+import kotlin.reflect.*
+
+@Deprecated("Use assertFailsWith instead.", ReplaceWith("assertFailsWith(exceptionClass, block)"), kotlin.DeprecationLevel.ERROR)
+fun failsWith(exceptionClass: Class, block: () -> Any): T = assertFailsWith(exceptionClass, { block() })
+
+/** Asserts that a [block] fails with a specific exception being thrown. */
+fun assertFailsWith(exceptionClass: Class, block: () -> Unit): T = assertFailsWith(exceptionClass, null, block)
+
+/** Asserts that a [block] fails with a specific exception being thrown. */
+fun assertFailsWith(exceptionClass: Class, message: String?, block: () -> Unit): T {
+ try {
+ block()
+ } catch (e: Throwable) {
+ if (exceptionClass.isInstance(e)) {
+ @Suppress("UNCHECKED_CAST")
+ return e as T
+ }
+ asserter.fail((message?.let { "$it. " } ?: "") + "Expected an exception of type $exceptionClass to be thrown, but was $e")
+ throw e
+ }
+ val msg = message?.let { "$it. " } ?: ""
+ asserter.fail(msg + "Expected an exception of type $exceptionClass to be thrown, but was completed successfully.")
+ throw IllegalStateException(msg + "Should have failed.")
+}
+
+/** Asserts that a [block] fails with a specific exception of type [exceptionClass] being thrown. */
+@Suppress("DEPRECATION")
+fun assertFailsWith(exceptionClass: KClass, message: String? = null, block: () -> Unit): T = assertFailsWith(exceptionClass.javaClass, message, block)
+
+/** Asserts that a [block] fails with a specific exception of type [T] being thrown.
+ * Since inline method doesn't allow to trace where it was invoked, it is required to pass a [message] to distinguish this method call from others.
+ */
+@Suppress("DEPRECATION")
+inline fun assertFailsWith(message: String, noinline block: () -> Unit): T = assertFailsWith(T::class.javaClass, message, block)
+
+
+/**
+ * Comments out a [block] of test code until it is implemented while keeping a link to the code
+ * to implement in your unit test output
+ */
+inline fun todo(@Suppress("UNUSED_PARAMETER") block: () -> Unit) {
+ System.out.println("TODO at " + currentStackTrace()[1])
+}
+
+@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "NOTHING_TO_INLINE")
+inline fun currentStackTrace() = (java.lang.Exception() as java.lang.Throwable).stackTrace
+
+/**
+ * The active implementation of [Asserter]. An implementation of [Asserter] can be provided
+ * using the [Java service loader](http://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html) mechanism.
+ */
+val asserter: Asserter
+ get() = lookup()
+
+
+/**
+ * Returns a Java [Class] instance corresponding to the given [KClass] instance.
+ */
+@Suppress("UNCHECKED_CAST")
+@Intrinsic("kotlin.KClass.java.property")
+@Deprecated("This is for internal use only", ReplaceWith("this.java"), kotlin.DeprecationLevel.WARNING)
+val KClass.javaClass: Class
+ get() = (this as ClassBasedDeclarationContainer).jClass as Class
diff --git a/libraries/kotlin.test/shared/src/main/kotlin/kotlin/internal/internalAnnottaions.kt b/libraries/kotlin.test/shared/src/main/kotlin/kotlin/internal/internalAnnottaions.kt
new file mode 100644
index 00000000000..71404811ec6
--- /dev/null
+++ b/libraries/kotlin.test/shared/src/main/kotlin/kotlin/internal/internalAnnottaions.kt
@@ -0,0 +1,10 @@
+package kotlin.internal
+
+
+/**
+ * The value of this type parameter should be mentioned in input types (argument types, receiver type or expected type).
+ */
+@Target(AnnotationTarget.TYPE_PARAMETER)
+@Retention(AnnotationRetention.BINARY)
+internal annotation class OnlyInputTypes
+
diff --git a/libraries/kotlin.test/shared/src/main/kotlin/kotlin/jvm/annotations.kt b/libraries/kotlin.test/shared/src/main/kotlin/kotlin/jvm/annotations.kt
new file mode 100644
index 00000000000..bc99c9c8c36
--- /dev/null
+++ b/libraries/kotlin.test/shared/src/main/kotlin/kotlin/jvm/annotations.kt
@@ -0,0 +1,25 @@
+package kotlin.jvm
+
+
+/**
+ * Specifies the name for the Java class or method
+ * which is generated from this element.
+ * See the [Kotlin language documentation](http://kotlinlang.org/docs/reference/java-interop.html#handling-signature-clashes-with-platformname)
+ * for more information.
+ * @property name the name of the element.
+ */
+@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.FILE)
+@Retention(AnnotationRetention.BINARY)
+@MustBeDocumented
+public annotation class JvmName(public val name: String)
+
+/**
+ * Instructs the Kotlin compiler to generate a multifile class with top-level functions and properties declared in this file as one of its parts.
+ * Name of the corresponding multifile class is provided by the [JvmName] annotation.
+ */
+@Target(AnnotationTarget.FILE)
+@Retention(AnnotationRetention.SOURCE)
+@MustBeDocumented
+public annotation class JvmMultifileClass
+
+
diff --git a/libraries/kotlin.test/shared/src/main/kotlin/kotlin/test/CollectionAssertions.kt b/libraries/kotlin.test/shared/src/main/kotlin/kotlin/test/CollectionAssertions.kt
new file mode 100644
index 00000000000..12aba09dcd2
--- /dev/null
+++ b/libraries/kotlin.test/shared/src/main/kotlin/kotlin/test/CollectionAssertions.kt
@@ -0,0 +1,133 @@
+package kotlin.test
+
+import java.util.*
+
+class CollectionAssertionSession>(val collection: C)
+
+inline fun > assert(collection: C, block: CollectionAssertionSession.() -> Unit) {
+ CollectionAssertionSession(collection).block()
+}
+
+fun > CollectionAssertionSession<*, C>.sizeShouldBe(expectedSize: Int, message: String? = null) {
+ assertEquals(expectedSize, collection.size, message ?: "collection should have size $expectedSize but it is ${collection.size}")
+}
+
+fun CollectionAssertionSession.elementAtShouldBe(position: Int, expected: T, message: String? = null) {
+ assertEquals(expected, collection.elementAt(position), message ?: "element at $position should be $expected")
+}
+
+fun > CollectionAssertionSession.elementAtShouldComply(position: Int, message: String? = null, predicate: (T) -> Boolean) {
+ assertTrue(message) { predicate(collection.elementAt(position)) }
+}
+
+fun CollectionAssertionSession.lastElementShouldBe(expected: T, message: String? = null) {
+ assertEquals(expected, collection.last(), message ?: "the last element should be $expected")
+}
+
+fun CollectionAssertionSession.containsAll(vararg elements: T) {
+ for (e in elements) {
+ assertTrue(message = "Element $e is missing in the collection") { e in collection }
+ }
+}
+
+fun > CollectionAssertionSession.shouldBe(expectedElements: Iterable, message: String? = null) {
+ val actual = collection.iterator()
+ val expected = expectedElements.iterator()
+
+ while (actual.hasNext() && expected.hasNext()) {
+ assertEquals(expected.next(), actual.next(), message)
+ }
+
+ if (actual.hasNext()) {
+ fail("Actual collection is longer than expected. Extra elements are: ${actual.remaining()}")
+ }
+ if (expected.hasNext()) {
+ fail("Actual collection is shorter than expected. Missing elements are: ${expected.remaining()}")
+ }
+}
+
+fun > CollectionAssertionSession.shouldBeSet(other: Set, message: String? = null) {
+ for (e in other) {
+ if (e !in collection) {
+ fail(message ?: "Element $e in not in the collection $collection")
+ }
+ }
+ for (e in collection) {
+ if (e !in other) {
+ fail(message ?: "Element $e is not expected")
+ }
+ }
+}
+
+fun > CollectionAssertionSession.shouldBeSet(vararg other: T) {
+ val otherSet = HashSet()
+ for (e in other) {
+ otherSet.add(e)
+ }
+
+ shouldBeSet(otherSet)
+}
+
+private operator fun Iterable.contains(e: T): Boolean {
+ if (this is Set) {
+ return contains(e)
+ }
+ for (it in this) {
+ if (it == e) {
+ return true
+ }
+ }
+ return false
+}
+
+private fun Iterable.last(): T {
+ if (this is List) {
+ if (this.isEmpty()) {
+ throw NoSuchElementException()
+ }
+
+ return this[size - 1]
+ }
+
+ val it = iterator()
+ var result: T = iterator().next()
+
+ while (it.hasNext()) {
+ result = it.next()
+ }
+
+ return result
+}
+
+private fun Iterable.elementAt(position: Int): T {
+ if (position < 0) {
+ throw IllegalArgumentException("position shouldn't be negative: $position")
+ }
+ if (this is List) {
+ return this[position]
+ }
+
+ val iterator = iterator()
+ var idx = 0
+ do {
+ if (!iterator.hasNext()) {
+ throw IndexOutOfBoundsException("index $position is out of the collection bounds [0; $idx)")
+ }
+ val result = iterator.next()
+ if (idx == position) {
+ return result
+ }
+
+ idx++
+ } while (true)
+
+ throw IllegalStateException()
+}
+
+private fun Iterator.remaining(): List {
+ val result = ArrayList()
+ while (hasNext()) {
+ result.add(next())
+ }
+ return result
+}
\ No newline at end of file
diff --git a/libraries/kotlin.test/shared/src/main/kotlin/kotlin/test/DefaultAsserter.kt b/libraries/kotlin.test/shared/src/main/kotlin/kotlin/test/DefaultAsserter.kt
new file mode 100644
index 00000000000..eed8581329a
--- /dev/null
+++ b/libraries/kotlin.test/shared/src/main/kotlin/kotlin/test/DefaultAsserter.kt
@@ -0,0 +1,14 @@
+package kotlin.test
+
+/**
+ * Default [Asserter] implementation to avoid dependency on JUnit or TestNG.
+ */
+class DefaultAsserter() : Asserter {
+
+ override fun fail(message: String?) {
+ if (message == null)
+ throw AssertionError()
+ else
+ throw AssertionError(message)
+ }
+}
\ No newline at end of file
diff --git a/libraries/kotlin.test/shared/src/main/kotlin/kotlin/test/TestAssertions.kt b/libraries/kotlin.test/shared/src/main/kotlin/kotlin/test/TestAssertions.kt
new file mode 100644
index 00000000000..7983d130e9b
--- /dev/null
+++ b/libraries/kotlin.test/shared/src/main/kotlin/kotlin/test/TestAssertions.kt
@@ -0,0 +1,187 @@
+@file:kotlin.jvm.JvmMultifileClass
+@file:kotlin.jvm.JvmName("TestAssertionsKt")
+
+/**
+ * A number of helper methods for writing unit tests.
+ */
+package kotlin.test
+
+import kotlin.internal.*
+
+
+/** Asserts that the given [block] returns `false`. */
+@Deprecated("Use assertFalse instead.", ReplaceWith("assertFalse(message, block)"), kotlin.DeprecationLevel.ERROR)
+fun assertNot(message: String, block: () -> Boolean): Unit = assertFalse(message, block)
+
+/** Asserts that the given [block] returns `false`. */
+@Deprecated("Use assertFalse instead.", ReplaceWith("assertFalse(null, block)"), kotlin.DeprecationLevel.ERROR)
+fun assertNot(block: () -> Boolean): Unit = assertFalse(block = block)
+
+/** Asserts that the given [block] returns `true`. */
+fun assertTrue(message: String? = null, block: () -> Boolean): Unit = assertTrue(block(), message)
+
+/** Asserts that the expression is `true` with an optional [message]. */
+fun assertTrue(actual: Boolean, message: String? = null) {
+ return asserter.assertTrue(message ?: "Expected value to be true.", actual)
+}
+
+/** Asserts that the given [block] returns `false`. */
+fun assertFalse(message: String? = null, block: () -> Boolean): Unit = assertFalse(block(), message)
+
+/** Asserts that the expression is `false` with an optional [message]. */
+fun assertFalse(actual: Boolean, message: String? = null) {
+ return asserter.assertTrue(message ?: "Expected value to be false.", !actual)
+}
+
+/** Asserts that the [expected] value is equal to the [actual] value, with an optional [message]. */
+fun <@OnlyInputTypes T> assertEquals(expected: T, actual: T, message: String? = null) {
+ asserter.assertEquals(message, expected, actual)
+}
+
+/** Asserts that the [actual] value is not equal to the illegal value, with an optional [message]. */
+fun <@OnlyInputTypes T> assertNotEquals(illegal: T, actual: T, message: String? = null) {
+ asserter.assertNotEquals(message, illegal, actual)
+}
+
+/** Asserts that the [actual] value is not `null`, with an optional [message]. */
+fun assertNotNull(actual: T?, message: String? = null): T {
+ asserter.assertNotNull(message, actual)
+ return actual!!
+}
+
+/** Asserts that the [actual] value is not `null`, with an optional [message] and a function [block] to process the not-null value. */
+fun assertNotNull(actual: T?, message: String? = null, block: (T) -> R) {
+ asserter.assertNotNull(message, actual)
+ if (actual != null) {
+ block(actual)
+ }
+}
+
+/** Asserts that the [actual] value is `null`, with an optional [message]. */
+fun assertNull(actual: Any?, message: String? = null) {
+ asserter.assertNull(message, actual)
+}
+
+/** Marks a test as having failed if this point in the execution path is reached, with an optional [message]. */
+fun fail(message: String? = null) {
+ asserter.fail(message)
+}
+
+/** Asserts that given function [block] returns the given [expected] value. */
+fun <@OnlyInputTypes T> expect(expected: T, block: () -> T) {
+ assertEquals(expected, block())
+}
+
+/** Asserts that given function [block] returns the given [expected] value and use the given [message] if it fails. */
+fun <@OnlyInputTypes T> expect(expected: T, message: String?, block: () -> T) {
+ assertEquals(expected, block(), message)
+}
+
+@Deprecated("Use assertFails instead.", ReplaceWith("assertFails(block)"), kotlin.DeprecationLevel.ERROR)
+fun fails(block: () -> Unit): Throwable? = assertFails(block)
+
+/** Asserts that given function [block] fails by throwing an exception. */
+fun assertFails(block: () -> Unit): Throwable? {
+ var thrown: Throwable? = null
+ try {
+ block()
+ } catch (e: Throwable) {
+ thrown = e
+ }
+ if (thrown == null)
+ asserter.fail("Expected an exception to be thrown")
+ return thrown
+}
+
+/**
+ * Abstracts the logic for performing assertions. Specific implementations of [Asserter] can use JUnit
+ * or TestNG assertion facilities.
+ */
+interface Asserter {
+ /**
+ * Fails the current test with the specified message.
+ *
+ * @param message the message to report.
+ */
+ fun fail(message: String?): Unit
+
+ /**
+ * Asserts that the specified value is `true`.
+ *
+ * @param lazyMessage the function to return a message to report if the assertion fails.
+ */
+ fun assertTrue(lazyMessage: () -> String?, actual: Boolean): Unit {
+ if (!actual) {
+ fail(lazyMessage())
+ }
+ }
+
+ /**
+ * Asserts that the specified value is `true`.
+ *
+ * @param message the message to report if the assertion fails.
+ */
+ fun assertTrue(message: String?, actual: Boolean): Unit {
+ assertTrue({ message }, actual)
+ }
+
+ /**
+ * Asserts that the specified values are equal.
+ *
+ * @param message the message to report if the assertion fails.
+ */
+ fun assertEquals(message: String?, expected: Any?, actual: Any?): Unit {
+ assertTrue({ (message?.let { "$it. " } ?: "") + "Expected <$expected>, actual <$actual>." }, actual == expected)
+ }
+
+ /**
+ * Asserts that the specified values are not equal.
+ *
+ * @param message the message to report if the assertion fails.
+ */
+ fun assertNotEquals(message: String?, illegal: Any?, actual: Any?): Unit {
+ assertTrue({ (message?.let { "$it. " } ?: "") + "Illegal value: <$actual>." }, actual != illegal)
+ }
+
+ /**
+ * Asserts that the specified value is `null`.
+ *
+ * @param message the message to report if the assertion fails.
+ */
+ fun assertNull(message: String?, actual: Any?): Unit {
+ assertTrue({ (message?.let { "$it. " } ?: "") + "Expected value to be null, but was: <$actual>." }, actual == null)
+ }
+
+ /**
+ * Asserts that the specified value is not `null`.
+ *
+ * @param message the message to report if the assertion fails.
+ */
+ fun assertNotNull(message: String?, actual: Any?): Unit {
+ assertTrue({ (message?.let { "$it. " } ?: "") + "Expected value to be not null." }, actual != null)
+ }
+
+}
+
+/**
+ * Checks applicability and provides Asserter instance
+ */
+interface AsserterContributor {
+ /**
+ * Provides [Asserter] instance or `null` depends on the current context.
+ *
+ * @return asserter instance or null if it is not applicable now
+ */
+ fun contribute(): Asserter?
+}
+
+internal fun T.let(block: (T) -> R): R = block(this)
+
+internal fun Iterable.firstOrNull(predicate: (T) -> Boolean): T? {
+ for (e in this) {
+ if (predicate(e)) {
+ return e
+ }
+ }
+ return null
+}
diff --git a/libraries/kotlin.test/shared/src/test/kotlin.jvm/BasicAssertionsJVMTest.kt b/libraries/kotlin.test/shared/src/test/kotlin.jvm/BasicAssertionsJVMTest.kt
new file mode 100644
index 00000000000..e5206fbb19c
--- /dev/null
+++ b/libraries/kotlin.test/shared/src/test/kotlin.jvm/BasicAssertionsJVMTest.kt
@@ -0,0 +1,39 @@
+package kotlinx.testing.tests
+
+import kotlin.test.*
+import org.junit.*
+
+class BasicAssertionsJVMTest {
+ @Test
+ fun testFailsWith() {
+ assertFailsWith(IllegalArgumentException::class) {
+ throw IllegalArgumentException()
+ }
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testFailsWithFails() {
+ assertFailsWith(IllegalArgumentException::class) {
+ throw IllegalStateException()
+ }
+ }
+
+ @Test
+ fun testFailsWithMessage() {
+ assertFailsWith("") {
+ throw IllegalArgumentException()
+ }
+ }
+
+ @Test
+ fun testToDo() {
+ todo {
+ fail("Shouldn't pass here")
+ }
+ }
+
+ @Test
+ fun testCurrentStackTrace() {
+ assertEquals("BasicAssertionsJVMTest.kt", currentStackTrace()[0].fileName)
+ }
+}
diff --git a/libraries/kotlin.test/shared/src/test/kotlin/kotlin/test/tests/BasicAssertionsTest.kt b/libraries/kotlin.test/shared/src/test/kotlin/kotlin/test/tests/BasicAssertionsTest.kt
new file mode 100644
index 00000000000..224152dab20
--- /dev/null
+++ b/libraries/kotlin.test/shared/src/test/kotlin/kotlin/test/tests/BasicAssertionsTest.kt
@@ -0,0 +1,131 @@
+package kotlin.test.tests
+
+import org.junit.*
+import kotlin.test.*
+
+class BasicAssertionsTest {
+ @Test
+ fun testAssertEquals() {
+ assertEquals(1, 1)
+ }
+
+ @Test
+ fun testAssertEqualsString() {
+ assertEquals("a", "a")
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testAssertEqualsFails() {
+ assertEquals(1, 2)
+ }
+
+ @Test
+ fun testAssertTrue() {
+ assertTrue(true)
+ }
+
+ @Test
+ fun testAssertTrueWithLambda() {
+ assertTrue { true }
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testAssertTrueFails() {
+ assertTrue(false)
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testAssertTrueWithLambdaFails() {
+ assertTrue { false }
+ }
+
+ @Test
+ fun testAssertFalse() {
+ assertFalse(false)
+ }
+
+ @Test
+ fun testAssertFalseLambda() {
+ assertFalse { false }
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testAssertFalseFails() {
+ assertFalse(true)
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testAssertFalseWithLambdaFails() {
+ assertFalse { true }
+ }
+
+ @Test
+ fun testAssertFails() {
+ assertFails { throw IllegalStateException() }
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testAssertFailsFails() {
+ assertFails { }
+ Assert.fail("Shouldn't pass here")
+ }
+
+
+ @Test
+ fun testAssertNotEquals() {
+ assertNotEquals(1, 2)
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testAssertNotEqualsFails() {
+ assertNotEquals(1, 1)
+ }
+
+ @Test
+ fun testAssertNotNull() {
+ assertNotNull(true)
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testAssertNotNullFails() {
+ assertNotNull(null)
+ }
+
+ @Test
+ fun testAssertNotNullLambda() {
+ assertNotNull("") { assertEquals("", it) }
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testAssertNotNullLambdaFails() {
+ assertNotNull(null) {
+ @Suppress("UNREACHABLE_CODE")
+ assertNotNull(it)
+ }
+ }
+
+ @Test
+ fun testAssertNull() {
+ assertNull(null)
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testAssertNullFails() {
+ assertNull(true)
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testFail() {
+ fail("should fail")
+ }
+
+ @Test
+ fun testExpect() {
+ expect(1) { 1 }
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testExpectFails() {
+ expect(1) { 2 }
+ }
+}
\ No newline at end of file
diff --git a/libraries/kotlin.test/shared/src/test/kotlin/kotlin/test/tests/CollectionAssertionTest.kt b/libraries/kotlin.test/shared/src/test/kotlin/kotlin/test/tests/CollectionAssertionTest.kt
new file mode 100644
index 00000000000..c0f55ed772c
--- /dev/null
+++ b/libraries/kotlin.test/shared/src/test/kotlin/kotlin/test/tests/CollectionAssertionTest.kt
@@ -0,0 +1,136 @@
+package kotlin.test.tests
+
+import org.junit.*
+import java.util.*
+import kotlin.test.*
+
+class CollectionAssertionTest {
+ @Test
+ fun testList() {
+ assert(listOf(1, 2, 3)) {
+ sizeShouldBe(3)
+ elementAtShouldBe(0, 1)
+ elementAtShouldComply(0) { it > 0 }
+ lastElementShouldBe(3)
+ containsAll(1, 2)
+
+ shouldBe(listOf(1, 2, 3))
+ }
+ }
+
+ @Test
+ fun testSet() {
+ assert(setOf(1, 2, 3)) {
+ sizeShouldBe(3)
+ elementAtShouldBe(0, 1)
+ elementAtShouldComply(0) { it > 0 }
+ lastElementShouldBe(3)
+ containsAll(1, 2)
+
+ shouldBeSet(setOf(1, 2, 3))
+ }
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testSizeShouldBeFails() {
+ assert(listOf(1, 2, 3)) {
+ sizeShouldBe(1)
+ }
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testElementAtShouldBeFail() {
+ assert(listOf(1, 2, 3)) {
+ elementAtShouldBe(0, 0)
+ }
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testElementAtShouldComplyFail() {
+ assert(listOf(1, 2, 3)) {
+ elementAtShouldComply(0) { it < 0 }
+ }
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testLastElementFail() {
+ assert(listOf(1, 2, 3)) {
+ lastElementShouldBe(0)
+ }
+ }
+
+ @Test(expected = NoSuchElementException::class)
+ fun testLastElementOnEmptyFail() {
+ assert(listOf()) {
+ lastElementShouldBe(0)
+ }
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testContainsAll() {
+ assert(listOf(1, 2, 3)) {
+ containsAll(1, 8)
+ }
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testContainsAllWithSet() {
+ assert(setOf(1, 2, 3)) {
+ containsAll(1, 8)
+ }
+ }
+
+ @Test
+ fun testShouldBeLess() {
+ try {
+ assert(listOf(1, 2, 3)) {
+ shouldBe(listOf(1, 2, 3, 4))
+ }
+ Assert.fail("It shouldn't pass here")
+ } catch (e: AssertionError) {
+ assertTrue { "[4]" in e.message!! }
+ assertTrue { "shorter" in e.message!! }
+ }
+ }
+
+ @Test
+ fun testShouldBeLonger() {
+ try {
+ assert(listOf(1, 2, 3)) {
+ shouldBe(listOf(1, 2))
+ }
+ Assert.fail("It shouldn't pass here")
+ } catch (e: AssertionError) {
+ assertTrue { "[3]" in e.message!! }
+ assertTrue { "longer" in e.message!! }
+ }
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testShouldBeSetExtra() {
+ assert(setOf(1, 2, 3)) {
+ shouldBeSet(setOf(1, 2))
+ }
+ }
+
+ @Test
+ fun testShouldBeSetExact() {
+ assert(setOf(1, 2, 3)) {
+ shouldBeSet(setOf(1, 2, 3))
+ }
+ }
+
+ @Test
+ fun testShouldBeSetExactVararg() {
+ assert(setOf(1, 2, 3)) {
+ shouldBeSet(1, 2, 3)
+ }
+ }
+
+ @Test(expected = AssertionError::class)
+ fun testShouldBeSetMissing() {
+ assert(setOf(1, 2, 3)) {
+ shouldBeSet(setOf(1, 2, 3, 4))
+ }
+ }
+}
diff --git a/libraries/kotlin.test/shared/src/test/kotlin/kotlin/test/tests/util.kt b/libraries/kotlin.test/shared/src/test/kotlin/kotlin/test/tests/util.kt
new file mode 100644
index 00000000000..80f09691e6a
--- /dev/null
+++ b/libraries/kotlin.test/shared/src/test/kotlin/kotlin/test/tests/util.kt
@@ -0,0 +1,24 @@
+package kotlin.test.tests
+
+import java.util.*
+
+internal fun listOf(vararg elements: T): List {
+ val result = ArrayList(elements.size)
+ for (e in elements) {
+ result.add(e)
+ }
+
+ return result
+}
+
+internal fun setOf(vararg elements: T): Set {
+ val result = HashSet(elements.size)
+ for (e in elements) {
+ result.add(e)
+ }
+
+ return result
+}
+
+internal operator fun String.contains(sub: String) = (this as java.lang.String).contains(sub)
+
diff --git a/libraries/pom.xml b/libraries/pom.xml
index d7a7eeb78fe..b164b4af3a9 100644
--- a/libraries/pom.xml
+++ b/libraries/pom.xml
@@ -97,6 +97,8 @@
tools/kotlin-js-tests
tools/kotlin-js-tests-junit
+ kotlin.test
+
examples/kotlin-java-example
examples/js-example
examples/kotlin-js-library-example
diff --git a/libraries/stdlib/pom.xml b/libraries/stdlib/pom.xml
index 6eca2f5aa98..9335016ad6f 100644
--- a/libraries/stdlib/pom.xml
+++ b/libraries/stdlib/pom.xml
@@ -19,6 +19,12 @@
kotlin-runtime
${project.version}
+
+ org.jetbrains.kotlin
+ kotlin.test.jvm
+ ${project.version}
+ test
+
diff --git a/libraries/tools/kotlin-js-tests-junit/pom.xml b/libraries/tools/kotlin-js-tests-junit/pom.xml
index 42dfe702848..b15be51fb73 100644
--- a/libraries/tools/kotlin-js-tests-junit/pom.xml
+++ b/libraries/tools/kotlin-js-tests-junit/pom.xml
@@ -21,6 +21,11 @@
kotlin-stdlib
${project.version}
+
+ org.jetbrains.kotlin
+ kotlin.test.jvm
+ ${project.version}
+
org.jetbrains.kotlin
kotlin-js-tests