Compare commits
No commits in common. "d2c0c2159f2e143673fd6b4b57def6512c37ded1" and "6cac486fbe24af3d0429a4f6cf3893bed29cf3a8" have entirely different histories.
d2c0c2159f
...
6cac486fbe
3 changed files with 4 additions and 298 deletions
|
|
@ -1,146 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
TeamsISO — MSI installer (WiX v5)
|
||||
|
||||
Produces: TeamsISO-Setup-<Version>.msi (per-machine install).
|
||||
|
||||
Build:
|
||||
dotnet publish src/TeamsISO.App/TeamsISO.App.csproj -c Release -r win-x64 (no self contained) -o publish/TeamsISO
|
||||
dotnet build installer/TeamsISO.Installer.wixproj -c Release
|
||||
|
||||
Runtime expectations:
|
||||
- .NET 8 Desktop runtime present on target (framework-dependent build)
|
||||
- NDI 6 Runtime present — checked in CheckNdiRuntime; absence WARNS
|
||||
but does not block install (operators can install NDI after the app)
|
||||
-->
|
||||
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
|
||||
xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
|
||||
|
||||
<Package Name="TeamsISO"
|
||||
Manufacturer="Wild Dragon LLC"
|
||||
Version="1.0.0.0"
|
||||
UpgradeCode="9F4A8B2C-1D3E-4A5B-9C6D-8E7F0A1B2C3D"
|
||||
Scope="perMachine"
|
||||
Compressed="yes"
|
||||
InstallerVersion="500">
|
||||
|
||||
<SummaryInformation Description="TeamsISO — Per-Participant NDI ISO Controller for Microsoft Teams"
|
||||
Manufacturer="Wild Dragon LLC" />
|
||||
|
||||
<!--
|
||||
MajorUpgrade: a newer install replaces an older one in-place.
|
||||
Disallow downgrades; users should uninstall the newer first.
|
||||
-->
|
||||
<MajorUpgrade DowngradeErrorMessage="A newer version of TeamsISO is already installed. Uninstall it before installing this older version."
|
||||
Schedule="afterInstallInitialize" />
|
||||
|
||||
<!--
|
||||
Single MSI feature; users see only the install/uninstall screens.
|
||||
-->
|
||||
<Feature Id="Main" Title="TeamsISO" Level="1">
|
||||
<ComponentGroupRef Id="ApplicationFiles" />
|
||||
<ComponentGroupRef Id="Shortcuts" />
|
||||
<ComponentGroupRef Id="ArpEntry" />
|
||||
</Feature>
|
||||
|
||||
<!--
|
||||
Friendly install UI. WixToolset.UI.wixext provides several flavors;
|
||||
WixUI_InstallDir lets the user pick the directory.
|
||||
-->
|
||||
<ui:WixUI Id="WixUI_InstallDir" />
|
||||
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
|
||||
|
||||
<!--
|
||||
ARP icon + about-box link.
|
||||
-->
|
||||
<Property Id="ARPHELPLINK" Value="https://wilddragon.net" />
|
||||
<Property Id="ARPURLINFOABOUT" Value="https://wilddragon.net" />
|
||||
<!-- ARPNOMODIFY is set by WixUI_InstallDir; don't redeclare. -->
|
||||
<Property Id="ARPNOREPAIR" Value="1" />
|
||||
|
||||
<!--
|
||||
NDI Runtime detection. We check for NDI_RUNTIME_DIR_V6 in the system
|
||||
environment block. Missing → warn during install, don't block. The
|
||||
engine surfaces a clear MessageBox with an install-NDI link at first
|
||||
launch if the runtime really isn't there.
|
||||
-->
|
||||
<Property Id="NDIRUNTIMEDIR" Value="0">
|
||||
<RegistrySearch Id="NdiRuntimeDirV6Search"
|
||||
Root="HKLM"
|
||||
Key="SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
|
||||
Name="NDI_RUNTIME_DIR_V6"
|
||||
Type="raw" />
|
||||
</Property>
|
||||
|
||||
<!--
|
||||
NDI runtime detection is surfaced at first app launch (App.xaml.cs pops a
|
||||
MessageBox with an install link). We deliberately don't block install on
|
||||
a missing runtime so admins can stage the app before NDI is rolled out.
|
||||
VBScript-based install-time prompts are deprecated in WiX v5 / Windows
|
||||
and rewriting in C++ is overkill for a soft warning.
|
||||
-->
|
||||
|
||||
<!--
|
||||
Install layout under Program Files\Wild Dragon\TeamsISO.
|
||||
-->
|
||||
<StandardDirectory Id="ProgramFiles64Folder">
|
||||
<Directory Id="ManufacturerFolder" Name="Wild Dragon">
|
||||
<Directory Id="INSTALLFOLDER" Name="TeamsISO" />
|
||||
</Directory>
|
||||
</StandardDirectory>
|
||||
|
||||
<StandardDirectory Id="ProgramMenuFolder">
|
||||
<Directory Id="WildDragonStartMenuFolder" Name="Wild Dragon" />
|
||||
</StandardDirectory>
|
||||
|
||||
<!--
|
||||
Files: harvested from the publish output dir at build time.
|
||||
WiX v5 understands <Files Include="..."> with glob patterns and
|
||||
synthesizes one Component per file with stable GUIDs.
|
||||
-->
|
||||
<ComponentGroup Id="ApplicationFiles" Directory="INSTALLFOLDER">
|
||||
<Files Include="$(var.PublishDir)**" />
|
||||
</ComponentGroup>
|
||||
|
||||
<!--
|
||||
Start Menu shortcut to the WPF host. KeyPath sits on a registry
|
||||
value so component identity is stable across upgrades.
|
||||
-->
|
||||
<ComponentGroup Id="Shortcuts" Directory="WildDragonStartMenuFolder">
|
||||
<Component Id="StartMenuShortcut" Guid="*">
|
||||
<Shortcut Id="StartMenuTeamsISO"
|
||||
Name="TeamsISO"
|
||||
Description="Per-Participant NDI ISO Controller for Microsoft Teams"
|
||||
Target="[INSTALLFOLDER]TeamsISO.exe"
|
||||
WorkingDirectory="INSTALLFOLDER" />
|
||||
<!-- Required by ICE64: Start Menu folder must be cleaned on uninstall. -->
|
||||
<RemoveFolder Id="RemoveWildDragonStartMenuFolder"
|
||||
Directory="WildDragonStartMenuFolder"
|
||||
On="uninstall" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Wild Dragon\TeamsISO"
|
||||
Name="StartMenuShortcut"
|
||||
Type="integer"
|
||||
Value="1"
|
||||
KeyPath="yes" />
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
|
||||
<!--
|
||||
ARP icon registry entry. Optional — the MSI auto-fills most ARP
|
||||
fields from the Package element. We only need to point at the
|
||||
executable for the ARP icon.
|
||||
-->
|
||||
<ComponentGroup Id="ArpEntry" Directory="INSTALLFOLDER">
|
||||
<Component Id="ArpIconRegistry" Guid="*">
|
||||
<RegistryValue Root="HKLM"
|
||||
Key="Software\Wild Dragon\TeamsISO"
|
||||
Name="InstallPath"
|
||||
Type="string"
|
||||
Value="[INSTALLFOLDER]"
|
||||
KeyPath="yes" />
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
|
||||
</Package>
|
||||
</Wix>
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
<Project Sdk="WixToolset.Sdk/5.0.2">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Package</OutputType>
|
||||
<OutputName>TeamsISO-Setup-$(Version)</OutputName>
|
||||
|
||||
<!-- 64-bit MSI; suppresses ICE80 on components in Program Files (x64). -->
|
||||
<Platform>x64</Platform>
|
||||
<InstallerPlatform>x64</InstallerPlatform>
|
||||
|
||||
<!--
|
||||
Built artifact location. The installer expects a published build of
|
||||
TeamsISO.App rooted here. CI / local script:
|
||||
dotnet publish src/TeamsISO.App/TeamsISO.App.csproj
|
||||
-c Release -r win-x64 (with self contained false)
|
||||
-o $(SolutionDir)publish/TeamsISO
|
||||
-->
|
||||
<PublishDir>$(MSBuildThisFileDirectory)..\publish\TeamsISO\</PublishDir>
|
||||
|
||||
<!-- Pass MSBuild values into WiX preprocessor. -->
|
||||
<DefineConstants>PublishDir=$(PublishDir)</DefineConstants>
|
||||
|
||||
<!-- Code-signing hook (set externally for release builds; left empty for dev). -->
|
||||
<SignOutput Condition=" '$(SignOutput)' == '' ">false</SignOutput>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--
|
||||
Reference the WiX UI extension so the MSI shows a friendly progress UI
|
||||
instead of the silent default.
|
||||
-->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="WixToolset.UI.wixext" Version="5.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -1,124 +1,11 @@
|
|||
using System.Runtime.Versioning;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using TeamsISO.Engine.Interop;
|
||||
using TeamsISO.Engine.NdiInterop;
|
||||
|
||||
namespace TeamsISO.Engine.IntegrationTests;
|
||||
|
||||
/// <summary>
|
||||
/// Integration tests that talk to a real NDI runtime — gated behind
|
||||
/// <c>requires=ndi</c> so the default CI run skips them. Run locally with:
|
||||
/// dotnet test --filter requires=ndi
|
||||
///
|
||||
/// Pre-conditions:
|
||||
/// - Windows host
|
||||
/// - NDI 6 Runtime installed (NDI_RUNTIME_DIR_V6 set)
|
||||
/// - Network discovery permitted on the loopback / local subnet
|
||||
/// </summary>
|
||||
[SupportedOSPlatform("windows")]
|
||||
public class NdiInteropIntegrationTests
|
||||
public class IntegrationTestsScaffold
|
||||
{
|
||||
private static NdiInteropPInvoke NewInterop() =>
|
||||
new(NullLogger<NdiInteropPInvoke>.Instance);
|
||||
|
||||
[Fact]
|
||||
[Fact(Skip = "Phase A: integration tests require NDI runtime — added in Phase B.")]
|
||||
[Trait("requires", "ndi")]
|
||||
public void NdiRuntime_LoadsAndReportsVersion()
|
||||
public void ScaffoldFactSkipsCleanly()
|
||||
{
|
||||
using var interop = NewInterop();
|
||||
|
||||
var version = interop.GetRuntimeVersion();
|
||||
|
||||
version.Should().NotBeNullOrEmpty();
|
||||
version.Should().StartWith(NdiVersion.ExpectedRuntimeVersionPrefix,
|
||||
because: "the engine probe asserts this prefix; if it ever drifts CI must catch it");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("requires", "ndi")]
|
||||
public void Finder_DefaultGroups_CreatesAndDisposesCleanly()
|
||||
{
|
||||
using var interop = NewInterop();
|
||||
|
||||
using (var finder = interop.CreateFinder())
|
||||
{
|
||||
finder.Should().NotBeNull(because: "default-group finder must construct successfully");
|
||||
// Snapshot any visible sources — exercises the path; we don't assert on count
|
||||
// because the test environment's NDI sources are unknowable.
|
||||
_ = interop.GetCurrentSources(finder);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("requires", "ndi")]
|
||||
[InlineData("teamsiso-test-input")]
|
||||
[InlineData("teamsiso-test-input,production")]
|
||||
[InlineData(" teamsiso-test-input ")]
|
||||
public void Finder_CustomGroups_DoesNotThrow(string groups)
|
||||
{
|
||||
using var interop = NewInterop();
|
||||
|
||||
var act = () =>
|
||||
{
|
||||
using var finder = interop.CreateFinder(groups);
|
||||
_ = interop.GetCurrentSources(finder);
|
||||
};
|
||||
|
||||
act.Should().NotThrow(because: $"groups='{groups}' must round-trip into NDIlib_find_create_v2 cleanly");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("requires", "ndi")]
|
||||
public void Sender_DefaultGroups_CreatesAndDisposesCleanly()
|
||||
{
|
||||
using var interop = NewInterop();
|
||||
|
||||
using (var sender = interop.CreateSender("TEAMSISO_TEST_DEFAULT"))
|
||||
{
|
||||
sender.Should().NotBeNull();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("requires", "ndi")]
|
||||
public void Sender_CustomGroups_CreatesAndDisposesCleanly()
|
||||
{
|
||||
using var interop = NewInterop();
|
||||
|
||||
using (var sender = interop.CreateSender("TEAMSISO_TEST_GROUPED", "teamsiso-test-output"))
|
||||
{
|
||||
sender.Should().NotBeNull();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("requires", "ndi")]
|
||||
public async Task LoopbackDiscovery_FindsOurOwnSenderWithinFiveSeconds()
|
||||
{
|
||||
// End-to-end check: a sender we create on the local machine must be visible
|
||||
// to a finder running in the same process, within a reasonable window. Catches
|
||||
// network-layer regressions (firewall, mDNS, multicast disable).
|
||||
using var interop = NewInterop();
|
||||
|
||||
var uniqueName = $"TEAMSISO_LOOP_{Guid.NewGuid().ToString("N")[..8].ToUpperInvariant()}";
|
||||
using var sender = interop.CreateSender(uniqueName);
|
||||
using var finder = interop.CreateFinder();
|
||||
|
||||
var deadline = DateTime.UtcNow.AddSeconds(5);
|
||||
bool found = false;
|
||||
while (DateTime.UtcNow < deadline)
|
||||
{
|
||||
var sources = interop.GetCurrentSources(finder);
|
||||
if (sources.Any(s => s.Contains(uniqueName, StringComparison.Ordinal)))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
await Task.Delay(250);
|
||||
}
|
||||
|
||||
found.Should().BeTrue(
|
||||
because: $"local sender '{uniqueName}' must be discovered by a same-process finder within 5s");
|
||||
Assert.True(true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue