Коммит b7f8b0f0 создал по автору Ilya Pankratov's avatar Ilya Pankratov
Просмотр файлов

Add tests for list conversion between kotlin and Qt

владелец 33baa84f
/**
* SPDX-FileCopyrightText: Copyright 2025 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
package ru.aurora.kmp.qtbindings.ksp
import ru.aurora.kmp.qtbindings.ksp.internal.*
import com.tschuchort.compiletesting.SourceFile
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
import kotlin.io.path.Path
import kotlin.test.Test
@OptIn(ExperimentalCompilerApi::class)
class ListConversionTests : BaseCompilationTest() {
@Test
fun `check that helper functions for list manipulating have been generated`() {
val expectedKotlinFile = GeneratedFile(
Path("ru/aurora/kmp/qtbindings/generated/Generated.kt"), """
package ru.aurora.kmp.qtbindings.generated
import kotlin.Any
import kotlin.Boolean
import kotlin.Byte
import kotlin.Char
import kotlin.Double
import kotlin.Float
import kotlin.Int
import kotlin.Long
import kotlin.OptIn
import kotlin.Short
import kotlin.String
import kotlin.UByte
import kotlin.UInt
import kotlin.ULong
import kotlin.UShort
import kotlin.Unit
import kotlin.collections.List
import kotlin.collections.MutableList
import kotlinx.cinterop.COpaquePointer
import kotlinx.cinterop.ExperimentalForeignApi
import ru.aurora.kmp.qtbindings.listGetElementByIndexPredefined
import ru.aurora.kmp.qtbindings.listGetSizePredefined
import ru.aurora.kmp.qtbindings.mutableListAddClassPredefined
import ru.aurora.kmp.qtbindings.mutableListAddSimpleElementPredefined
import ru.aurora.kmp.qtbindings.mutableListCreateEmptyPredefined
import ru.aurora.kmp.qtbindings.mutableListToListPredefined
@OptIn(ExperimentalForeignApi::class)
public fun listGetSize(ptr: COpaquePointer?): Int = listGetSizePredefined(ptr)
@OptIn(ExperimentalForeignApi::class)
public fun listGetElementByIndex(ptr: COpaquePointer?, index: Int): COpaquePointer? = listGetElementByIndexPredefined(ptr, index)
public fun mutableListCreateEmpty(): MutableList<Any> = mutableListCreateEmptyPredefined()
@OptIn(ExperimentalForeignApi::class)
public fun mutableListAddClass(list: MutableList<Any>, ptr: COpaquePointer): Unit = mutableListAddClassPredefined(list, ptr)
public fun mutableListAddUnit(l: MutableList<Unit>, el: Unit): Unit = mutableListAddSimpleElementPredefined(l, el)
public fun mutableListAddBoolean(l: MutableList<Boolean>, el: Boolean): Unit = mutableListAddSimpleElementPredefined(l, el)
public fun mutableListAddChar(l: MutableList<Char>, el: Char): Unit = mutableListAddSimpleElementPredefined(l, el)
public fun mutableListAddByte(l: MutableList<Byte>, el: Byte): Unit = mutableListAddSimpleElementPredefined(l, el)
public fun mutableListAddShort(l: MutableList<Short>, el: Short): Unit = mutableListAddSimpleElementPredefined(l, el)
public fun mutableListAddInt(l: MutableList<Int>, el: Int): Unit = mutableListAddSimpleElementPredefined(l, el)
public fun mutableListAddLong(l: MutableList<Long>, el: Long): Unit = mutableListAddSimpleElementPredefined(l, el)
public fun mutableListAddUByte(l: MutableList<UByte>, el: UByte): Unit = mutableListAddSimpleElementPredefined(l, el)
public fun mutableListAddUShort(l: MutableList<UShort>, el: UShort): Unit = mutableListAddSimpleElementPredefined(l, el)
public fun mutableListAddUInt(l: MutableList<UInt>, el: UInt): Unit = mutableListAddSimpleElementPredefined(l, el)
public fun mutableListAddULong(l: MutableList<ULong>, el: ULong): Unit = mutableListAddSimpleElementPredefined(l, el)
public fun mutableListAddFloat(l: MutableList<Float>, el: Float): Unit = mutableListAddSimpleElementPredefined(l, el)
public fun mutableListAddDouble(l: MutableList<Double>, el: Double): Unit = mutableListAddSimpleElementPredefined(l, el)
public fun mutableListAddString(l: MutableList<String>, el: String): Unit = mutableListAddSimpleElementPredefined(l, el)
public fun mutableListToList(mutableList: MutableList<*>): List<*> = mutableListToListPredefined(mutableList)
""".trimIndent()
)
run(listOf(), listOf(expectedKotlinFile))
}
@Test
fun `check that helper function are generated in anonymous namespace`() {
val kotlinSource = SourceFile.new(
"List.kt", """
package test
import ru.aurora.kmp.qtbindings.QtExport
@QtExport
fun intList() : List<Int> { return emptyList() }
""".trimIndent()
)
val compilation = run(kotlinSource)
compilation.assertThatFileHasContent(
Path("test/List.cpp"), "namespace {", "} /* anonymous */"
)
compileAndRunQtTestCase(compilation).assertIsOk() // Check that it can be compiled
}
@Test
fun `check that Boolean List can be converted between Kotlin and Qt`() {
val kotlinSource = SourceFile.new(
"List.kt", """
package test
import ru.aurora.kmp.qtbindings.QtExport
@QtExport
data class TestBoolListConversion(var list: List<Boolean>)
""".trimIndent()
)
val qtTestCase = """
#include <QDebug>
#include "List.hpp"
using namespace test;
int main() {
{
// Qt -> Kotlin
TestBoolListConversion conversion = TestBoolListConversion(QList<bool>{true, false, true, false});
// Kotlin -> Qt
qInfo() << conversion.getList();
// Qt -> Kotlin again
QList<bool> list = {true, true, true};
conversion.setList(list);
// Kotlin -> Qt again
qInfo() << conversion.getList();
}
return 0;
}
""".trimIndent()
val compilation = run(kotlinSource)
compilation.assertThatFileHasContent(
Path("test/List.hpp"), """
| QList<bool> getList() const;
| void setList(const QList<bool> &set);
""".trimMargin()
)
compilation.assertThatFileHasContent(
Path("test/List.cpp"), """
libshared_kref_kotlin_collections_List fromQListBool(const QList<bool> &qtList)
{
auto s = libshared_symbols();
auto ns = s->kotlin.root.ru.aurora.kmp.qtbindings.generated;
auto kotlinResult = ns.mutableListCreateEmpty();
for (const auto &qtElement : qtList) {
ns.mutableListAddBoolean(kotlinResult, qtElement);
}
auto kotlinResult1 = ns.mutableListToList(kotlinResult);
if (kotlinResult.pinned != nullptr) {
s->DisposeStablePointer(kotlinResult.pinned);
}
return kotlinResult1;
}
""".trimIndent(), """
QList<bool> toQListBool(libshared_kref_kotlin_collections_List kotlinList)
{
QList<bool> qtResult;
auto s = libshared_symbols();
auto ns = s->kotlin.root.ru.aurora.kmp.qtbindings.generated;
for (int i = 0; i < ns.listGetSize(kotlinList.pinned); i++) {
auto ptr = ns.listGetElementByIndex(kotlinList.pinned, i);
qtResult.append((bool) ptr);
}
return qtResult;
}
""".trimIndent(), """
void TestBoolListConversion::setList(const QList<bool> &set)
{
auto s = libshared_symbols();
auto ns = s->kotlin.root.test.TestBoolListConversion;
auto kotlinSet = fromQListBool(set);
ns.set_list(d_ptr, kotlinSet);
if (kotlinSet.pinned != nullptr) {
s->DisposeStablePointer(kotlinSet.pinned);
}
}
""".trimIndent(), """
QList<bool> TestBoolListConversion::getList() const
{
auto s = libshared_symbols();
auto ns = s->kotlin.root.test.TestBoolListConversion;
auto kotlinResult = ns.get_list(d_ptr);
auto qtResult = toQListBool(kotlinResult);
if (kotlinResult.pinned != nullptr) {
s->DisposeStablePointer(kotlinResult.pinned);
}
return qtResult;
}
""".trimIndent()
)
val qtTestCaseResult = compileAndRunQtTestCase(compilation, qtTestCase)
qtTestCaseResult.assertIsOk()
qtTestCaseResult.assertStderrHas("(true, false, true, false)", "(true, true, true)")
}
@Test
fun `check that String Mutable List can be converted between Kotlin and Qt`() {
val kotlinSource = SourceFile.new(
"List.kt", """
package test
import ru.aurora.kmp.qtbindings.QtExport
@QtExport
data class TestStringMutableListConversion(var list: MutableList<String>)
""".trimIndent()
)
val qtTestCase = """
#include <QDebug>
#include "List.hpp"
using namespace test;
int main() {
{
// Qt -> Kotlin
TestStringMutableListConversion conversion = TestStringMutableListConversion(QList<QString>{"one", "two", "three", "four"});
// Kotlin -> Qt
qInfo() << conversion.getList();
// Qt -> Kotlin again
QList<QString> list = {"five", "six" };
conversion.setList(list);
// Kotlin -> Qt again
qInfo() << conversion.getList();
}
return 0;
}
""".trimIndent()
val compilation = run(kotlinSource)
compilation.assertThatFileHasContent(
Path("test/List.hpp"), """
| QList<QString> getList() const;
| void setList(const QList<QString> &set);
""".trimMargin()
)
compilation.assertThatFileHasContent(
Path("test/List.cpp"), """
QList<QString> toMutableQListQString(libshared_kref_kotlin_collections_MutableList kotlinList)
{
QList<QString> qtResult;
auto s = libshared_symbols();
auto ns = s->kotlin.root.ru.aurora.kmp.qtbindings.generated;
for (int i = 0; i < ns.listGetSize(kotlinList.pinned); i++) {
auto ptr = ns.listGetElementByIndex(kotlinList.pinned, i);
qtResult.append(ptr != nullptr ? QString::fromUtf8((char *) ptr) : QString());
if (ptr != nullptr) {
s->DisposeString((char *) ptr);
}
}
return qtResult;
}
""".trimIndent(), """
libshared_kref_kotlin_collections_MutableList fromMutableQListQString(const QList<QString> &qtList)
{
auto s = libshared_symbols();
auto ns = s->kotlin.root.ru.aurora.kmp.qtbindings.generated;
auto kotlinResult = ns.mutableListCreateEmpty();
for (const auto &qtElement : qtList) {
auto byteArrayQtElement = qtElement.toUtf8();
auto kotlinElement = byteArrayQtElement.data();
ns.mutableListAddString(kotlinResult, kotlinElement);
}
return kotlinResult;
}
""".trimIndent(), """
QList<QString> TestStringMutableListConversion::getList() const
{
auto s = libshared_symbols();
auto ns = s->kotlin.root.test.TestStringMutableListConversion;
auto kotlinResult = ns.get_list(d_ptr);
auto qtResult = toMutableQListQString(kotlinResult);
if (kotlinResult.pinned != nullptr) {
s->DisposeStablePointer(kotlinResult.pinned);
}
return qtResult;
}
""".trimIndent(), """
void TestStringMutableListConversion::setList(const QList<QString> &set)
{
auto s = libshared_symbols();
auto ns = s->kotlin.root.test.TestStringMutableListConversion;
auto kotlinSet = fromMutableQListQString(set);
ns.set_list(d_ptr, kotlinSet);
if (kotlinSet.pinned != nullptr) {
s->DisposeStablePointer(kotlinSet.pinned);
}
}
""".trimIndent()
)
val qtTestCaseResult = compileAndRunQtTestCase(compilation, qtTestCase)
qtTestCaseResult.assertIsOk()
qtTestCaseResult.assertStderrHas("(\"one\", \"two\", \"three\", \"four\")", "(\"five\", \"six\")")
}
@Test
fun `check that Class Mutable List can be converted between Kotlin and Qt`() {
val kotlinSource1 = SourceFile.new(
"TestData.kt", """
package another
import ru.aurora.kmp.qtbindings.QtExport
@QtExport
data class TestData(var str: String, var integer: Int)
""".trimIndent()
)
val kotlinSource2 = SourceFile.new(
"List.kt", """
package test
import another.TestData
import ru.aurora.kmp.qtbindings.QtExport
@QtExport
data class TestClassMutableListConversion(var list: MutableList<TestData>)
""".trimIndent()
)
val qtTestCase = """
#include <QDebug>
#include "List.hpp"
using namespace test;
using namespace another;
void printData(const QList<TestData> &data) {
int counter = 0;
for (auto &el: data) {
qInfo() << "[" << counter << "]" << "(" << el.getStr() << el.getInteger() << ")";
counter++;
}
}
int main() {
{
// Qt -> Kotlin
TestClassMutableListConversion conversion = TestClassMutableListConversion(QList<TestData>{TestData{"one", 1}, TestData{"two", 2}});
// Kotlin -> Qt
printData(conversion.getList());
// Qt -> Kotlin again
QList<TestData> list = { TestData{"three", 3} };
conversion.setList(list);
// Kotlin -> Qt again
printData(conversion.getList());
}
return 0;
}
""".trimIndent()
val compilation = run(listOf(kotlinSource1, kotlinSource2))
// Also it checks that TestData is fully qualified (another::TestData)
compilation.assertThatFileHasContent(
Path("test/List.hpp"), """
| QList<another::TestData> getList() const;
| void setList(const QList<another::TestData> &set);
""".trimMargin()
)
compilation.assertThatFileHasContent(
Path("test/List.cpp"), """
QList<another::TestData> toMutableQListAnotherTestData(libshared_kref_kotlin_collections_MutableList kotlinList)
{
QList<another::TestData> qtResult;
auto s = libshared_symbols();
auto ns = s->kotlin.root.ru.aurora.kmp.qtbindings.generated;
for (int i = 0; i < ns.listGetSize(kotlinList.pinned); i++) {
auto ptr = ns.listGetElementByIndex(kotlinList.pinned, i);
qtResult.append(another::TestData(libshared_kref_another_TestData{ ptr }));
}
return qtResult;
}
""".trimIndent(), """
libshared_kref_kotlin_collections_MutableList fromMutableQListAnotherTestData(const QList<another::TestData> &qtList)
{
auto s = libshared_symbols();
auto ns = s->kotlin.root.ru.aurora.kmp.qtbindings.generated;
auto kotlinResult = ns.mutableListCreateEmpty();
for (const auto &qtElement : qtList) {
auto kotlinElement = qtElement.unsafeKotlinPointer();
ns.mutableListAddClass(kotlinResult, kotlinElement.pinned);
}
return kotlinResult;
}
""".trimIndent(), """
QList<another::TestData> TestClassMutableListConversion::getList() const
{
auto s = libshared_symbols();
auto ns = s->kotlin.root.test.TestClassMutableListConversion;
auto kotlinResult = ns.get_list(d_ptr);
auto qtResult = toMutableQListAnotherTestData(kotlinResult);
if (kotlinResult.pinned != nullptr) {
s->DisposeStablePointer(kotlinResult.pinned);
}
return qtResult;
}
""".trimIndent(), """
void TestClassMutableListConversion::setList(const QList<another::TestData> &set)
{
auto s = libshared_symbols();
auto ns = s->kotlin.root.test.TestClassMutableListConversion;
auto kotlinSet = fromMutableQListAnotherTestData(set);
ns.set_list(d_ptr, kotlinSet);
if (kotlinSet.pinned != nullptr) {
s->DisposeStablePointer(kotlinSet.pinned);
}
}
""".trimIndent()
)
val qtTestCaseResult = compileAndRunQtTestCase(compilation, qtTestCase)
qtTestCaseResult.assertIsOk()
qtTestCaseResult.assertStderrHas(
"""
[ 0 ] ( "one" 1 )
[ 1 ] ( "two" 2 )
[ 0 ] ( "three" 3 )
""".trimIndent()
)
}
@Test
fun `check that String List List List can be converted between Kotlin and Qt`() {
val kotlinSource = SourceFile.new(
"List.kt", """
package test
import ru.aurora.kmp.qtbindings.QtExport
@QtExport
data class TestStringListListListConversion(var list: List<List<List<String>>>)
""".trimIndent()
)
val qtTestCase = """
#include <QDebug>
#include "List.hpp"
using namespace test;
int main() {
{
// Qt -> Kotlin
QList<QList<QList<QString>>> list1 = { { { "One" }, { "two" } }, { { "three" }, { "four" } }};
TestStringListListListConversion conversion = { list1 };
// Kotlin -> Qt
auto l1 = conversion.getList();
qInfo() << l1;
// Qt -> Kotlin again
QList<QList<QList<QString>>> list2 = { { { "five" }, { "six" } }, { { "seven" }, { "eight" } }};
conversion.setList(list2);
// Kotlin -> Qt again
auto l2 = conversion.getList();
qInfo() << l2;
}
return 0;
}
""".trimIndent()
val compilation = run(kotlinSource)
compilation.assertThatFileHasContent(
Path("test/List.hpp"), """
| QList<QList<QList<QString>>> getList() const;
| void setList(const QList<QList<QList<QString>>> &set);
""".trimMargin()
)
compilation.assertThatFileHasContent(
Path("test/List.cpp"), """
QList<QString> toQListQString(libshared_kref_kotlin_collections_List kotlinList)
{
QList<QString> qtResult;
auto s = libshared_symbols();
auto ns = s->kotlin.root.ru.aurora.kmp.qtbindings.generated;
for (int i = 0; i < ns.listGetSize(kotlinList.pinned); i++) {
auto ptr = ns.listGetElementByIndex(kotlinList.pinned, i);
qtResult.append(ptr != nullptr ? QString::fromUtf8((char *) ptr) : QString());
if (ptr != nullptr) {
s->DisposeString((char *) ptr);
}
}
return qtResult;
}
QList<QList<QString>> toQListQListQString(libshared_kref_kotlin_collections_List kotlinList)
{
QList<QList<QString>> qtResult;
auto s = libshared_symbols();
auto ns = s->kotlin.root.ru.aurora.kmp.qtbindings.generated;
for (int i = 0; i < ns.listGetSize(kotlinList.pinned); i++) {
auto ptr = ns.listGetElementByIndex(kotlinList.pinned, i);
qtResult.append(toQListQString(libshared_kref_kotlin_collections_List{ ptr }));
if (ptr != nullptr) {
s->DisposeStablePointer(ptr);
}
}
return qtResult;
}
QList<QList<QList<QString>>> toQListQListQListQString(libshared_kref_kotlin_collections_List kotlinList)
{
QList<QList<QList<QString>>> qtResult;
auto s = libshared_symbols();
auto ns = s->kotlin.root.ru.aurora.kmp.qtbindings.generated;
for (int i = 0; i < ns.listGetSize(kotlinList.pinned); i++) {
auto ptr = ns.listGetElementByIndex(kotlinList.pinned, i);
qtResult.append(toQListQListQString(libshared_kref_kotlin_collections_List{ ptr }));
if (ptr != nullptr) {
s->DisposeStablePointer(ptr);
}
}
return qtResult;
}
libshared_kref_kotlin_collections_List fromQListQString(const QList<QString> &qtList)
{
auto s = libshared_symbols();
auto ns = s->kotlin.root.ru.aurora.kmp.qtbindings.generated;
auto kotlinResult = ns.mutableListCreateEmpty();
for (const auto &qtElement : qtList) {
auto byteArrayQtElement = qtElement.toUtf8();
auto kotlinElement = byteArrayQtElement.data();
ns.mutableListAddString(kotlinResult, kotlinElement);
}
auto kotlinResult1 = ns.mutableListToList(kotlinResult);
if (kotlinResult.pinned != nullptr) {
s->DisposeStablePointer(kotlinResult.pinned);
}
return kotlinResult1;
}
libshared_kref_kotlin_collections_List fromQListQListQString(const QList<QList<QString>> &qtList)
{
auto s = libshared_symbols();
auto ns = s->kotlin.root.ru.aurora.kmp.qtbindings.generated;
auto kotlinResult = ns.mutableListCreateEmpty();
for (const auto &qtElement : qtList) {
auto kotlinElement = fromQListQString(qtElement);
ns.mutableListAddClass(kotlinResult, kotlinElement.pinned);
if (kotlinElement.pinned != nullptr) {
s->DisposeStablePointer(kotlinElement.pinned);
}
}
auto kotlinResult1 = ns.mutableListToList(kotlinResult);
if (kotlinResult.pinned != nullptr) {
s->DisposeStablePointer(kotlinResult.pinned);
}
return kotlinResult1;
}
libshared_kref_kotlin_collections_List fromQListQListQListQString(const QList<QList<QList<QString>>> &qtList)
{
auto s = libshared_symbols();
auto ns = s->kotlin.root.ru.aurora.kmp.qtbindings.generated;
auto kotlinResult = ns.mutableListCreateEmpty();
for (const auto &qtElement : qtList) {
auto kotlinElement = fromQListQListQString(qtElement);
ns.mutableListAddClass(kotlinResult, kotlinElement.pinned);
if (kotlinElement.pinned != nullptr) {
s->DisposeStablePointer(kotlinElement.pinned);
}
}
auto kotlinResult1 = ns.mutableListToList(kotlinResult);
if (kotlinResult.pinned != nullptr) {
s->DisposeStablePointer(kotlinResult.pinned);
}
return kotlinResult1;
}
""".trimIndent(), """
QList<QList<QList<QString>>> TestStringListListListConversion::getList() const
{
auto s = libshared_symbols();
auto ns = s->kotlin.root.test.TestStringListListListConversion;
auto kotlinResult = ns.get_list(d_ptr);
auto qtResult = toQListQListQListQString(kotlinResult);
if (kotlinResult.pinned != nullptr) {
s->DisposeStablePointer(kotlinResult.pinned);
}
return qtResult;
}
""".trimIndent(), """
void TestStringListListListConversion::setList(const QList<QList<QList<QString>>> &set)
{
auto s = libshared_symbols();
auto ns = s->kotlin.root.test.TestStringListListListConversion;
auto kotlinSet = fromQListQListQListQString(set);
ns.set_list(d_ptr, kotlinSet);
if (kotlinSet.pinned != nullptr) {
s->DisposeStablePointer(kotlinSet.pinned);
}
}
""".trimIndent()
)
val qtTestCaseResult = compileAndRunQtTestCase(compilation, qtTestCase)
qtTestCaseResult.assertIsOk()
qtTestCaseResult.assertStderrHas(
"""
((("One"), ("two")), (("three"), ("four")))
((("five"), ("six")), (("seven"), ("eight")))
""".trimIndent()
)
}
}
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать