Открыть боковую панель
Aurora OS
Kotlin Multiplatform
QtBindings
Коммиты
b7f8b0f0
Коммит
b7f8b0f0
создал
Мар 25, 2025
по автору
Ilya Pankratov
Просмотр файлов
Add tests for list conversion between kotlin and Qt
владелец
33baa84f
Изменения
1
Скрыть пробелы
Построчно
Рядом
qtbindings-ksp/src/test/kotlin/ru/aurora/kmp/qtbindings/ksp/ListConversionTests.kt
0 → 100644
Просмотр файла @
b7f8b0f0
/**
* 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.
Сначала завершите редактирование этого сообщения!
Отмена
Пожалуйста,
зарегистрируйтесь
или
войдите
чтобы прокомментировать