summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi-example-rondpoint/tests/bindings
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/uniffi-example-rondpoint/tests/bindings')
-rw-r--r--third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.kts250
-rw-r--r--third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.py146
-rw-r--r--third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.rb142
-rw-r--r--third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.swift232
4 files changed, 770 insertions, 0 deletions
diff --git a/third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.kts b/third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.kts
new file mode 100644
index 0000000000..cc5ddf2a86
--- /dev/null
+++ b/third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.kts
@@ -0,0 +1,250 @@
+import uniffi.rondpoint.*
+
+val dico = Dictionnaire(Enumeration.DEUX, true, 0u, 123456789u)
+val copyDico = copieDictionnaire(dico)
+assert(dico == copyDico)
+
+assert(copieEnumeration(Enumeration.DEUX) == Enumeration.DEUX)
+assert(copieEnumerations(listOf(Enumeration.UN, Enumeration.DEUX)) == listOf(Enumeration.UN, Enumeration.DEUX))
+assert(copieCarte(mapOf(
+ "0" to EnumerationAvecDonnees.Zero,
+ "1" to EnumerationAvecDonnees.Un(1u),
+ "2" to EnumerationAvecDonnees.Deux(2u, "deux")
+)) == mapOf(
+ "0" to EnumerationAvecDonnees.Zero,
+ "1" to EnumerationAvecDonnees.Un(1u),
+ "2" to EnumerationAvecDonnees.Deux(2u, "deux")
+))
+
+val var1: EnumerationAvecDonnees = EnumerationAvecDonnees.Zero
+val var2: EnumerationAvecDonnees = EnumerationAvecDonnees.Un(1u)
+val var3: EnumerationAvecDonnees = EnumerationAvecDonnees.Un(2u)
+assert(var1 != var2)
+assert(var2 != var3)
+assert(var1 == EnumerationAvecDonnees.Zero)
+assert(var1 != EnumerationAvecDonnees.Un(1u))
+assert(var2 == EnumerationAvecDonnees.Un(1u))
+
+assert(switcheroo(false))
+
+// Test the roundtrip across the FFI.
+// This shows that the values we send come back in exactly the same state as we sent them.
+// i.e. it shows that lowering from kotlin and lifting into rust is symmetrical with
+// lowering from rust and lifting into kotlin.
+val rt = Retourneur()
+
+fun <T> List<T>.affirmAllerRetour(fn: (T) -> T) {
+ this.forEach { v ->
+ assert(fn.invoke(v) == v) { "$fn($v)" }
+ }
+}
+
+// Booleans
+listOf(true, false).affirmAllerRetour(rt::identiqueBoolean)
+
+// Bytes.
+listOf(Byte.MIN_VALUE, Byte.MAX_VALUE).affirmAllerRetour(rt::identiqueI8)
+listOf(0x00, 0xFF).map { it.toUByte() }.affirmAllerRetour(rt::identiqueU8)
+
+// Shorts
+listOf(Short.MIN_VALUE, Short.MAX_VALUE).affirmAllerRetour(rt::identiqueI16)
+listOf(0x0000, 0xFFFF).map { it.toUShort() }.affirmAllerRetour(rt::identiqueU16)
+
+// Ints
+listOf(0, 1, -1, Int.MIN_VALUE, Int.MAX_VALUE).affirmAllerRetour(rt::identiqueI32)
+listOf(0x00000000, 0xFFFFFFFF).map { it.toUInt() }.affirmAllerRetour(rt::identiqueU32)
+
+// Longs
+listOf(0L, 1L, -1L, Long.MIN_VALUE, Long.MAX_VALUE).affirmAllerRetour(rt::identiqueI64)
+listOf(0u, 1u, ULong.MIN_VALUE, ULong.MAX_VALUE).affirmAllerRetour(rt::identiqueU64)
+
+// Floats
+listOf(0.0F, 0.5F, 0.25F, Float.MIN_VALUE, Float.MAX_VALUE).affirmAllerRetour(rt::identiqueFloat)
+
+// Doubles
+listOf(0.0, 1.0, Double.MIN_VALUE, Double.MAX_VALUE).affirmAllerRetour(rt::identiqueDouble)
+
+// Strings
+listOf("", "abc", "null\u0000byte", "été", "ښي لاس ته لوستلو لوستل", "😻emoji 👨‍👧‍👦multi-emoji, 🇨🇭a flag, a canal, panama")
+ .affirmAllerRetour(rt::identiqueString)
+
+listOf(-1, 0, 1).map { DictionnaireNombresSignes(it.toByte(), it.toShort(), it.toInt(), it.toLong()) }
+ .affirmAllerRetour(rt::identiqueNombresSignes)
+
+listOf(0, 1).map { DictionnaireNombres(it.toUByte(), it.toUShort(), it.toUInt(), it.toULong()) }
+ .affirmAllerRetour(rt::identiqueNombres)
+
+
+rt.destroy()
+
+// Test one way across the FFI.
+//
+// We send one representation of a value to lib.rs, and it transforms it into another, a string.
+// lib.rs sends the string back, and then we compare here in kotlin.
+//
+// This shows that the values are transformed into strings the same way in both kotlin and rust.
+// i.e. if we assume that the string return works (we test this assumption elsewhere)
+// we show that lowering from kotlin and lifting into rust has values that both kotlin and rust
+// both stringify in the same way. i.e. the same values.
+//
+// If we roundtripping proves the symmetry of our lowering/lifting from here to rust, and lowering/lifting from rust t here,
+// and this convinces us that lowering/lifting from here to rust is correct, then
+// together, we've shown the correctness of the return leg.
+val st = Stringifier()
+
+typealias StringyEquals<T> = (observed: String, expected: T) -> Boolean
+fun <T> List<T>.affirmEnchaine(
+ fn: (T) -> String,
+ equals: StringyEquals<T> = { obs, exp -> obs == exp.toString() }
+) {
+ this.forEach { exp ->
+ val obs = fn.invoke(exp)
+ assert(equals(obs, exp)) { "$fn($exp): observed=$obs, expected=$exp" }
+ }
+}
+
+// Test the efficacy of the string transport from rust. If this fails, but everything else
+// works, then things are very weird.
+val wellKnown = st.wellKnownString("kotlin")
+assert("uniffi 💚 kotlin!" == wellKnown) { "wellKnownString 'uniffi 💚 kotlin!' == '$wellKnown'" }
+
+// Booleans
+listOf(true, false).affirmEnchaine(st::toStringBoolean)
+
+// Bytes.
+listOf(Byte.MIN_VALUE, Byte.MAX_VALUE).affirmEnchaine(st::toStringI8)
+listOf(UByte.MIN_VALUE, UByte.MAX_VALUE).affirmEnchaine(st::toStringU8)
+
+// Shorts
+listOf(Short.MIN_VALUE, Short.MAX_VALUE).affirmEnchaine(st::toStringI16)
+listOf(UShort.MIN_VALUE, UShort.MAX_VALUE).affirmEnchaine(st::toStringU16)
+
+// Ints
+listOf(0, 1, -1, Int.MIN_VALUE, Int.MAX_VALUE).affirmEnchaine(st::toStringI32)
+listOf(0u, 1u, UInt.MIN_VALUE, UInt.MAX_VALUE).affirmEnchaine(st::toStringU32)
+
+// Longs
+listOf(0L, 1L, -1L, Long.MIN_VALUE, Long.MAX_VALUE).affirmEnchaine(st::toStringI64)
+listOf(0u, 1u, ULong.MIN_VALUE, ULong.MAX_VALUE).affirmEnchaine(st::toStringU64)
+
+// Floats
+// MIN_VALUE is 1.4E-45. Accuracy and formatting get weird at small sizes.
+listOf(0.0F, 1.0F, -1.0F, Float.MIN_VALUE, Float.MAX_VALUE).affirmEnchaine(st::toStringFloat) { s, n -> s.toFloat() == n }
+
+// Doubles
+// MIN_VALUE is 4.9E-324. Accuracy and formatting get weird at small sizes.
+listOf(0.0, 1.0, -1.0, Double.MIN_VALUE, Double.MAX_VALUE).affirmEnchaine(st::toStringDouble) { s, n -> s.toDouble() == n }
+
+st.destroy()
+
+// Prove to ourselves that default arguments are being used.
+// Step 1: call the methods without arguments, and check against the UDL.
+val op = Optionneur()
+
+assert(op.sinonString() == "default")
+
+assert(op.sinonBoolean() == false)
+
+assert(op.sinonSequence() == listOf<String>())
+
+// optionals
+assert(op.sinonNull() == null)
+assert(op.sinonZero() == 0)
+
+// decimal integers
+assert(op.sinonI8Dec() == (-42).toByte())
+assert(op.sinonU8Dec() == 42.toUByte())
+assert(op.sinonI16Dec() == 42.toShort())
+assert(op.sinonU16Dec() == 42.toUShort())
+assert(op.sinonI32Dec() == 42)
+assert(op.sinonU32Dec() == 42.toUInt())
+assert(op.sinonI64Dec() == 42L)
+assert(op.sinonU64Dec() == 42uL)
+
+// hexadecimal integers
+assert(op.sinonI8Hex() == (-0x7f).toByte())
+assert(op.sinonU8Hex() == 0xff.toUByte())
+assert(op.sinonI16Hex() == 0x7f.toShort())
+assert(op.sinonU16Hex() == 0xffff.toUShort())
+assert(op.sinonI32Hex() == 0x7fffffff)
+assert(op.sinonU32Hex() == 0xffffffff.toUInt())
+assert(op.sinonI64Hex() == 0x7fffffffffffffffL)
+assert(op.sinonU64Hex() == 0xffffffffffffffffuL)
+
+// octal integers
+assert(op.sinonU32Oct() == 493u) // 0o755
+
+// floats
+assert(op.sinonF32() == 42.0f)
+assert(op.sinonF64() == 42.1)
+
+// enums
+assert(op.sinonEnum() == Enumeration.TROIS)
+
+// Step 2. Convince ourselves that if we pass something else, then that changes the output.
+// We have shown something coming out of the sinon methods, but without eyeballing the Rust
+// we can't be sure that the arguments will change the return value.
+listOf("foo", "bar").affirmAllerRetour(op::sinonString)
+listOf(true, false).affirmAllerRetour(op::sinonBoolean)
+listOf(listOf("a", "b"), listOf()).affirmAllerRetour(op::sinonSequence)
+
+// optionals
+listOf("0", "1").affirmAllerRetour(op::sinonNull)
+listOf(0, 1).affirmAllerRetour(op::sinonZero)
+
+// integers
+listOf(0, 1).map { it.toUByte() }.affirmAllerRetour(op::sinonU8Dec)
+listOf(0, 1).map { it.toByte() }.affirmAllerRetour(op::sinonI8Dec)
+listOf(0, 1).map { it.toUShort() }.affirmAllerRetour(op::sinonU16Dec)
+listOf(0, 1).map { it.toShort() }.affirmAllerRetour(op::sinonI16Dec)
+listOf(0, 1).map { it.toUInt() }.affirmAllerRetour(op::sinonU32Dec)
+listOf(0, 1).map { it.toInt() }.affirmAllerRetour(op::sinonI32Dec)
+listOf(0, 1).map { it.toULong() }.affirmAllerRetour(op::sinonU64Dec)
+listOf(0, 1).map { it.toLong() }.affirmAllerRetour(op::sinonI64Dec)
+
+listOf(0, 1).map { it.toUByte() }.affirmAllerRetour(op::sinonU8Hex)
+listOf(0, 1).map { it.toByte() }.affirmAllerRetour(op::sinonI8Hex)
+listOf(0, 1).map { it.toUShort() }.affirmAllerRetour(op::sinonU16Hex)
+listOf(0, 1).map { it.toShort() }.affirmAllerRetour(op::sinonI16Hex)
+listOf(0, 1).map { it.toUInt() }.affirmAllerRetour(op::sinonU32Hex)
+listOf(0, 1).map { it.toInt() }.affirmAllerRetour(op::sinonI32Hex)
+listOf(0, 1).map { it.toULong() }.affirmAllerRetour(op::sinonU64Hex)
+listOf(0, 1).map { it.toLong() }.affirmAllerRetour(op::sinonI64Hex)
+
+listOf(0, 1).map { it.toUInt() }.affirmAllerRetour(op::sinonU32Oct)
+
+// floats
+listOf(0.0f, 1.0f).affirmAllerRetour(op::sinonF32)
+listOf(0.0, 1.0).affirmAllerRetour(op::sinonF64)
+
+// enums
+Enumeration.values().toList().affirmAllerRetour(op::sinonEnum)
+
+op.destroy()
+
+// Testing defaulting properties in record types.
+val defaultes = OptionneurDictionnaire()
+val explicites = OptionneurDictionnaire(
+ i8Var = -8,
+ u8Var = 8u,
+ i16Var = -16,
+ u16Var = 0x10u,
+ i32Var = -32,
+ u32Var = 32u,
+ i64Var = -64L,
+ u64Var = 64uL,
+ floatVar = 4.0f,
+ doubleVar = 8.0,
+ booleanVar = true,
+ stringVar = "default",
+ listVar = listOf(),
+ enumerationVar = Enumeration.DEUX,
+ dictionnaireVar = null
+)
+assert(defaultes == explicites)
+
+// …and makes sure they travel across and back the FFI.
+val rt2 = Retourneur()
+listOf(defaultes).affirmAllerRetour(rt2::identiqueOptionneurDictionnaire)
+
+rt2.destroy()
diff --git a/third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.py b/third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.py
new file mode 100644
index 0000000000..ecfcc1e527
--- /dev/null
+++ b/third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.py
@@ -0,0 +1,146 @@
+import sys
+import ctypes
+from rondpoint import *
+
+dico = Dictionnaire(Enumeration.DEUX, True, 0, 123456789)
+copyDico = copie_dictionnaire(dico)
+assert dico == copyDico
+
+assert copie_enumeration(Enumeration.DEUX) == Enumeration.DEUX
+assert copie_enumerations([Enumeration.UN, Enumeration.DEUX]) == [Enumeration.UN, Enumeration.DEUX]
+assert copie_carte({
+ "0": EnumerationAvecDonnees.ZERO(),
+ "1": EnumerationAvecDonnees.UN(1),
+ "2": EnumerationAvecDonnees.DEUX(2, "deux"),
+}) == {
+ "0": EnumerationAvecDonnees.ZERO(),
+ "1": EnumerationAvecDonnees.UN(1),
+ "2": EnumerationAvecDonnees.DEUX(2, "deux"),
+}
+
+assert switcheroo(False) is True
+
+assert EnumerationAvecDonnees.ZERO() != EnumerationAvecDonnees.UN(1)
+assert EnumerationAvecDonnees.UN(1) == EnumerationAvecDonnees.UN(1)
+assert EnumerationAvecDonnees.UN(1) != EnumerationAvecDonnees.UN(2)
+
+# Test the roundtrip across the FFI.
+# This shows that the values we send come back in exactly the same state as we sent them.
+# i.e. it shows that lowering from python and lifting into rust is symmetrical with
+# lowering from rust and lifting into python.
+rt = Retourneur()
+
+def affirmAllerRetour(vals, identique):
+ for v in vals:
+ id_v = identique(v)
+ assert id_v == v, f"Round-trip failure: {v} => {id_v}"
+
+MIN_I8 = -1 * 2**7
+MAX_I8 = 2**7 - 1
+MIN_I16 = -1 * 2**15
+MAX_I16 = 2**15 - 1
+MIN_I32 = -1 * 2**31
+MAX_I32 = 2**31 - 1
+MIN_I64 = -1 * 2**31
+MAX_I64 = 2**31 - 1
+
+# Python floats are always doubles, so won't round-trip through f32 correctly.
+# This truncates them appropriately.
+F32_ONE_THIRD = ctypes.c_float(1.0 / 3).value
+
+# Booleans
+affirmAllerRetour([True, False], rt.identique_boolean)
+
+# Bytes.
+affirmAllerRetour([MIN_I8, -1, 0, 1, MAX_I8], rt.identique_i8)
+affirmAllerRetour([0x00, 0x12, 0xFF], rt.identique_u8)
+
+# Shorts
+affirmAllerRetour([MIN_I16, -1, 0, 1, MAX_I16], rt.identique_i16)
+affirmAllerRetour([0x0000, 0x1234, 0xFFFF], rt.identique_u16)
+
+# Ints
+affirmAllerRetour([MIN_I32, -1, 0, 1, MAX_I32], rt.identique_i32)
+affirmAllerRetour([0x00000000, 0x12345678, 0xFFFFFFFF], rt.identique_u32)
+
+# Longs
+affirmAllerRetour([MIN_I64, -1, 0, 1, MAX_I64], rt.identique_i64)
+affirmAllerRetour([0x0000000000000000, 0x1234567890ABCDEF, 0xFFFFFFFFFFFFFFFF], rt.identique_u64)
+
+# Floats
+affirmAllerRetour([0.0, 0.5, 0.25, 1.0, F32_ONE_THIRD], rt.identique_float)
+
+# Doubles
+affirmAllerRetour(
+ [0.0, 0.5, 0.25, 1.0, 1.0 / 3, sys.float_info.max, sys.float_info.min],
+ rt.identique_double
+)
+
+# Strings
+affirmAllerRetour(
+ ["", "abc", "été", "ښي لاس ته لوستلو لوستل", "😻emoji 👨‍👧‍👦multi-emoji, 🇨🇭a flag, a canal, panama"],
+ rt.identique_string
+)
+
+# Test one way across the FFI.
+#
+# We send one representation of a value to lib.rs, and it transforms it into another, a string.
+# lib.rs sends the string back, and then we compare here in python.
+#
+# This shows that the values are transformed into strings the same way in both python and rust.
+# i.e. if we assume that the string return works (we test this assumption elsewhere)
+# we show that lowering from python and lifting into rust has values that both python and rust
+# both stringify in the same way. i.e. the same values.
+#
+# If we roundtripping proves the symmetry of our lowering/lifting from here to rust, and lowering/lifting from rust to here,
+# and this convinces us that lowering/lifting from here to rust is correct, then
+# together, we've shown the correctness of the return leg.
+st = Stringifier()
+
+def affirmEnchaine(vals, toString, rustyStringify=lambda v: str(v).lower()):
+ for v in vals:
+ str_v = toString(v)
+ assert rustyStringify(v) == str_v, f"String compare error {v} => {str_v}"
+
+# Test the efficacy of the string transport from rust. If this fails, but everything else
+# works, then things are very weird.
+wellKnown = st.well_known_string("python")
+assert "uniffi 💚 python!" == wellKnown
+
+# Booleans
+affirmEnchaine([True, False], st.to_string_boolean)
+
+# Bytes.
+affirmEnchaine([MIN_I8, -1, 0, 1, MAX_I8], st.to_string_i8)
+affirmEnchaine([0x00, 0x12, 0xFF], st.to_string_u8)
+
+# Shorts
+affirmEnchaine([MIN_I16, -1, 0, 1, MAX_I16], st.to_string_i16)
+affirmEnchaine([0x0000, 0x1234, 0xFFFF], st.to_string_u16)
+
+# Ints
+affirmEnchaine([MIN_I32, -1, 0, 1, MAX_I32], st.to_string_i32)
+affirmEnchaine([0x00000000, 0x12345678, 0xFFFFFFFF], st.to_string_u32)
+
+# Longs
+affirmEnchaine([MIN_I64, -1, 0, 1, MAX_I64], st.to_string_i64)
+affirmEnchaine([0x0000000000000000, 0x1234567890ABCDEF, 0xFFFFFFFFFFFFFFFF], st.to_string_u64)
+
+# Floats
+def rustyFloatToStr(v):
+ """Stringify a float in the same way that rust seems to."""
+ # Rust doesn't include the decimal part of whole enumber floats when stringifying.
+ if int(v) == v:
+ return str(int(v))
+ return str(v)
+
+affirmEnchaine([0.0, 0.5, 0.25, 1.0], st.to_string_float, rustyFloatToStr)
+assert st.to_string_float(F32_ONE_THIRD) == "0.33333334" # annoyingly different string repr
+
+# Doubles
+# TODO: float_info.max/float_info.min don't stringify-roundtrip properly yet, TBD.
+affirmEnchaine(
+ [0.0, 0.5, 0.25, 1.0, 1.0 / 3],
+ st.to_string_double,
+ rustyFloatToStr,
+)
diff --git a/third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.rb b/third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.rb
new file mode 100644
index 0000000000..0121f6e0f9
--- /dev/null
+++ b/third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.rb
@@ -0,0 +1,142 @@
+# frozen_string_literal: true
+
+require 'test/unit'
+require 'rondpoint'
+
+include Test::Unit::Assertions
+include Rondpoint
+
+dico = Dictionnaire.new Enumeration::DEUX, true, 0, 123_456_789
+
+assert_equal dico, Rondpoint.copie_dictionnaire(dico)
+
+assert_equal Rondpoint.copie_enumeration(Enumeration::DEUX), Enumeration::DEUX
+
+assert_equal Rondpoint.copie_enumerations([
+ Enumeration::UN,
+ Enumeration::DEUX
+ ]), [Enumeration::UN, Enumeration::DEUX]
+
+assert_equal Rondpoint.copie_carte({
+ '0' => EnumerationAvecDonnees::ZERO.new,
+ '1' => EnumerationAvecDonnees::UN.new(1),
+ '2' => EnumerationAvecDonnees::DEUX.new(2, 'deux')
+ }), {
+ '0' => EnumerationAvecDonnees::ZERO.new,
+ '1' => EnumerationAvecDonnees::UN.new(1),
+ '2' => EnumerationAvecDonnees::DEUX.new(2, 'deux')
+ }
+
+assert Rondpoint.switcheroo(false)
+
+assert_not_equal EnumerationAvecDonnees::ZERO.new, EnumerationAvecDonnees::UN.new(1)
+assert_equal EnumerationAvecDonnees::UN.new(1), EnumerationAvecDonnees::UN.new(1)
+assert_not_equal EnumerationAvecDonnees::UN.new(1), EnumerationAvecDonnees::UN.new(2)
+
+# Test the roundtrip across the FFI.
+# This shows that the values we send come back in exactly the same state as we sent them.
+# i.e. it shows that lowering from ruby and lifting into rust is symmetrical with
+# lowering from rust and lifting into ruby.
+RT = Retourneur.new
+
+def affirm_aller_retour(vals, fn_name)
+ vals.each do |v|
+ id_v = RT.public_send fn_name, v
+
+ assert_equal id_v, v, "Round-trip failure: #{v} => #{id_v}"
+ end
+end
+
+MIN_I8 = -1 * 2**7
+MAX_I8 = 2**7 - 1
+MIN_I16 = -1 * 2**15
+MAX_I16 = 2**15 - 1
+MIN_I32 = -1 * 2**31
+MAX_I32 = 2**31 - 1
+MIN_I64 = -1 * 2**31
+MAX_I64 = 2**31 - 1
+
+# Ruby floats are always doubles, so won't round-trip through f32 correctly.
+# This truncates them appropriately.
+F32_ONE_THIRD = [1.0 / 3].pack('f').unpack('f')[0]
+
+# Booleans
+affirm_aller_retour([true, false], :identique_boolean)
+
+# Bytes.
+affirm_aller_retour([MIN_I8, -1, 0, 1, MAX_I8], :identique_i8)
+affirm_aller_retour([0x00, 0x12, 0xFF], :identique_u8)
+
+# Shorts
+affirm_aller_retour([MIN_I16, -1, 0, 1, MAX_I16], :identique_i16)
+affirm_aller_retour([0x0000, 0x1234, 0xFFFF], :identique_u16)
+
+# Ints
+affirm_aller_retour([MIN_I32, -1, 0, 1, MAX_I32], :identique_i32)
+affirm_aller_retour([0x00000000, 0x12345678, 0xFFFFFFFF], :identique_u32)
+
+# Longs
+affirm_aller_retour([MIN_I64, -1, 0, 1, MAX_I64], :identique_i64)
+affirm_aller_retour([0x0000000000000000, 0x1234567890ABCDEF, 0xFFFFFFFFFFFFFFFF], :identique_u64)
+
+# Floats
+affirm_aller_retour([0.0, 0.5, 0.25, 1.0, F32_ONE_THIRD], :identique_float)
+
+# Doubles
+affirm_aller_retour(
+ [0.0, 0.5, 0.25, 1.0, 1.0 / 3, Float::MAX, Float::MIN],
+ :identique_double
+)
+
+# Strings
+affirm_aller_retour(
+ ['', 'abc', 'été', 'ښي لاس ته لوستلو لوستل',
+ '😻emoji 👨‍👧‍👦multi-emoji, 🇨🇭a flag, a canal, panama'],
+ :identique_string
+)
+
+# Test one way across the FFI.
+#
+# We send one representation of a value to lib.rs, and it transforms it into another, a string.
+# lib.rs sends the string back, and then we compare here in ruby.
+#
+# This shows that the values are transformed into strings the same way in both ruby and rust.
+# i.e. if we assume that the string return works (we test this assumption elsewhere)
+# we show that lowering from ruby and lifting into rust has values that both ruby and rust
+# both stringify in the same way. i.e. the same values.
+#
+# If we roundtripping proves the symmetry of our lowering/lifting from here to rust, and lowering/lifting from rust to here,
+# and this convinces us that lowering/lifting from here to rust is correct, then
+# together, we've shown the correctness of the return leg.
+ST = Stringifier.new
+
+def affirm_enchaine(vals, fn_name)
+ vals.each do |v|
+ str_v = ST.public_send fn_name, v
+
+ assert_equal v.to_s, str_v, "String compare error #{v} => #{str_v}"
+ end
+end
+
+# Test the efficacy of the string transport from rust. If this fails, but everything else
+# works, then things are very weird.
+assert_equal ST.well_known_string('ruby'), 'uniffi 💚 ruby!'
+
+# Booleans
+affirm_enchaine([true, false], :to_string_boolean)
+
+# Bytes.
+affirm_enchaine([MIN_I8, -1, 0, 1, MAX_I8], :to_string_i8)
+affirm_enchaine([0x00, 0x12, 0xFF], :to_string_u8)
+
+# Shorts
+affirm_enchaine([MIN_I16, -1, 0, 1, MAX_I16], :to_string_i16)
+affirm_enchaine([0x0000, 0x1234, 0xFFFF], :to_string_u16)
+
+# Ints
+affirm_enchaine([MIN_I32, -1, 0, 1, MAX_I32], :to_string_i32)
+affirm_enchaine([0x00000000, 0x12345678, 0xFFFFFFFF], :to_string_u32)
+
+# Longs
+affirm_enchaine([MIN_I64, -1, 0, 1, MAX_I64], :to_string_i64)
+affirm_enchaine([0x0000000000000000, 0x1234567890ABCDEF, 0xFFFFFFFFFFFFFFFF], :to_string_u64)
diff --git a/third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.swift b/third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.swift
new file mode 100644
index 0000000000..d9f47058ed
--- /dev/null
+++ b/third_party/rust/uniffi-example-rondpoint/tests/bindings/test_rondpoint.swift
@@ -0,0 +1,232 @@
+import rondpoint
+
+let dico = Dictionnaire(un: .deux, deux: false, petitNombre: 0, grosNombre: 123456789)
+let copyDico = copieDictionnaire(d: dico)
+assert(dico == copyDico)
+
+assert(copieEnumeration(e: .deux) == .deux)
+assert(copieEnumerations(e: [.un, .deux]) == [.un, .deux])
+assert(copieCarte(c:
+ ["0": .zero,
+ "1": .un(premier: 1),
+ "2": .deux(premier: 2, second: "deux")
+]) == [
+ "0": .zero,
+ "1": .un(premier: 1),
+ "2": .deux(premier: 2, second: "deux")
+])
+
+assert(EnumerationAvecDonnees.zero != EnumerationAvecDonnees.un(premier: 1))
+assert(EnumerationAvecDonnees.un(premier: 1) == EnumerationAvecDonnees.un(premier: 1))
+assert(EnumerationAvecDonnees.un(premier: 1) != EnumerationAvecDonnees.un(premier: 2))
+
+
+assert(switcheroo(b: false))
+
+// Test the roundtrip across the FFI.
+// This shows that the values we send come back in exactly the same state as we sent them.
+// i.e. it shows that lowering from swift and lifting into rust is symmetrical with
+// lowering from rust and lifting into swift.
+let rt = Retourneur()
+
+// Booleans
+[true, false].affirmAllerRetour(rt.identiqueBoolean)
+
+// Bytes.
+[.min, .max].affirmAllerRetour(rt.identiqueI8)
+[0x00, 0xFF].map { $0 as UInt8 }.affirmAllerRetour(rt.identiqueU8)
+
+// Shorts
+[.min, .max].affirmAllerRetour(rt.identiqueI16)
+[0x0000, 0xFFFF].map { $0 as UInt16 }.affirmAllerRetour(rt.identiqueU16)
+
+// Ints
+[0, 1, -1, .min, .max].affirmAllerRetour(rt.identiqueI32)
+[0x00000000, 0xFFFFFFFF].map { $0 as UInt32 }.affirmAllerRetour(rt.identiqueU32)
+
+// Longs
+[.zero, 1, -1, .min, .max].affirmAllerRetour(rt.identiqueI64)
+[.zero, 1, .min, .max].affirmAllerRetour(rt.identiqueU64)
+
+// Floats
+[.zero, 1, 0.25, .leastNonzeroMagnitude, .greatestFiniteMagnitude].affirmAllerRetour(rt.identiqueFloat)
+
+// Doubles
+[0.0, 1.0, .leastNonzeroMagnitude, .greatestFiniteMagnitude].affirmAllerRetour(rt.identiqueDouble)
+
+// Strings
+["", "abc", "null\0byte", "été", "ښي لاس ته لوستلو لوستل", "😻emoji 👨‍👧‍👦multi-emoji, 🇨🇭a flag, a canal, panama"]
+ .affirmAllerRetour(rt.identiqueString)
+
+// Test one way across the FFI.
+//
+// We send one representation of a value to lib.rs, and it transforms it into another, a string.
+// lib.rs sends the string back, and then we compare here in swift.
+//
+// This shows that the values are transformed into strings the same way in both swift and rust.
+// i.e. if we assume that the string return works (we test this assumption elsewhere)
+// we show that lowering from swift and lifting into rust has values that both swift and rust
+// both stringify in the same way. i.e. the same values.
+//
+// If we roundtripping proves the symmetry of our lowering/lifting from here to rust, and lowering/lifting from rust t here,
+// and this convinces us that lowering/lifting from here to rust is correct, then
+// together, we've shown the correctness of the return leg.
+let st = Stringifier()
+
+// Test the effigacy of the string transport from rust. If this fails, but everything else
+// works, then things are very weird.
+let wellKnown = st.wellKnownString(value: "swift")
+assert("uniffi 💚 swift!" == wellKnown, "wellKnownString 'uniffi 💚 swift!' == '\(wellKnown)'")
+
+// Booleans
+[true, false].affirmEnchaine(st.toStringBoolean)
+
+// Bytes.
+[.min, .max].affirmEnchaine(st.toStringI8)
+[.min, .max].affirmEnchaine(st.toStringU8)
+
+// Shorts
+[.min, .max].affirmEnchaine(st.toStringI16)
+[.min, .max].affirmEnchaine(st.toStringU16)
+
+// Ints
+[0, 1, -1, .min, .max].affirmEnchaine(st.toStringI32)
+[0, 1, .min, .max].affirmEnchaine(st.toStringU32)
+
+// Longs
+[.zero, 1, -1, .min, .max].affirmEnchaine(st.toStringI64)
+[.zero, 1, .min, .max].affirmEnchaine(st.toStringU64)
+
+// Floats
+[.zero, 1, -1, .leastNonzeroMagnitude, .greatestFiniteMagnitude].affirmEnchaine(st.toStringFloat) { Float.init($0) == $1 }
+
+// Doubles
+[.zero, 1, -1, .leastNonzeroMagnitude, .greatestFiniteMagnitude].affirmEnchaine(st.toStringDouble) { Double.init($0) == $1 }
+
+// Some extension functions for testing the results of roundtripping and stringifying
+extension Array where Element: Equatable {
+ static func defaultEquals(_ observed: String, expected: Element) -> Bool {
+ let exp = "\(expected)"
+ return observed == exp
+ }
+
+ func affirmEnchaine(_ fn: (Element) -> String, equals: (String, Element) -> Bool = defaultEquals) {
+ self.forEach { v in
+ let obs = fn(v)
+ assert(equals(obs, v), "toString_\(type(of:v))(\(v)): observed=\(obs), expected=\(v)")
+ }
+ }
+
+ func affirmAllerRetour(_ fn: (Element) -> Element) {
+ self.forEach { v in
+ assert(fn(v) == v, "identique_\(type(of:v))(\(v))")
+ }
+ }
+}
+
+// Prove to ourselves that default arguments are being used.
+// Step 1: call the methods without arguments, and check against the UDL.
+let op = Optionneur()
+
+assert(op.sinonString() == "default")
+
+assert(op.sinonBoolean() == false)
+
+assert(op.sinonSequence() == [])
+
+// optionals
+assert(op.sinonNull() == nil)
+assert(op.sinonZero() == 0)
+
+// decimal integers
+assert(op.sinonU8Dec() == UInt8(42))
+assert(op.sinonI8Dec() == Int8(-42))
+assert(op.sinonU16Dec() == UInt16(42))
+assert(op.sinonI16Dec() == Int16(42))
+assert(op.sinonU32Dec() == UInt32(42))
+assert(op.sinonI32Dec() == Int32(42))
+assert(op.sinonU64Dec() == UInt64(42))
+assert(op.sinonI64Dec() == Int64(42))
+
+// hexadecimal integers
+assert(op.sinonU8Hex() == UInt8(0xff))
+assert(op.sinonI8Hex() == Int8(-0x7f))
+assert(op.sinonU16Hex() == UInt16(0xffff))
+assert(op.sinonI16Hex() == Int16(0x7f))
+assert(op.sinonU32Hex() == UInt32(0xffffffff))
+assert(op.sinonI32Hex() == Int32(0x7fffffff))
+assert(op.sinonU64Hex() == UInt64(0xffffffffffffffff))
+assert(op.sinonI64Hex() == Int64(0x7fffffffffffffff))
+
+// octal integers
+assert(op.sinonU32Oct() == UInt32(0o755))
+
+// floats
+assert(op.sinonF32() == 42.0)
+assert(op.sinonF64() == Double(42.1))
+
+// enums
+assert(op.sinonEnum() == .trois)
+
+// Step 2. Convince ourselves that if we pass something else, then that changes the output.
+// We have shown something coming out of the sinon methods, but without eyeballing the Rust
+// we can't be sure that the arguments will change the return value.
+["foo", "bar"].affirmAllerRetour(op.sinonString)
+[true, false].affirmAllerRetour(op.sinonBoolean)
+[["a", "b"], []].affirmAllerRetour(op.sinonSequence)
+
+// optionals
+["0", "1"].affirmAllerRetour(op.sinonNull)
+[0, 1].affirmAllerRetour(op.sinonZero)
+
+// integers
+[0, 1].affirmAllerRetour(op.sinonU8Dec)
+[0, 1].affirmAllerRetour(op.sinonI8Dec)
+[0, 1].affirmAllerRetour(op.sinonU16Dec)
+[0, 1].affirmAllerRetour(op.sinonI16Dec)
+[0, 1].affirmAllerRetour(op.sinonU32Dec)
+[0, 1].affirmAllerRetour(op.sinonI32Dec)
+[0, 1].affirmAllerRetour(op.sinonU64Dec)
+[0, 1].affirmAllerRetour(op.sinonI64Dec)
+
+[0, 1].affirmAllerRetour(op.sinonU8Hex)
+[0, 1].affirmAllerRetour(op.sinonI8Hex)
+[0, 1].affirmAllerRetour(op.sinonU16Hex)
+[0, 1].affirmAllerRetour(op.sinonI16Hex)
+[0, 1].affirmAllerRetour(op.sinonU32Hex)
+[0, 1].affirmAllerRetour(op.sinonI32Hex)
+[0, 1].affirmAllerRetour(op.sinonU64Hex)
+[0, 1].affirmAllerRetour(op.sinonI64Hex)
+
+[0, 1].affirmAllerRetour(op.sinonU32Oct)
+
+// floats
+[0.0, 1.0].affirmAllerRetour(op.sinonF32)
+[0.0, 1.0].affirmAllerRetour(op.sinonF64)
+
+// enums
+[.un, .deux, .trois].affirmAllerRetour(op.sinonEnum)
+
+// Testing defaulting properties in record types.
+let defaultes = OptionneurDictionnaire()
+let explicites = OptionneurDictionnaire(
+ i8Var: Int8(-8),
+ u8Var: UInt8(8),
+ i16Var: Int16(-16),
+ u16Var: UInt16(0x10),
+ i32Var: -32,
+ u32Var: UInt32(32),
+ i64Var: Int64(-64),
+ u64Var: UInt64(64),
+ floatVar: Float(4.0),
+ doubleVar: Double(8.0),
+ booleanVar: true,
+ stringVar: "default",
+ listVar: [],
+ enumerationVar: .deux,
+ dictionnaireVar: nil
+)
+
+// …and makes sure they travel across and back the FFI.
+assert(defaultes == explicites)
+[defaultes].affirmAllerRetour(rt.identiqueOptionneurDictionnaire)