XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using Microsoft.UI.Xaml.Media;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Presentation;
using XFEEDToolBox.Core.Services;
using XFEEDToolBox.Views;

namespace XFEEDToolBox;

public partial class App
{
    private static async Task<object> RunActivityLogProbeAsync(string directory)
    {
        Directory.CreateDirectory(directory);
        string[] requirements=
        [
            "Use only actual Runtime activity entries; never inject a demonstration exception or replace player data.",
            "Refresh CurrentSystem read-only with extension preparation disabled in --activity-only mode; never start flight, commerce or a session workflow.",
            "Select LogTabs index 1 and capture the actual toolbox log page with a frozen array of real immutable entries.",
            "Record severity, first/last observation, occurrence count, diagnostic text and hashes from those same entries.",
            "If a real diagnostic exists, expand its actual realized row and capture it; otherwise mark expansion unconfirmed.",
            "Capture success is not visual acceptance: preserve layout, visible text, screenshot SHA-256 and outstanding pixel-review requirements."
        ];
        await File.WriteAllTextAsync(Path.Combine(directory,"activity-log-requirements.json"),JsonSerializer.Serialize(new{at=DateTimeOffset.UtcNow,requirements},ToolboxSettings.JsonOptions));
        var checks=new List<object>();var captures=new List<object>();var refreshes=new List<object>();var states=new List<object>();
        var originalSize=MainWindow.AppWindow.Size;var beforePlayer=Runtime.Player;
        var entries=Array.Empty<ActivityEntry>();var snapshotHash="";string? failure=null;long? expandedEntryId=null;
        LogViewPage? page=null;Pivot? tabs=null;ListView? list=null;var previousTab=0;var failed=0;var unconfirmed=0;
        void Check(string id,bool? passed,string evidence)
        {
            if(passed==false)failed++;if(passed is null)unconfirmed++;
            checks.Add(new{id,status=passed is null?"unconfirmed":passed.Value?"passed":"failed",evidence});
        }
        static string Hash(string value)=>Convert.ToHexStringLower(SHA256.HashData(Encoding.UTF8.GetBytes(value)));
        static string EntriesHash(ActivityEntry[] value)=>Convert.ToHexStringLower(SHA256.HashData(JsonSerializer.SerializeToUtf8Bytes(value,ToolboxSettings.JsonOptions)));
        bool Visible(FrameworkElement element)
        {
            DependencyObject? current=element;
            while(current is not null)
            {
                if(current is UIElement ui&&ui.Visibility!=Visibility.Visible)return false;
                if(ReferenceEquals(current,page))break;
                current=VisualTreeHelper.GetParent(current);
            }
            return element.ActualWidth>0&&element.ActualHeight>0;
        }
        object? Bounds(FrameworkElement? element)
        {
            if(page is null||element is null||element.ActualWidth<=0||element.ActualHeight<=0)return null;
            var origin=element.TransformToVisual(page).TransformPoint(new(0,0));
            return new{element.Name,x=origin.X,y=origin.Y,width=element.ActualWidth,height=element.ActualHeight,
                right=origin.X+element.ActualWidth,bottom=origin.Y+element.ActualHeight,visible=Visible(element)};
        }
        async Task Capture(string name)
        {
            // Reapply the frozen real snapshot, rather than reevaluating Runtime
            // enumerables at different points while capture awaits its pixels.
            ViewModel.Activity.Apply(entries);page!.UpdateLayout();await Task.Delay(100);
            var rowsBefore=ViewModel.Activity.Select(row=>row.Record).ToArray();
            var path=Path.Combine(directory,name+".png");await CapturePageAsync(page,path);
            var imageHash=Convert.ToHexStringLower(SHA256.HashData(await File.ReadAllBytesAsync(path)));
            var rowsAfter=ViewModel.Activity.Select(row=>row.Record).ToArray();
            var stable=rowsBefore.SequenceEqual(entries)&&rowsAfter.SequenceEqual(entries);
            var texts=Descendants(page).OfType<TextBlock>().Where(Visible).Select(text=>text.Text).Where(text=>!string.IsNullOrWhiteSpace(text)).Distinct().ToArray();
            var horizontal=Descendants(list!).OfType<ScrollViewer>().Any(scroll=>Visible(scroll)&&scroll.ScrollableWidth>1);
            captures.Add(new{name,path,sha256=imageHash,bytes=new FileInfo(path).Length});
            states.Add(new{name,pageWidth=page.ActualWidth,pageHeight=page.ActualHeight,windowWidth=MainWindow.AppWindow.Size.Width,windowHeight=MainWindow.AppWindow.Size.Height,
                selectedTab=tabs!.SelectedIndex,list=Bounds(list),horizontalOverflow=horizontal,snapshotHash,renderedSnapshotHash=EntriesHash(rowsAfter),snapshotStable=stable,
                realizedRowCount=Descendants(list!).OfType<ListViewItem>().Count(Visible),expandedEntryId,
                expandedDetails=Descendants(list!).OfType<Expander>().Where(expander=>Visible(expander)&&expander.IsExpanded).Select(expander=>new{header=expander.Header?.ToString(),bounds=Bounds(expander)}).ToArray(),texts});
            Check("frozen-source-"+name,stable,"Visible activity records before and after pixel capture must equal the frozen actual Runtime entries, including their counts and diagnostics.");
            Check("layout-"+name,!horizontal&&page.ActualWidth>0&&page.ActualHeight>0,"The selected toolbox log has a positive actual viewport and no realized horizontal scrolling; original pixels still require review.");
        }
        try
        {
            var args=Environment.GetCommandLineArgs();
            if(!args.Contains("--activity-only"))throw new InvalidOperationException("Activity acceptance requires --activity-only so CurrentSystem extension preparation is disabled.");
            if(ViewModel.DemoMode||ViewModel.OfflinePreview)throw new InvalidOperationException("Activity acceptance requires real Runtime data, without demonstration or offline substitution.");
            if(ViewModel.RunActive||ViewModel.NavigationActive||ViewModel.SystemFlightActive||ViewModel.SessionBusy)
                throw new InvalidOperationException("Activity acceptance cannot share this application with an active trade, flight or session operation.");
            ViewModel.ShowCurrentSystem();
            try
            {
                for(var attempt=0;attempt<3;attempt++)
                {
                    var elapsed=Stopwatch.StartNew();await ViewModel.RefreshCurrentSystemCommand.ExecuteAsync(null);
                    var until=DateTimeOffset.UtcNow.AddSeconds(20);
                    while(ViewModel.CurrentSystemReading&&DateTimeOffset.UtcNow<until)await Task.Delay(50);
                    refreshes.Add(new{attempt=attempt+1,at=DateTimeOffset.UtcNow,elapsedMilliseconds=elapsed.Elapsed.TotalMilliseconds,
                        ViewModel.CurrentSystemReading,ViewModel.CurrentSystemStatus,ViewModel.SystemControlAvailability,
                        source=ViewModel.CurrentSystemSceneSnapshot is {} source?new{source.SystemAddress,source.SystemName,source.EntityId,source.Sequence,source.ObservedAt}:null});
                    if(ViewModel.CurrentSystemReading)throw new TimeoutException("The actual read-only current-system refresh did not finish within the observation window.");
                    await Task.Delay(120);
                }
            }
            finally{ViewModel.HideCurrentSystem();}
            entries=Runtime.Activity.ToArray();snapshotHash=EntriesHash(entries);
            Check("real-current-system-observation",entries.Any(entry=>entry.Code.StartsWith("current-system.",StringComparison.Ordinal)),"The source is actual read-only refresh output, whether normal loading, successful reading or a real diagnostic. No expected outcome was manufactured.");
            Check("bounded-activity-snapshot",entries.Length<=200,"The actual Runtime retains the latest bounded activity snapshot; ordinary execution events remain separate.");
            Check("diagnostic-summary-separation",entries.Where(entry=>entry.HasDiagnostic).All(entry=>!entry.Message.Contains("\n   at ",StringComparison.Ordinal)&&!entry.Message.Contains("\n   在 ",StringComparison.Ordinal)),"Full stacks belong to diagnostic details rather than the visible summary.");
            var repeated=entries.Where(entry=>entry.Occurrences>1).ToArray();
            Check("real-aggregation",repeated.Length>0?repeated.All(entry=>entry.LastSeen>=entry.Time&&entry.RepetitionDisplay.Length>0):null,
                repeated.Length>0?"Real repeated observations retain first/last time and a visible count.":"No actual repeated diagnostic/status was observed; none was inserted for the probe.");
            AppShellPage.Current!.Navigate(typeof(LogViewPage));await Task.Delay(200);page=(LogViewPage)AppShellPage.Current.CurrentPage!;
            tabs=(Pivot)page.FindName("LogTabs");previousTab=tabs.SelectedIndex;tabs.SelectedIndex=1;
            ViewModel.Activity.Apply(entries);page.UpdateLayout();await Task.Delay(160);
            list=Descendants(page).OfType<ListView>().FirstOrDefault(candidate=>ReferenceEquals(candidate.ItemsSource,ViewModel.Activity))
                ??throw new InvalidOperationException("The actual toolbox activity ListView was not realized.");
            foreach(var expander in Descendants(list).OfType<Expander>())expander.IsExpanded=false;
            Check("toolbox-tab-selected",tabs.SelectedIndex==1,"LogTabs index 1 is the toolbox activity tab.");
            await Capture("activity-log-summary");
            var diagnostic=entries.FirstOrDefault(entry=>entry.HasDiagnostic);
            if(diagnostic is null)
                Check("real-diagnostic-expanded",null,"No real Runtime entry contains diagnostic details; expansion remains unconfirmed and no fake error was generated.");
            else
            {
                var row=ViewModel.Activity.Single(value=>value.Record.Id==diagnostic.Id);list.ScrollIntoView(row);page.UpdateLayout();await Task.Delay(160);
                var container=list.ContainerFromItem(row) as FrameworkElement;
                var expander=container is null?null:Descendants(container).OfType<Expander>().FirstOrDefault(value=>value.Header?.ToString()=="诊断详情");
                if(expander is null)Check("real-diagnostic-expanded",null,"A real diagnostic exists but its actual Expander was not realized; no substitute row was created.");
                else
                {
                    expandedEntryId=diagnostic.Id;expander.IsExpanded=true;page.UpdateLayout();expander.StartBringIntoView(new BringIntoViewOptions{AnimationDesired=false});await Task.Delay(120);
                    Check("real-diagnostic-expanded",expander.IsExpanded&&Visible(expander),"Expanded the existing real diagnostic row identified by its stable ActivityEntry Id.");
                    await Capture("activity-log-real-diagnostic");expander.IsExpanded=false;expandedEntryId=null;
                }
            }
            Check("no-execution-started",!ViewModel.RunActive&&!ViewModel.NavigationActive&&!ViewModel.SystemFlightActive&&!ViewModel.SessionBusy,
                "This probe invoked only read-only CurrentSystem refresh and log-page presentation; no execution, module preparation, menu operation or OS input was requested.");
        }
        catch(Exception error){failure=error.ToString();}
        finally
        {
            ViewModel.HideCurrentSystem();if(tabs is not null)tabs.SelectedIndex=previousTab;MainWindow.AppWindow.Resize(originalSize);
            var frozenChecks=checks.ToArray();var frozenCaptures=captures.ToArray();var frozenStates=states.ToArray();var frozenRefreshes=refreshes.ToArray();
            var frozenEntries=entries.Select(entry=>new{entry.Id,entry.Time,entry.LastSeen,entry.ClosedAt,entry.Kind,entry.Code,entry.Message,entry.FirstMessage,
                severity=entry.Severity.ToString(),entry.SeverityDisplay,entry.Occurrences,entry.RepetitionDisplay,entry.Resolved,entry.Superseded,entry.HasDiagnostic,
                entry.FirstDiagnostic,entry.LastDiagnostic,details=entry.DiagnosticDisplay,detailsSha256=Hash(entry.DiagnosticDisplay)}).ToArray();
            var current=Runtime.Player;
            await File.WriteAllTextAsync(Path.Combine(directory,"activity-log-evidence.json"),JsonSerializer.Serialize(new
            {
                at=DateTimeOffset.UtcNow,requirements,checks=frozenChecks,captures=frozenCaptures,states=frozenStates,refreshes=frozenRefreshes,entries=frozenEntries,snapshotHash,
                actualDataDirectory=Runtime.DataDirectory,failure,failedChecks=failed,unconfirmedChecks=unconfirmed,structurePassed=failure is null&&failed==0,
                playerObservation=new{before=new{beforePlayer.CommanderId,beforePlayer.CurrentShipId,beforePlayer.LoadGameAt,beforePlayer.CargoCount,beforePlayer.Credits},
                    after=new{current.CommanderId,current.CurrentShipId,current.LoadGameAt,current.CargoCount,current.Credits}},
                source="actual-runtime-activity-after-read-only-current-system-refresh",syntheticExceptions=false,syntheticPlayerData=false,
                gameFlightCommandsInvoked=false,tradeOrdersSubmitted=0,sessionOperationsInvoked=false,modulePreparationInvoked=false,gameRestartInvoked=false,osInputsSent=0,
                visualReview="Original pixels, local details and OCR still require independent visual inspection. Missing real diagnostics leave expansion unconfirmed."
            },ToolboxSettings.JsonOptions));
        }
        return new{failure,checks=checks.ToArray(),captures=captures.ToArray(),failedChecks=failed,unconfirmedChecks=unconfirmed,structurePassed=failure is null&&failed==0,
            entryCount=entries.Length,snapshotHash,realDiagnosticCount=entries.Count(entry=>entry.HasDiagnostic),gameActionsInvoked=false,syntheticExceptions=false};
    }
}