Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions System.ServiceModel.sln
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.ServiceModel.NetFram
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.ServiceModel.NetNamedPipe", "src\System.ServiceModel.NetNamedPipe\src\System.ServiceModel.NetNamedPipe.csproj", "{5ECB8887-D7EE-449F-9439-35D0BBBB1E07}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.ServiceModel.Msmq", "src\System.ServiceModel.Msmq\src\System.ServiceModel.Msmq.csproj", "{697D1BB3-BBF5-40CE-BA60-EA5945D783BB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.ServiceModel.Msmq.Tests", "src\System.ServiceModel.Msmq\tests\System.ServiceModel.Msmq.Tests.csproj", "{44F85C39-7BD4-489F-A4FC-D387DF925F40}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Binding.Msmq.IntegrationTests", "src\System.Private.ServiceModel\tests\Scenarios\Binding\Msmq\Binding.Msmq.IntegrationTests.csproj", "{B7AC3382-C6DE-4BD2-910E-70BA53E7DA38}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.ServiceModel.UnixDomainSocket", "src\System.ServiceModel.UnixDomainSocket\src\System.ServiceModel.UnixDomainSocket.csproj", "{1664DB18-8451-43C0-8A85-2DD9189C3897}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Binding.UDS.IntegrationTests", "src\System.Private.ServiceModel\tests\Scenarios\Binding\UDS\Binding.UDS.IntegrationTests.csproj", "{B7C7D4F1-DE4D-421B-9CE9-C7320A503D58}"
Expand Down Expand Up @@ -579,6 +585,42 @@ Global
{5ECB8887-D7EE-449F-9439-35D0BBBB1E07}.Release|x64.Build.0 = Release|Any CPU
{5ECB8887-D7EE-449F-9439-35D0BBBB1E07}.Release|x86.ActiveCfg = Release|Any CPU
{5ECB8887-D7EE-449F-9439-35D0BBBB1E07}.Release|x86.Build.0 = Release|Any CPU
{697D1BB3-BBF5-40CE-BA60-EA5945D783BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{697D1BB3-BBF5-40CE-BA60-EA5945D783BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{697D1BB3-BBF5-40CE-BA60-EA5945D783BB}.Debug|x64.ActiveCfg = Debug|Any CPU
{697D1BB3-BBF5-40CE-BA60-EA5945D783BB}.Debug|x64.Build.0 = Debug|Any CPU
{697D1BB3-BBF5-40CE-BA60-EA5945D783BB}.Debug|x86.ActiveCfg = Debug|Any CPU
{697D1BB3-BBF5-40CE-BA60-EA5945D783BB}.Debug|x86.Build.0 = Debug|Any CPU
{697D1BB3-BBF5-40CE-BA60-EA5945D783BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{697D1BB3-BBF5-40CE-BA60-EA5945D783BB}.Release|Any CPU.Build.0 = Release|Any CPU
{697D1BB3-BBF5-40CE-BA60-EA5945D783BB}.Release|x64.ActiveCfg = Release|Any CPU
{697D1BB3-BBF5-40CE-BA60-EA5945D783BB}.Release|x64.Build.0 = Release|Any CPU
{697D1BB3-BBF5-40CE-BA60-EA5945D783BB}.Release|x86.ActiveCfg = Release|Any CPU
{697D1BB3-BBF5-40CE-BA60-EA5945D783BB}.Release|x86.Build.0 = Release|Any CPU
{44F85C39-7BD4-489F-A4FC-D387DF925F40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{44F85C39-7BD4-489F-A4FC-D387DF925F40}.Debug|Any CPU.Build.0 = Debug|Any CPU
{44F85C39-7BD4-489F-A4FC-D387DF925F40}.Debug|x64.ActiveCfg = Debug|Any CPU
{44F85C39-7BD4-489F-A4FC-D387DF925F40}.Debug|x64.Build.0 = Debug|Any CPU
{44F85C39-7BD4-489F-A4FC-D387DF925F40}.Debug|x86.ActiveCfg = Debug|Any CPU
{44F85C39-7BD4-489F-A4FC-D387DF925F40}.Debug|x86.Build.0 = Debug|Any CPU
{44F85C39-7BD4-489F-A4FC-D387DF925F40}.Release|Any CPU.ActiveCfg = Release|Any CPU
{44F85C39-7BD4-489F-A4FC-D387DF925F40}.Release|Any CPU.Build.0 = Release|Any CPU
{44F85C39-7BD4-489F-A4FC-D387DF925F40}.Release|x64.ActiveCfg = Release|Any CPU
{44F85C39-7BD4-489F-A4FC-D387DF925F40}.Release|x64.Build.0 = Release|Any CPU
{44F85C39-7BD4-489F-A4FC-D387DF925F40}.Release|x86.ActiveCfg = Release|Any CPU
{44F85C39-7BD4-489F-A4FC-D387DF925F40}.Release|x86.Build.0 = Release|Any CPU
{B7AC3382-C6DE-4BD2-910E-70BA53E7DA38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7AC3382-C6DE-4BD2-910E-70BA53E7DA38}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7AC3382-C6DE-4BD2-910E-70BA53E7DA38}.Debug|x64.ActiveCfg = Debug|Any CPU
{B7AC3382-C6DE-4BD2-910E-70BA53E7DA38}.Debug|x64.Build.0 = Debug|Any CPU
{B7AC3382-C6DE-4BD2-910E-70BA53E7DA38}.Debug|x86.ActiveCfg = Debug|Any CPU
{B7AC3382-C6DE-4BD2-910E-70BA53E7DA38}.Debug|x86.Build.0 = Debug|Any CPU
{B7AC3382-C6DE-4BD2-910E-70BA53E7DA38}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B7AC3382-C6DE-4BD2-910E-70BA53E7DA38}.Release|Any CPU.Build.0 = Release|Any CPU
{B7AC3382-C6DE-4BD2-910E-70BA53E7DA38}.Release|x64.ActiveCfg = Release|Any CPU
{B7AC3382-C6DE-4BD2-910E-70BA53E7DA38}.Release|x64.Build.0 = Release|Any CPU
{B7AC3382-C6DE-4BD2-910E-70BA53E7DA38}.Release|x86.ActiveCfg = Release|Any CPU
{B7AC3382-C6DE-4BD2-910E-70BA53E7DA38}.Release|x86.Build.0 = Release|Any CPU
{1664DB18-8451-43C0-8A85-2DD9189C3897}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1664DB18-8451-43C0-8A85-2DD9189C3897}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1664DB18-8451-43C0-8A85-2DD9189C3897}.Debug|x64.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -760,6 +802,9 @@ Global
{88918456-A2B2-431F-BB95-BAAD2818BFC7} = {DFDC71CF-6E65-481D-99D7-C35ED7EF6D4E}
{B7C7D4F1-DE4D-421B-9CE9-C7320A503D58} = {D6302510-AB10-4775-BCE9-98FA96FDEB76}
{FD188537-21AF-48C5-84FF-F8758F4C42A8} = {DFDC71CF-6E65-481D-99D7-C35ED7EF6D4E}
{697D1BB3-BBF5-40CE-BA60-EA5945D783BB} = {D6302510-AB10-4775-BCE9-98FA96FDEB76}
{44F85C39-7BD4-489F-A4FC-D387DF925F40} = {DFDC71CF-6E65-481D-99D7-C35ED7EF6D4E}
{B7AC3382-C6DE-4BD2-910E-70BA53E7DA38} = {D6302510-AB10-4775-BCE9-98FA96FDEB76}
{0CB62138-1E7D-4D9D-B7BF-4FCB79BC8EAA} = {D6302510-AB10-4775-BCE9-98FA96FDEB76}
{7D7DACFC-D1DA-48C0-9BA1-C3A414780658} = {D6302510-AB10-4775-BCE9-98FA96FDEB76}
EndGlobalSection
Expand Down
10 changes: 10 additions & 0 deletions eng/SendToHelix.proj
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@
<HelixPreCommands>$(HelixPreCommands);security default-keychain -d user -s $HOME/Library/Keychains/login.keychain-db</HelixPreCommands>
</PropertyGroup>

<PropertyGroup Condition="'$(TestJob)' == 'Windows'">
<!-- Enable the MSMQ Windows feature on the Helix worker so the
Binding.Msmq.IntegrationTests scenarios can talk to a local
queue manager. Best-effort: if enablement fails (e.g. the
worker is non-admin on this queue, or MSMQ is already on),
the scenario tests skip cleanly via [Condition(MsmqInstalled)]
instead of failing the leg. -->
<HelixPreCommands>$(HelixPreCommands);powershell -NoProfile -ExecutionPolicy Bypass -Command "try { Enable-WindowsOptionalFeature -Online -FeatureName MSMQ-Server -All -NoRestart -ErrorAction SilentlyContinue | Out-Null } catch {}; try { Start-Service MSMQ -ErrorAction SilentlyContinue } catch {}"</HelixPreCommands>
</PropertyGroup>

<PropertyGroup Condition="'$(TestJob)' == 'Windows' AND '$(RunWithCoreWCFService)' == 'true'">
<HelixPreCommands>$(HelixPreCommands);set PATH=%HELIX_CORRELATION_PAYLOAD%\dotnet-cli%3B%PATH%</HelixPreCommands>
<!-- %3B is an escaped ; -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,5 +253,42 @@ internal static string GetSPN()
return TestProperties.GetProperty(TestProperties.NegotiateTestSpn_PropertyName);
}

