[Runtime testing] Add basic runtime tests infrastructure

This commit is contained in:
Ilya Matveev
2020-10-06 13:05:24 +07:00
committed by Ilya Matveev
parent 378dc1954b
commit f606c59d5b
9 changed files with 575 additions and 0 deletions
+1
View File
@@ -103,6 +103,7 @@ val compileGroovy: GroovyCompile by tasks
// https://youtrack.jetbrains.com/issue/KT-37435
compileKotlin.apply {
kotlinOptions.jvmTarget = "1.8"
kotlinOptions.freeCompilerArgs += listOf("-Xno-optimized-callable-references", "-Xskip-prerelease-check")
}
@@ -0,0 +1,193 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.testing.native
import groovy.lang.Closure
import java.io.File
import javax.inject.Inject
import org.gradle.api.*
import org.gradle.api.tasks.*
import org.jetbrains.kotlin.ExecClang
import org.jetbrains.kotlin.bitcode.CompileToBitcode
import org.jetbrains.kotlin.konan.target.*
open class CompileNativeTest @Inject constructor(
@InputFile val inputFile: File,
@Input val target: String
) : DefaultTask () {
@OutputFile
var outputFile = project.buildDir.resolve("bin/test/$target/${inputFile.nameWithoutExtension}.o")
@Input
val clangArgs = mutableListOf<String>()
@TaskAction
fun compile() {
val plugin = project.convention.getPlugin(ExecClang::class.java)
plugin.execBareClang(Action {
it.executable = "clang++"
it.args = clangArgs + listOf(inputFile.absolutePath, "-o", outputFile.absolutePath)
})
}
}
open class LinkNativeTest @Inject constructor(
@InputFiles val inputFiles: List<File>,
@OutputFile val outputFile: File,
@Internal val target: String,
@Internal val linkerArgs: List<String>,
private val platformManager: PlatformManager
) : DefaultTask () {
companion object {
fun create(
project: Project,
platformManager: PlatformManager,
taskName: String,
inputFiles: List<File>,
target: String,
outputFile: File,
linkerArgs: List<String>
): LinkNativeTest = project.tasks.create(
taskName,
LinkNativeTest::class.java,
inputFiles,
outputFile,
target,
linkerArgs,
platformManager)
fun create(
project: Project,
platformManager: PlatformManager,
taskName: String,
inputFiles: List<File>,
target: String,
executableName: String,
linkerArgs: List<String> = listOf()
): LinkNativeTest = create(
project,
platformManager,
taskName,
inputFiles,
target,
project.buildDir.resolve("bin/test/$target/$executableName"),
linkerArgs)
}
@get:Input
val commands: List<List<String>>
get() {
// Getting link commands requires presence of a target toolchain.
// Thus we cannot get them at the configuration stage because the toolchain may be not downloaded yet.
val linker = platformManager.platform(platformManager.targetByName(target)).linker
return linker.finalLinkCommands(
inputFiles.map { it.absolutePath },
outputFile.absolutePath,
listOf(),
linkerArgs,
optimize = false,
debug = false,
kind = LinkerOutputKind.EXECUTABLE,
outputDsymBundle = "",
needsProfileLibrary = false
).map { it.argsWithExecutable }
}
@TaskAction
fun link() {
for (command in commands) {
project.exec {
it.commandLine(command)
}
}
}
}
@Suppress("UNCHECKED_CAST")
private fun <T: Task> T.configure(f: T.() -> Unit): T =
this.configure(object: Closure<Unit>(this) {
// Dynamically invoked by Groovy
fun doCall() {
f()
}
}) as T
fun createTestTask(
project: Project,
testTaskName: String,
testedTaskNames: List<String>
): Task {
val platformManager = project.rootProject.findProperty("platformManager") as PlatformManager
val googleTestExtension = project.extensions.getByName(RuntimeTestingPlugin.GOOGLE_TEST_EXTENSION_NAME) as GoogleTestExtension
val testedTasks = testedTaskNames.map {
project.tasks.getByName(it) as CompileToBitcode
}
val target = testedTasks.map {
it.target
}.distinct().single()
val konanTarget = platformManager.targetByName(target)
val compileToBitcodeTasks = testedTasks.mapNotNull {
val name = "${it.name}TestBitcode"
val task = project.tasks.findByName(name) as? CompileToBitcode ?:
project.tasks.create(name,
CompileToBitcode::class.java,
it.srcRoot,
"${it.folderName}Tests",
target, "test"
).configure {
excludeFiles = emptyList()
includeFiles = listOf("**/*Test.cpp", "**/*Test.mm")
dependsOn(it)
compilerArgs.addAll(it.compilerArgs)
headersDirs += googleTestExtension.headersDirs
}
if (task.inputFiles.count() == 0)
null
else
task
}
val testFrameworkTasks = listOf(
project.tasks.getByName("${target}Googletest") as CompileToBitcode,
project.tasks.getByName("${target}Googlemock") as CompileToBitcode
)
val compileToObjectFileTasks = (compileToBitcodeTasks + testedTasks + testFrameworkTasks).map {
val name = "${it.name}Object"
val clangFlags = platformManager.platform(konanTarget).configurables as ClangFlags
project.tasks.findByName(name) as? CompileNativeTest ?:
project.tasks.create(name,
CompileNativeTest::class.java,
it.outFile,
target
).configure {
dependsOn(it)
clangArgs.addAll(clangFlags.clangFlags)
clangArgs.addAll(clangFlags.clangNooptFlags)
}
}
val linkTask = LinkNativeTest.create(
project,
platformManager,
"${testTaskName}Link",
compileToObjectFileTasks.map { it.outputFile },
target,
testTaskName
).configure {
dependsOn(compileToObjectFileTasks)
}
return project.tasks.create(testTaskName, Exec::class.java).apply {
dependsOn(linkTask)
workingDir = project.buildDir.resolve("testReports/$testTaskName")
val xmlReport = workingDir.resolve("report.xml")
executable(linkTask.outputFile)
args("--gtest_output=xml:${xmlReport.absoluteFile}")
doFirst {
workingDir.mkdirs()
}
}
}
+10
View File
@@ -8,10 +8,14 @@ set(CMAKE_CXX_STANDARD 14)
set(SRC_DIR ${CMAKE_SOURCE_DIR}/src)
set(GOOGLETEST_DIR ${CMAKE_SOURCE_DIR}/googletest)
include_directories(${SRC_DIR}/main/cpp)
include_directories(${SRC_DIR}/debug/headers)
include_directories(${CMAKE_SOURCE_DIR}/../common/src/hash/headers)
include_directories(${GOOGLETEST_DIR}/googletest/include)
include_directories(${GOOGLETEST_DIR}/googlemock/include)
add_executable(runtime
src/main/cpp/Arrays.cpp
@@ -76,6 +80,12 @@ add_executable(runtime
src/objc/cpp/ObjCExportCollections.mm
src/objc/cpp/ObjCInteropUtilsClasses.mm
# Tests
src/test_support/cpp/CompilerGenerated.cpp
src/test_support/cpp/CompilerGeneratedObjC.mm
src/test_support/cpp/TestLauncher.cpp
src/main/cpp/ArraysTest.cpp
)
target_compile_definitions(runtime PUBLIC "-DKONAN_OSX=1")
+42
View File
@@ -86,12 +86,54 @@ bitcode {
create("objc") {
includeRuntime()
}
create("test_support", outputGroup = "test") {
includeRuntime()
dependsOn("downloadGoogleTest")
headersDirs += googletest.headersDirs
}
}
targetList.forEach { targetName ->
createTestTask(
project,
"${targetName}StdAllocRuntimeTests",
listOf(
"${targetName}Runtime",
"${targetName}TestSupport",
"${targetName}Strict",
"${targetName}Release",
"${targetName}StdAlloc"
)
)
createTestTask(
project,
"${targetName}MimallocRuntimeTests",
listOf(
"${targetName}Runtime",
"${targetName}TestSupport",
"${targetName}Strict",
"${targetName}Release",
"${targetName}Mimalloc",
"${targetName}OptAlloc"
)
)
tasks.register("${targetName}RuntimeTests") {
dependsOn("${targetName}StdAllocRuntimeTests")
dependsOn("${targetName}MimallocRuntimeTests")
}
}
val hostRuntime by tasks.registering {
dependsOn("${hostName}Runtime")
}
val hostRuntimeTests by tasks.registering {
dependsOn("${hostName}RuntimeTests")
}
val assemble by tasks.registering {
dependsOn(tasks.withType(CompileToBitcode::class).matching {
it.outputGroup == "main"
@@ -0,0 +1,233 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "CompilerGenerated.h"
#include "Types.h"
namespace {
TypeInfo theAnyTypeInfoImpl = {};
TypeInfo theArrayTypeInfoImpl = {};
TypeInfo theBooleanArrayTypeInfoImpl = {};
TypeInfo theByteArrayTypeInfoImpl = {};
TypeInfo theCharArrayTypeInfoImpl = {};
TypeInfo theDoubleArrayTypeInfoImpl = {};
TypeInfo theFloatArrayTypeInfoImpl = {};
TypeInfo theForeignObjCObjectTypeInfoImpl = {};
TypeInfo theFreezableAtomicReferenceTypeInfoImpl = {};
TypeInfo theIntArrayTypeInfoImpl = {};
TypeInfo theLongArrayTypeInfoImpl = {};
TypeInfo theNativePtrArrayTypeInfoImpl = {};
TypeInfo theObjCObjectWrapperTypeInfoImpl = {};
TypeInfo theOpaqueFunctionTypeInfoImpl = {};
TypeInfo theShortArrayTypeInfoImpl = {};
TypeInfo theStringTypeInfoImpl = {};
TypeInfo theThrowableTypeInfoImpl = {};
TypeInfo theUnitTypeInfoImpl = {};
TypeInfo theWorkerBoundReferenceTypeInfoImpl = {};
ArrayHeader theEmptyStringImpl = { &theStringTypeInfoImpl, /* element count */ 0 };
template <class T>
struct KBox {
ObjHeader header;
const T value;
};
} // namespace
extern "C" {
extern const int KonanNeedDebugInfo = 0;
extern const TypeInfo* theAnyTypeInfo = &theAnyTypeInfoImpl;
extern const TypeInfo* theArrayTypeInfo = &theArrayTypeInfoImpl;
extern const TypeInfo* theBooleanArrayTypeInfo = &theBooleanArrayTypeInfoImpl;
extern const TypeInfo* theByteArrayTypeInfo = &theByteArrayTypeInfoImpl;
extern const TypeInfo* theCharArrayTypeInfo = &theCharArrayTypeInfoImpl;
extern const TypeInfo* theDoubleArrayTypeInfo = &theDoubleArrayTypeInfoImpl;
extern const TypeInfo* theFloatArrayTypeInfo = &theFloatArrayTypeInfoImpl;
extern const TypeInfo* theForeignObjCObjectTypeInfo = &theForeignObjCObjectTypeInfoImpl;
extern const TypeInfo* theFreezableAtomicReferenceTypeInfo = &theFreezableAtomicReferenceTypeInfoImpl;
extern const TypeInfo* theIntArrayTypeInfo = &theIntArrayTypeInfoImpl;
extern const TypeInfo* theLongArrayTypeInfo = &theLongArrayTypeInfoImpl;
extern const TypeInfo* theNativePtrArrayTypeInfo = &theNativePtrArrayTypeInfoImpl;
extern const TypeInfo* theObjCObjectWrapperTypeInfo = &theObjCObjectWrapperTypeInfoImpl;
extern const TypeInfo* theOpaqueFunctionTypeInfo = &theOpaqueFunctionTypeInfoImpl;
extern const TypeInfo* theShortArrayTypeInfo = &theShortArrayTypeInfoImpl;
extern const TypeInfo* theStringTypeInfo = &theStringTypeInfoImpl;
extern const TypeInfo* theThrowableTypeInfo = &theThrowableTypeInfoImpl;
extern const TypeInfo* theUnitTypeInfo = &theUnitTypeInfoImpl;
extern const TypeInfo* theWorkerBoundReferenceTypeInfo = &theWorkerBoundReferenceTypeInfoImpl;
extern const ArrayHeader theEmptyArray = { &theArrayTypeInfoImpl, /* element count */0 };
OBJ_GETTER0(TheEmptyString) {
RETURN_OBJ(theEmptyStringImpl.obj());
}
RUNTIME_NORETURN OBJ_GETTER(makeWeakReferenceCounter, void*) {
THROW_NOT_IMPLEMENTED
}
RUNTIME_NORETURN OBJ_GETTER(makePermanentWeakReferenceImpl, void*) {
THROW_NOT_IMPLEMENTED
}
RUNTIME_NORETURN OBJ_GETTER(makeObjCWeakReferenceImpl, void*) {
THROW_NOT_IMPLEMENTED
}
void checkRangeIndexes(KInt from, KInt to, KInt size) {
if (from < 0 || to > size) {
throw std::out_of_range("Index out of bounds: from=" + std::to_string(from)
+ ", to=" + std::to_string(to)
+ ", size=" + std::to_string(size));
}
if (from > to) {
throw std::invalid_argument("Illegal argument: from > to, from=" + std::to_string(from) + ", to=" + std::to_string(to));
}
}
RUNTIME_NORETURN OBJ_GETTER(WorkerLaunchpad, KRef) {
THROW_NOT_IMPLEMENTED
}
void RUNTIME_NORETURN ThrowWorkerInvalidState() {
THROW_NOT_IMPLEMENTED
}
void RUNTIME_NORETURN ThrowNullPointerException() {
THROW_NOT_IMPLEMENTED
}
void RUNTIME_NORETURN ThrowArrayIndexOutOfBoundsException() {
THROW_NOT_IMPLEMENTED
}
void RUNTIME_NORETURN ThrowClassCastException(const ObjHeader* instance, const TypeInfo* type_info) {
THROW_NOT_IMPLEMENTED
}
void RUNTIME_NORETURN ThrowArithmeticException() {
THROW_NOT_IMPLEMENTED
}
void RUNTIME_NORETURN ThrowNumberFormatException() {
THROW_NOT_IMPLEMENTED
}
void RUNTIME_NORETURN ThrowOutOfMemoryError() {
THROW_NOT_IMPLEMENTED
}
void RUNTIME_NORETURN ThrowNotImplementedError() {
THROW_NOT_IMPLEMENTED
}
void RUNTIME_NORETURN ThrowCharacterCodingException() {
THROW_NOT_IMPLEMENTED
}
void RUNTIME_NORETURN ThrowIllegalArgumentException() {
THROW_NOT_IMPLEMENTED
}
void RUNTIME_NORETURN ThrowIllegalStateException() {
THROW_NOT_IMPLEMENTED
}
void RUNTIME_NORETURN ThrowInvalidMutabilityException(KConstRef where) {
THROW_NOT_IMPLEMENTED
}
void RUNTIME_NORETURN ThrowIncorrectDereferenceException() {
THROW_NOT_IMPLEMENTED
}
void RUNTIME_NORETURN ThrowIllegalObjectSharingException(KConstNativePtr typeInfo, KConstNativePtr address) {
THROW_NOT_IMPLEMENTED
}
void RUNTIME_NORETURN ThrowFreezingException(KRef toFreeze, KRef blocker) {
THROW_NOT_IMPLEMENTED
}
void ReportUnhandledException(KRef throwable) {
konan::consolePrintf("Uncaught Kotlin exception.");
}
RUNTIME_NORETURN OBJ_GETTER(DescribeObjectForDebugging, KConstNativePtr typeInfo, KConstNativePtr address) {
THROW_NOT_IMPLEMENTED
}
void ExceptionReporterLaunchpad(KRef reporter, KRef throwable) {
THROW_NOT_IMPLEMENTED
}
void Kotlin_WorkerBoundReference_freezeHook(KRef thiz) {
THROW_NOT_IMPLEMENTED
}
extern const KBoolean BOOLEAN_RANGE_FROM = false;
extern const KBoolean BOOLEAN_RANGE_TO = true;
extern KBox<KBoolean> BOOLEAN_CACHE[] = {
{{}, false},
{{}, true},
};
OBJ_GETTER(Kotlin_boxBoolean, KBoolean value) {
if (value) {
RETURN_OBJ(&BOOLEAN_CACHE[1].header);
} else {
RETURN_OBJ(&BOOLEAN_CACHE[0].header);
}
}
extern const KByte BYTE_RANGE_FROM = -1;
extern const KByte BYTE_RANGE_TO = 1;
extern KBox<KByte> BYTE_CACHE[] = {
{{}, -1},
{{}, 0},
{{}, 1},
};
extern const KChar CHAR_RANGE_FROM = 0;
extern const KChar CHAR_RANGE_TO = 2;
extern KBox<KChar> CHAR_CACHE[] = {
{{}, 0},
{{}, 1},
{{}, 2},
};
extern const KShort SHORT_RANGE_FROM = -1;
extern const KShort SHORT_RANGE_TO = 1;
extern KBox<KShort> SHORT_CACHE[] = {
{{}, -1},
{{}, 0},
{{}, 1},
};
extern const KInt INT_RANGE_FROM = -1;
extern const KInt INT_RANGE_TO = 1;
extern KBox<KInt> INT_CACHE[] = {
{{}, -1},
{{}, 0},
{{}, 1},
};
extern const KLong LONG_RANGE_FROM = -1;
extern const KLong LONG_RANGE_TO = 1;
extern KBox<KLong> LONG_CACHE[] = {
{{}, -1},
{{}, 0},
{{}, 1},
};
RUNTIME_NORETURN OBJ_GETTER(Kotlin_Throwable_getMessage, KRef throwable) {
THROW_NOT_IMPLEMENTED
}
} // extern "C"
@@ -0,0 +1,11 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#ifndef RUNTIME_COMPILER_GENERATED_H
#define RUNTIME_COMPILER_GENERATED_H
#define THROW_NOT_IMPLEMENTED throw std::runtime_error("Not implemented for tests");
#endif //RUNTIME_COMPILER_GENERATED_H
@@ -0,0 +1,71 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#if KONAN_OBJC_INTEROP
#include <Foundation/NSObject.h>
#include <objc/runtime.h>
#include "CompilerGenerated.h"
#include "Types.h"
extern "C" {
Class Kotlin_Interop_getObjCClass(const char* name) {
Class result = objc_lookUpClass(name);
if (result == nil) {
// GTest can display error messages of C++ exceptions so we use them instead of ObjC ones.
throw std::invalid_argument("Incorrect class name");
}
return result;
}
RUNTIME_NORETURN OBJ_GETTER0(Kotlin_NSEnumeratorAsKIterator_create) {
THROW_NOT_IMPLEMENTED
}
void Kotlin_NSEnumeratorAsKIterator_done(KRef thiz) {
THROW_NOT_IMPLEMENTED
}
void Kotlin_NSEnumeratorAsKIterator_setNext(KRef thiz, KRef value) {
THROW_NOT_IMPLEMENTED
}
RUNTIME_NORETURN OBJ_GETTER(Kotlin_ObjCExport_NSErrorAsExceptionImpl, KRef message, KRef error) {
THROW_NOT_IMPLEMENTED
}
void Kotlin_ObjCExport_ThrowCollectionConcurrentModification() {
THROW_NOT_IMPLEMENTED
}
void Kotlin_ObjCExport_ThrowCollectionTooLarge() {
THROW_NOT_IMPLEMENTED
}
typedef OBJ_GETTER((*convertReferenceFromObjC), id obj);
extern convertReferenceFromObjC Kotlin_ObjCExport_blockToFunctionConverters[] = {};
extern int Kotlin_ObjCExport_blockToFunctionConverters_size = 0;
RUNTIME_NORETURN OBJ_GETTER(Kotlin_ObjCExport_createContinuationArgumentImpl, KRef completionHolder, const TypeInfo** exceptionTypes) {
THROW_NOT_IMPLEMENTED
}
RUNTIME_NORETURN OBJ_GETTER(Kotlin_ObjCExport_getWrappedError, KRef throwable) {
THROW_NOT_IMPLEMENTED
}
void Kotlin_ObjCExport_resumeContinuationFailure(KRef continuation, KRef exception) {
THROW_NOT_IMPLEMENTED
}
void Kotlin_ObjCExport_resumeContinuationSuccess(KRef continuation, KRef result) {
THROW_NOT_IMPLEMENTED
}
} // extern "C"
#endif // KONAN_OBJC_INTEROP
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "gmock/gmock.h"
#include "gtest/gtest.h"
int main(int argc, char **argv) {
testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
@@ -28,6 +28,8 @@ open class Command(initialCommand: List<String>) {
constructor(vararg command: String) : this(command.toList<String>())
protected val command = initialCommand.toMutableList()
val argsWithExecutable: List<String> = command
val args: List<String>
get() = command.drop(1)