Skip to content

Commit 70bd727

Browse files
committed
test(csharp): cover map runtime exports
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
1 parent f1e5557 commit 70bd727

4 files changed

Lines changed: 179 additions & 3 deletions

File tree

crates/csharp/src/csproj.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub struct CSProjectLLVMBuilder {
1212
clean_targets: bool,
1313
world_name: String,
1414
binary: bool,
15+
skip_wit_component: bool,
1516
}
1617

1718
pub struct CSProjectMonoBuilder {
@@ -31,6 +32,7 @@ impl CSProject {
3132
clean_targets: false,
3233
world_name: world_name.to_string(),
3334
binary: false,
35+
skip_wit_component: false,
3436
}
3537
}
3638

@@ -69,6 +71,14 @@ impl CSProjectLLVMBuilder {
6971
"<OutputType>Library</OutputType>"
7072
};
7173

74+
let component_type_linker_arg = if self.skip_wit_component {
75+
"<CustomLinkerArg Include=\"-Wl,--skip-wit-component\" />".to_string()
76+
} else {
77+
format!(
78+
"<CustomLinkerArg Include=\"-Wl,--component-type,{camel}_component_type.wit\" />"
79+
)
80+
};
81+
7282
let mut csproj = format!(
7383
"<Project Sdk=\"Microsoft.NET.Sdk\">
7484
@@ -94,7 +104,7 @@ impl CSProjectLLVMBuilder {
94104
</ItemGroup>
95105
96106
<ItemGroup>
97-
<CustomLinkerArg Include=\"-Wl,--component-type,{camel}_component_type.wit\" />
107+
{component_type_linker_arg}
98108
</ItemGroup>
99109
"
100110
);
@@ -178,6 +188,10 @@ impl CSProjectLLVMBuilder {
178188
self.binary = true;
179189
}
180190

191+
pub fn skip_wit_component(&mut self) {
192+
self.skip_wit_component = true;
193+
}
194+
181195
pub fn clean(&mut self) -> &mut Self {
182196
self.clean_targets = true;
183197

crates/test/src/csharp.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
use crate::{Compile, LanguageMethods, Runner, Verify};
22
use anyhow::Result;
33
use heck::*;
4+
use serde::Deserialize;
45
use std::env;
56
use std::fs;
67
use std::path::Path;
78
use std::process::Command;
89

910
pub struct Csharp;
1011

12+
#[derive(Default, Deserialize)]
13+
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
14+
struct LangConfig {
15+
skip_wit_component: bool,
16+
}
17+
1118
fn dotnet() -> Command {
1219
let dotnet_cmd = match env::var("DOTNET_ROOT") {
1320
Ok(val) => Path::new(&val).join("dotnet"),
@@ -59,6 +66,7 @@ impl LanguageMethods for Csharp {
5966
}
6067

6168
fn compile(&self, runner: &Runner, compile: &Compile<'_>) -> Result<()> {
69+
let config = compile.component.deserialize_lang_config::<LangConfig>()?;
6270
let world_name = &compile.component.bindgen.world;
6371
let path = &compile.component.path;
6472
let test_dir = &compile.bindings_dir;
@@ -75,6 +83,9 @@ impl LanguageMethods for Csharp {
7583
let mut csproj =
7684
wit_bindgen_csharp::CSProject::new(test_dir.to_path_buf(), &assembly_name, world_name);
7785
csproj.aot();
86+
if config.skip_wit_component {
87+
csproj.skip_wit_component();
88+
}
7889
csproj.generate()?;
7990

8091
let mut cmd = dotnet();
@@ -109,7 +120,11 @@ impl LanguageMethods for Csharp {
109120

110121
runner.run_command(&mut cmd)?;
111122

112-
fs::copy(&wasm_filename, &compile.output)?;
123+
if config.skip_wit_component {
124+
runner.convert_p1_to_component_appending_type_metadata(&wasm_filename, compile)?;
125+
} else {
126+
fs::copy(&wasm_filename, &compile.output)?;
127+
}
113128

114129
Ok(())
115130
}

crates/test/src/lib.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1081,14 +1081,38 @@ status: {}",
10811081
///
10821082
/// Stores the output at `compile.output`.
10831083
fn convert_p1_to_component(&self, p1: &Path, compile: &Compile<'_>) -> Result<()> {
1084+
self.convert_p1_to_component_impl(p1, compile, false)
1085+
}
1086+
1087+
/// Converts the WASIp1 module at `p1` to a component, appending component
1088+
/// type metadata from the test WIT even if existing metadata is present.
1089+
///
1090+
/// Some toolchains emit incomplete or stale component type metadata even
1091+
/// when asked to skip componentization, while still needing their own
1092+
/// metadata for WASI imports. Use this when the test harness must add
1093+
/// guest-world metadata without discarding toolchain metadata.
1094+
fn convert_p1_to_component_appending_type_metadata(
1095+
&self,
1096+
p1: &Path,
1097+
compile: &Compile<'_>,
1098+
) -> Result<()> {
1099+
self.convert_p1_to_component_impl(p1, compile, true)
1100+
}
1101+
1102+
fn convert_p1_to_component_impl(
1103+
&self,
1104+
p1: &Path,
1105+
compile: &Compile<'_>,
1106+
append_component_type: bool,
1107+
) -> Result<()> {
10841108
let mut resolve = wit_parser::Resolve::default();
10851109
let (pkg, _) = resolve
10861110
.push_path(&compile.component.bindgen.wit_path)
10871111
.context("failed to load WIT")?;
10881112
let world = resolve.select_world(&[pkg], Some(&compile.component.bindgen.world))?;
10891113
let mut module = fs::read(&p1).context("failed to read wasm file")?;
10901114

1091-
if !has_component_type_sections(&module) {
1115+
if append_component_type || !has_component_type_sections(&module) {
10921116
let encoded =
10931117
wit_component::metadata::encode(&resolve, world, StringEncoding::UTF8, None)?;
10941118
let section = wasm_encoder::CustomSection {

tests/runtime/map/test.cs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
//@ wasmtime-flags = '-Wcomponent-model-map'
2+
//@ [lang]
3+
//@ skip-wit-component = true
4+
5+
using System.Diagnostics;
6+
using System.Text;
7+
8+
namespace TestWorld.wit.Exports.test.maps
9+
{
10+
public class ToTestExportsImpl : IToTestExports
11+
{
12+
public static Dictionary<string, uint> NamedRoundtrip(Dictionary<uint, string> a)
13+
{
14+
Debug.Assert(a.Count == 2);
15+
Debug.Assert(a[1] == "uno");
16+
Debug.Assert(a[2] == "two");
17+
18+
return a.ToDictionary(entry => entry.Value, entry => entry.Key);
19+
}
20+
21+
public static Dictionary<string, byte[]> BytesRoundtrip(Dictionary<string, byte[]> a)
22+
{
23+
Debug.Assert(a.Count == 2);
24+
Debug.Assert(a["hello"].SequenceEqual(Encoding.UTF8.GetBytes("world")));
25+
Debug.Assert(a["bin"].SequenceEqual(new byte[] { 0, 1, 2 }));
26+
27+
return a;
28+
}
29+
30+
public static Dictionary<uint, string> EmptyRoundtrip(Dictionary<uint, string> a)
31+
{
32+
Debug.Assert(a.Count == 0);
33+
return a;
34+
}
35+
36+
public static Dictionary<string, uint?> OptionRoundtrip(Dictionary<string, uint?> a)
37+
{
38+
Debug.Assert(a.Count == 2);
39+
Debug.Assert(a["some"] == 42);
40+
Debug.Assert(a["none"] == null);
41+
42+
return a;
43+
}
44+
45+
public static IToTestExports.LabeledEntry RecordRoundtrip(IToTestExports.LabeledEntry a)
46+
{
47+
Debug.Assert(a.label == "test-label");
48+
Debug.Assert(a.values.Count == 2);
49+
Debug.Assert(a.values[10] == "ten");
50+
Debug.Assert(a.values[20] == "twenty");
51+
52+
return a;
53+
}
54+
55+
public static Dictionary<string, uint> InlineRoundtrip(Dictionary<uint, string> a)
56+
{
57+
return a.ToDictionary(entry => entry.Value, entry => entry.Key);
58+
}
59+
60+
public static Dictionary<uint, string> LargeRoundtrip(Dictionary<uint, string> a)
61+
{
62+
Debug.Assert(a.Count == 100);
63+
return a;
64+
}
65+
66+
public static (Dictionary<string, uint>, Dictionary<string, byte[]>) MultiParamRoundtrip(
67+
Dictionary<uint, string> a,
68+
Dictionary<string, byte[]> b)
69+
{
70+
Debug.Assert(a.Count == 2);
71+
Debug.Assert(b.Count == 1);
72+
73+
return (a.ToDictionary(entry => entry.Value, entry => entry.Key), b);
74+
}
75+
76+
public static Dictionary<string, Dictionary<uint, string>> NestedRoundtrip(
77+
Dictionary<string, Dictionary<uint, string>> a)
78+
{
79+
Debug.Assert(a.Count == 2);
80+
Debug.Assert(a["group-a"].Count == 2);
81+
Debug.Assert(a["group-a"][1] == "one");
82+
Debug.Assert(a["group-a"][2] == "two");
83+
Debug.Assert(a["group-b"].Count == 1);
84+
Debug.Assert(a["group-b"][10] == "ten");
85+
86+
return a;
87+
}
88+
89+
public static IToTestExports.MapOrString VariantRoundtrip(IToTestExports.MapOrString a)
90+
{
91+
return a;
92+
}
93+
94+
public static Dictionary<uint, string> ResultRoundtrip(
95+
Result<Dictionary<uint, string>, string> a)
96+
{
97+
if (a.IsErr)
98+
{
99+
throw new WitException<string>(a.AsErr, 0);
100+
}
101+
102+
return a.AsOk;
103+
}
104+
105+
public static (Dictionary<uint, string>, ulong) TupleRoundtrip(
106+
(Dictionary<uint, string>, ulong) a)
107+
{
108+
Debug.Assert(a.Item1.Count == 1);
109+
Debug.Assert(a.Item1[7] == "seven");
110+
Debug.Assert(a.Item2 == 42);
111+
112+
return a;
113+
}
114+
115+
public static Dictionary<uint, string> SingleEntryRoundtrip(Dictionary<uint, string> a)
116+
{
117+
Debug.Assert(a.Count == 1);
118+
Debug.Assert(a[99] == "ninety-nine");
119+
120+
return a;
121+
}
122+
}
123+
}

0 commit comments

Comments
 (0)