// Detector used by [ConditionalFact(nameof(MsmqInstalled))].
// Returns true if the MSMQ feature is installed on the host (we
// check for mqrt.dll under System32). MSMQ scenario tests need
// a running queue manager; this gate keeps them skipped on hosts
// that don't have the Windows MSMQ feature enabled.
public static bool IsMsmqInstalled()
{
if (!IsWindows())
{
return false;
}
string system32 = Environment.GetFolderPath(Environment.SpecialFolder.System);
return !string.IsNullOrEmpty(system32)
&& System.IO.File.Exists(System.IO.Path.Combine(system32, "mqrt.dll"));
}

// Detector used by [ConditionalFact(nameof(ImplicitDtcEnabled))].
// .NET 8+ disables implicit DTC promotion by default. The
// AppContext switch has to be set via the consuming process's
// `runtimeconfig.json`:
// "System.Transactions.EnableImplicitDistributedTransactions": "true"
// Setting the corresponding TransactionManager property from
// application code is not reliable from inside an xunit test
// host because the DTC proxy reads the value before our setter
// runs. We therefore gate tests on the env var
// WCF_MSMQ_ENABLE_DTC_TESTS=true
// which the dev / CI explicitly sets when the runtime property
// is known to be in place.
public static bool IsImplicitDtcEnabled()
{
if (!IsWindows())
{
return false;
}
string env = Environment.GetEnvironmentVariable("WCF_MSMQ_ENABLE_DTC_TESTS");
return string.Equals(env, "true", StringComparison.OrdinalIgnoreCase);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,25 @@ public static bool IsNotWindows()
return !Is_Windows();
}

