109 lines
4.0 KiB
C#
109 lines
4.0 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Xml.Linq;
|
|
using Xunit;
|
|
using ytLive.Models;
|
|
|
|
namespace ytLive.Tests;
|
|
|
|
/// <summary>
|
|
/// The output-profile rule: a free build may emit every shipped resolution at 30 fps;
|
|
/// 60 fps requires Pro, independent of resolution.
|
|
///
|
|
/// <para>The one thing that must never break: a cracked or unlicensed build silently gets a
|
|
/// *smaller frame*, never a smaller feature set. If a future tier is added above the budget,
|
|
/// the last test here fails instead of shipping it to everyone.</para>
|
|
/// </summary>
|
|
public class OutputProfileTests
|
|
{
|
|
/// <summary>Mirror of <c>MainViewModel.QualityOptions</c> — the shipped tiers. Kept in
|
|
/// step deliberately: this is the table the budget has to cover.</summary>
|
|
private static readonly QualityOption[] Shipped =
|
|
{
|
|
new("1080p60", 60, 8.0, 1920, 1080),
|
|
new("1080p30", 30, 8.0, 1920, 1080),
|
|
new("720p60", 60, 6.0, 1280, 720),
|
|
new("720p30", 30, 6.0, 1280, 720),
|
|
new("Vertical 1080p30", 30, 8.0, 1080, 1920),
|
|
new("Vertical 1080p60", 60, 8.0, 1080, 1920),
|
|
};
|
|
|
|
private static QualityOption Tier(string name) => Shipped.Single(t => t.Display == name);
|
|
|
|
[Fact]
|
|
public void A_free_build_resolves_1080p60_to_1080p30()
|
|
{
|
|
var resolved = OutputProfile.Resolve(Tier("1080p60"), Shipped, pro: false);
|
|
|
|
Assert.Equal("1080p30", resolved.Display);
|
|
Assert.Equal(1920, resolved.Width);
|
|
Assert.Equal(1080, resolved.Height);
|
|
Assert.Equal(30, resolved.Fps);
|
|
Assert.Equal(8.0, resolved.Bitrate);
|
|
}
|
|
|
|
[Fact]
|
|
public void A_free_build_leaves_its_own_tier_untouched()
|
|
{
|
|
var requested = Tier("1080p30");
|
|
Assert.Same(requested, OutputProfile.Resolve(requested, Shipped, pro: false));
|
|
}
|
|
|
|
[Fact]
|
|
public void Pro_passes_every_shipped_tier_through()
|
|
{
|
|
foreach (var tier in Shipped)
|
|
Assert.Same(tier, OutputProfile.Resolve(tier, Shipped, pro: true));
|
|
}
|
|
|
|
[Fact]
|
|
public void Free_access_depends_on_frame_rate_not_resolution()
|
|
{
|
|
foreach (var tier in Shipped)
|
|
{
|
|
var freeAllowed = tier.Fps <= OutputProfile.FreeMaxFps;
|
|
Assert.Equal(freeAllowed, OutputProfile.IsAllowed(tier, pro: false));
|
|
Assert.Equal(!freeAllowed, tier.RequiresPro);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void An_empty_tier_table_still_yields_a_legal_frame()
|
|
{
|
|
var resolved = OutputProfile.Resolve(Tier("1080p60"), Array.Empty<QualityOption>(), pro: false);
|
|
|
|
Assert.Equal("1080p30", resolved.Display);
|
|
Assert.Equal(1920, resolved.Width);
|
|
Assert.Equal(1080, resolved.Height);
|
|
Assert.Equal(30, resolved.Fps);
|
|
Assert.Equal(8.0, resolved.Bitrate);
|
|
}
|
|
|
|
/// <summary>
|
|
/// The developer override must not exist in a shipped binary. A runtime test cannot prove
|
|
/// that (the test host is a Debug build), so this asserts the build rule itself: the
|
|
/// panel's XAML and code-behind are removed whenever the configuration is not Debug.
|
|
/// </summary>
|
|
[Fact]
|
|
public void The_developer_panel_is_excluded_from_the_release_compile()
|
|
{
|
|
var project = XDocument.Load(SourceAssert.RepoPath("ytLive.csproj"));
|
|
var guards = project.Descendants()
|
|
.Where(e => e.Name.LocalName is "Page" or "Compile")
|
|
.Where(e => (string?)e.Attribute("Remove") is string remove
|
|
&& remove.Contains("DeveloperPanel", StringComparison.OrdinalIgnoreCase))
|
|
.ToArray();
|
|
|
|
Assert.Equal(2, guards.Length);
|
|
foreach (var guard in guards)
|
|
{
|
|
// The condition may sit on the item or on its ItemGroup — either excludes it.
|
|
var condition = (string?)guard.Attribute("Condition")
|
|
?? (string?)guard.Parent?.Attribute("Condition");
|
|
Assert.NotNull(condition);
|
|
Assert.Contains("'$(Configuration)' != 'Debug'", condition, StringComparison.Ordinal);
|
|
}
|
|
}
|
|
}
|