// Returns 'true' if the MSMQ Windows feature is installed on the
// client machine. Use as a [Condition] for any MSMQ scenario test
// that needs an actual queue manager.
public static bool MsmqInstalled()
{
return GetConditionValue(nameof(MsmqInstalled),
ConditionalTestDetectors.IsMsmqInstalled);
}

// Returns 'true' if TransactionManager.ImplicitDistributedTransactions
// is currently set. MSMQ scenarios that need a TransactionScope use
// this gate so they skip cleanly on hosts where implicit DTC isn't
// enabled (the .NET 8+ default) instead of failing.
public static bool ImplicitDtcEnabled()
{
return GetConditionValue(nameof(ImplicitDtcEnabled),
ConditionalTestDetectors.IsImplicitDtcEnabled);
}

// Returns 'true' if both the server and the client are domain-joined.
public static bool Domain_Joined()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(ScenarioTestTargetFrameworks)</TargetFrameworks>
<CLSCompliant>false</CLSCompliant>
<IsTestProject>true</IsTestProject>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MSMQ.Messaging" Version="1.0.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(WcfInfrastructureCommonProj)" />
<ProjectReference Include="$(WcfScenarioTestCommonProj)" />
<ProjectReference Include="..\..\..\..\..\System.ServiceModel.Msmq\src\System.ServiceModel.Msmq.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.


using System;
using System.IO;
using System.Runtime.Versioning;
using System.ServiceModel;
using System.ServiceModel.Channels;
using Infrastructure.Common;
using MSMQ.Messaging;
using Xunit;

using WcfMessage = System.ServiceModel.Channels.Message;
using MsmqRawMessage = MSMQ.Messaging.Message;

namespace Binding.Msmq.IntegrationTests
{
// End-to-end scenario tests for NetMsmqBinding's send path. Each
// test provisions a fresh private queue, sends through our client
// channel, then verifies the message arrived by reading the queue
// back with MSMQ.Messaging directly. No service host is required;
// we exercise the wire path against the local queue manager.
[SupportedOSPlatform("windows")]
public class NetMsmqClientScenarioTests : ConditionalWcfTest, IDisposable
{
private readonly string _shortName;
private readonly string _machineQueuePath;
private readonly Uri _queueUri;

public NetMsmqClientScenarioTests()
{
_shortName = "wcf-test-" + Guid.NewGuid().ToString("N");
_machineQueuePath = ".\\private$\\" + _shortName;
_queueUri = new Uri("net.msmq://localhost/private/" + _shortName);
}

public void Dispose()
{
try
{
if (MessageQueue.Exists(_machineQueuePath))
{
MessageQueue.Delete(_machineQueuePath);
}
}
catch { /* swallow on cleanup */ }
}

[WcfFact]
[Condition(nameof(MsmqInstalled))]
public void Send_NonTransactional_MessageArrivesInQueue()
{
MessageQueue.Create(_machineQueuePath, transactional: false);

SendOne("hello");

using var receiver = new MessageQueue("FormatName:DIRECT=OS:" + _machineQueuePath);
using MsmqRawMessage received = receiver.Receive(TimeSpan.FromSeconds(10));
Assert.NotNull(received);
Assert.NotNull(received.BodyStream);
Assert.True(received.BodyStream.Length > 0);
}

[WcfFact]
[Condition(nameof(MsmqInstalled))]
public void Send_ToNonExistentQueue_ThrowsEndpointNotFoundException()
{
var binding = new NetMsmqBinding(NetMsmqSecurityMode.None)
{
Durable = false,
ExactlyOnce = false,
};
IChannelFactory<IOutputChannel> factory = binding.BuildChannelFactory<IOutputChannel>();
try
{
factory.Open();
Uri missing = new Uri("net.msmq://localhost/private/does-not-exist-" + Guid.NewGuid().ToString("N"));
IOutputChannel channel = factory.CreateChannel(new EndpointAddress(missing));
channel.Open();
try
{
WcfMessage msg = WcfMessage.CreateMessage(MessageVersion.Soap12WSAddressing10, "urn:test/echo", "x");
EndpointNotFoundException ex = Assert.Throws<EndpointNotFoundException>(() => channel.Send(msg));
Assert.IsType<MsmqException>(ex.InnerException);
}
finally
{
channel.Abort();
}
}
finally
{
factory.Close();
}
}

[WcfFact]
[Condition(nameof(MsmqInstalled))]
public void Send_NonTransactional_BodyBytesRoundTripBinaryEncoder()
{
MessageQueue.Create(_machineQueuePath, transactional: false);

byte[] sentEncoded = SendOne("payload-42", returnEncodedBody: true);

using var receiver = new MessageQueue("FormatName:DIRECT=OS:" + _machineQueuePath);
using MsmqRawMessage received = receiver.Receive(TimeSpan.FromSeconds(10));
using var ms = new MemoryStream();
received.BodyStream.CopyTo(ms);
Assert.Equal(sentEncoded, ms.ToArray());
}

private byte[] SendOne(string bodyText, bool returnEncodedBody = false)
{
var binding = new NetMsmqBinding(NetMsmqSecurityMode.None)
{
Durable = true,
ExactlyOnce = false,
};
IChannelFactory<IOutputChannel> factory = binding.BuildChannelFactory<IOutputChannel>();
try
{
factory.Open();
IOutputChannel channel = factory.CreateChannel(new EndpointAddress(_queueUri));
channel.Open();
try
{
WcfMessage msg = WcfMessage.CreateMessage(MessageVersion.Soap12WSAddressing10, "urn:test/echo", bodyText);
byte[] encoded = null;
if (returnEncodedBody)
{
using MessageBuffer buffer = msg.CreateBufferedCopy(int.MaxValue);
WcfMessage previewMsg = buffer.CreateMessage();
MessageEncoder encoder = new BinaryMessageEncodingBindingElement().CreateMessageEncoderFactory().Encoder;
var preview = encoder.WriteMessage(previewMsg, int.MaxValue, BufferManager.CreateBufferManager(64 * 1024, 64 * 1024));
encoded = new byte[preview.Count];
Buffer.BlockCopy(preview.Array, preview.Offset, encoded, 0, preview.Count);
msg = buffer.CreateMessage();
}
channel.Send(msg);
return encoded;
}
finally
{
channel.Close();
}
}
finally
{
factory.Close();
}
}
}
}
Loading
Loading