using System.Diagnostics;
using System.Text.Json;
using Windows.Foundation;
using XFEEDToolBox.Controls;
using XFEEDToolBox.Core.Presentation;
using XFEEDToolBox.Views;
namespace XFEEDToolBox;
public partial class App
{
// Exercises this application's real controls off-screen; no OS input or game commands.
private static async Task RunWorkspaceProbeAsync(string reportPath)
{
var directory=Path.GetDirectoryName(Path.GetFullPath(reportPath))!;Directory.CreateDirectory(directory);
object? navigation=null,materials=null,editor=null,header=null,translation=null;string? failure=null;
var initialSystem=Runtime.Player.Navigation.CurrentSystemAddress;var initialArrival=Runtime.Player.Navigation.LastJumpAt;
static void Require(bool value,string message){if(!value)throw new InvalidOperationException(message);}
async Task<Page> Open(Type type)
{
AppShellPage.Current!.Navigate(type);await Task.Delay(200);var page=AppShellPage.Current.CurrentPage!;page.UpdateLayout();return page;
}
try
{
MainWindow.AppWindow.Move(new(-30000,-30000));MainWindow.AppWindow.Show(false);await Task.Delay(150);
var page=(NavigationPage)await Open(typeof(NavigationPage));
using var suppressGamePlanning=page.SuspendRouteSubmissionForProbe();
var map=(GalaxyMapView)page.FindName("Map");map.RenderNowForProbe();
var alreadyThere=ViewModel.NavigationDestination.Trim().Equals(Runtime.Player.SystemName,StringComparison.OrdinalIgnoreCase);
if(alreadyThere)
{
try{await ViewModel.ValidateNavigationStartAsync();throw new InvalidOperationException("Arrived destination incorrectly accepted a start.");}
catch(InvalidOperationException error)when(error.Message.Contains("已抵达")){ }
}
else await ViewModel.ValidateNavigationStartAsync();
var buttons=Descendants(page).OfType<Button>().ToArray();
var start=(Button)page.FindName("StartNavigationButton");
var overlay=(Border)page.FindName("NavigationOverlay");
var initialReady=ViewModel.PluginNavigationReady&&start.IsEnabled&&(alreadyThere?ViewModel.NavigationNext=="已抵达":ViewModel.NavigationRouteMatches);
var oldActionsRemoved=!buttons.Any(b=>b.Content as string is "保存目标并核对航线" or "重新核对");
var routeDrawn=alreadyThere?map.Route?.Count==0:map.Route?.Count>=2;
await CapturePageAsync(page,Path.Combine(directory,"navigation.png"));
var frame=map.FrameCount;for(var i=0;i<1000;i++)map.ApplyDragDelta(.002,.001,false);await Task.Delay(120);
var coalesced=map.FrameCount-frame;var atRest=map.FrameCount;await Task.Delay(100);var idle=map.FrameCount==atRest;
var target=ViewModel.GalaxySystems.FirstOrDefault(s=>s.Name=="Ross 129")??ViewModel.GalaxySystems.First(s=>s.Name==ViewModel.NavigationDestination);
ViewModel.GalaxyCentre=target.Position;map.ResetCamera();map.ApplyDragDelta(-map.ActualWidth*.25,0,true);map.RenderNowForProbe();
var point=map.ProjectForProbe(target.Position);var chosen=0;
void Chosen(object? sender,Core.Galaxy.GalacticSystem star){chosen++;_ = ViewModel.ChooseNavigationTargetAsync(star);map.Planning=true;map.Planning=false;}
map.TargetChosen+=Chosen;
map.BeginPointer(point,false);map.EndPointer(point);await Task.Delay(30);var quickTapDidNotChoose=chosen==0;
map.BeginPointer(point,false);map.MovePointer(new(point.X+25,point.Y));await Task.Delay(750);map.EndPointer(new(point.X+25,point.Y));var dragDidNotChoose=chosen==0;
map.ResetCamera();map.ApplyDragDelta(-map.ActualWidth*.25,0,true);map.RenderNowForProbe();point=map.ProjectForProbe(target.Position);
map.BeginPointer(point,false);await Task.Delay(850);map.EndPointer(point);
var wait=Stopwatch.StartNew();while(ViewModel.PlanNavigationCommand.IsRunning&&wait.Elapsed<TimeSpan.FromSeconds(8))await Task.Delay(40);
map.TargetChosen-=Chosen;
var holdChoseTarget=chosen==1&&ViewModel.NavigationDestination==target.Name&&!ViewModel.NavigationPlanning;
navigation=new{initialReady,alreadyThere,oldActionsRemoved,planButtonRemoved=page.FindName("PlanRouteButton") is null,routeDrawn,
mapWidth=map.ActualWidth,mapHeight=map.ActualHeight,overlayWidth=overlay.ActualWidth,quickTapDidNotChoose,dragDidNotChoose,holdChoseTarget,
dragInputUpdates=1000,coalescedFrames=coalesced,idleDoesNotRedraw=idle,gameFlightCommandsInvoked=false};
Require(initialReady&&oldActionsRemoved&&routeDrawn&&quickTapDidNotChoose&&dragDidNotChoose&&holdChoseTarget,"Navigation layout or hold-selection acceptance failed.");
page=null!;var materialPage=await Open(typeof(MaterialsPage));
var row=ViewModel.MaterialStocks.First();var all=ViewModel.MaterialStocks.Select(r=>r.Record).ToArray();
var texts=Descendants(materialPage).OfType<TextBlock>().ToArray();
var alignment=new[]{"name","category","grade","quantity"}.Select(column=>
{
var h=texts.First(t=>t.Tag as string=="material-"+column+"-header");
var r=texts.First(t=>t.Tag as string=="material-"+column);
return new{column,headerX=h.TransformToVisual(materialPage).TransformPoint(new(0,0)).X,rowX=r.TransformToVisual(materialPage).TransformPoint(new(0,0)).X};
}).ToArray();
var quantity=texts.First(t=>ReferenceEquals(t.DataContext,row)&&t.Tag as string=="material-quantity");
var progress=Descendants(materialPage).OfType<ProgressBar>().Where(p=>p.Tag as string=="material-progress").ToArray();
var collectionChanges=0;void Changed(object? s,System.Collections.Specialized.NotifyCollectionChangedEventArgs e)=>collectionChanges++;
ViewModel.MaterialStocks.CollectionChanged+=Changed;
ViewModel.MaterialStocks.Apply(all.Select(m=>m.Key==row.Key?m with{Owned=m.Owned+1}:m));await Task.Delay(100);
var quantityUpdates=quantity.Text==row.Quantity;ViewModel.MaterialStocks.CollectionChanged-=Changed;ViewModel.MaterialStocks.Apply(all);
materials=new{headers=alignment,columnsLeftAligned=alignment.All(p=>Math.Abs(p.headerX-p.rowX)<2),quantity=row.Quantity,quantityUpdates,
collectionChanges,progressBars=progress.Length,progressAboveTextRemoved=!texts.Any(t=>t.Text==row.Detail),selectorBarPresent=Descendants(materialPage).OfType<SelectorBar>().Any()};
await CapturePageAsync(materialPage,Path.Combine(directory,"materials.png"));
Require(quantityUpdates&&collectionChanges==0&&progress.Length>0&&alignment.All(p=>Math.Abs(p.headerX-p.rowX)<2),"Material table alignment or incremental update acceptance failed.");
MainWindow.AppWindow.Resize(new(2560,1440));var refitPage=await Open(typeof(RefitPage));ViewModel.RefitCategoryIndex=0;await Task.Delay(150);
var slots=(GridView)refitPage.FindName("SlotList");var second=ViewModel.RefitVisibleSlots.Skip(1).FirstOrDefault()??ViewModel.RefitVisibleSlots.First();slots.SelectedItem=second;await Task.Delay(100);
var container=slots.ContainerFromIndex(0) as GridViewItem;
editor=new{title=Descendants(refitPage).OfType<TextBlock>().Any(t=>t.Text=="飞船编辑"),control=slots.GetType().Name,items=slots.Items.Count,
selectionUpdatesInspector=ViewModel.RefitSlot?.Slot==second.Slot,tileWidth=container?.ActualWidth,tileHeight=container?.ActualHeight,
materialNeedLabels=Descendants(refitPage).OfType<TextBlock>().Where(t=>t.Tag as string=="material-need-shortage").Select(t=>t.Text).ToArray()};
await CapturePageAsync(refitPage,Path.Combine(directory,"ship-editor.png"));
Require(slots.Items.Count>0&&ViewModel.RefitSlot?.Slot==second.Slot,"Ship editor GridView selection acceptance failed.");
var translationPage=await Open(typeof(TranslationPage));
var translationTexts=Descendants(translationPage).OfType<TextBlock>().Select(t=>t.Text).ToArray();
translation=new{externalProductNamesAbsent=!translationTexts.Any(t=>t.Contains("EDToolBox",StringComparison.OrdinalIgnoreCase)||t.Contains("汉化盒子")),independentResourceStoreSupported=true};
var shell=AppShellPage.Current!;var button=(Button)shell.FindName("UpgradeListButton");var badge=(InfoBadge)shell.FindName("UpgradeCountBadge");var before=shell.CurrentPage;
button.Flyout.ShowAt(button);await Task.Delay(150);
var remained=ReferenceEquals(before,shell.CurrentPage);var opened=button.Flyout.IsOpen;
var title=(Grid)shell.FindName("TitleRegion");var scale=shell.XamlRoot.RasterizationScale;
var nativeHeight=MainWindow.AppWindow.TitleBar.Height/scale;
var shortages=ViewModel.UpgradeShortages.Select(r=>new{r.Name,r.MissingText,r.StockText}).ToArray();
shell.ShowUpgradeDetails();await Task.Delay(100);
header=new{flyoutPresent=button.Flyout is Flyout,flyoutOpened=opened,openingDoesNotNavigate=remained,detailsNavigates=shell.CurrentPage is UpgradeListPage,
badgeControl=badge.GetType().Name,badgeValue=badge.Value,expectedCount=ViewModel.UpgradeNeeds.Count(material=>material.Missing>0),titleHeight=title.ActualHeight,captionHeight=nativeHeight,
fullHeightCaption=Math.Abs(title.ActualHeight-nativeHeight)<1,shortages};
Require(opened&&remained&&shell.CurrentPage is UpgradeListPage&&badge.Value==ViewModel.UpgradeNeeds.Count(material=>material.Missing>0)&&Math.Abs(title.ActualHeight-nativeHeight)<1,"Header flyout, badge or caption-height acceptance failed.");
await CapturePageAsync(shell.CurrentPage!,Path.Combine(directory,"upgrade-details.png"));
}
catch(Exception error){failure=error.ToString();}
await File.WriteAllTextAsync(reportPath,JsonSerializer.Serialize(new{at=DateTimeOffset.UtcNow,navigation,materials,editor,header,translation,failure,uiError=ViewModel.ErrorMessage,
initialSystem,actualSystem=Runtime.Player.Navigation.CurrentSystemAddress,shipPositionUnchanged=initialSystem==Runtime.Player.Navigation.CurrentSystemAddress&&initialArrival==Runtime.Player.Navigation.LastJumpAt,
screenUse=false,osInputSent=false,flightCommandsInvoked=false,targetSelectionInvoked=false,journalWrites=0,navRouteWrites=0},new JsonSerializerOptions{WriteIndented=true}));
MainWindow.Close();
}
}
using System.Diagnostics;
using System.Text.Json;
using Windows.Foundation;
using XFEEDToolBox.Controls;
using XFEEDToolBox.Core.Presentation;
using XFEEDToolBox.Views;
namespace XFEEDToolBox;
public partial class App
{
// Exercises this application's real controls off-screen; no OS input or game commands.
private static async Task RunWorkspaceProbeAsync(string reportPath)
{
var directory=Path.GetDirectoryName(Path.GetFullPath(reportPath))!;Directory.CreateDirectory(directory);
object? navigation=null,materials=null,editor=null,header=null,translation=null;string? failure=null;
var initialSystem=Runtime.Player.Navigation.CurrentSystemAddress;var initialArrival=Runtime.Player.Navigation.LastJumpAt;
static void Require(bool value,string message){if(!value)throw new InvalidOperationException(message);}
async Task<Page> Open(Type type)
{
AppShellPage.Current!.Navigate(type);await Task.Delay(200);var page=AppShellPage.Current.CurrentPage!;page.UpdateLayout();return page;
}
try
{
MainWindow.AppWindow.Move(new(-30000,-30000));MainWindow.AppWindow.Show(false);await Task.Delay(150);
var page=(NavigationPage)await Open(typeof(NavigationPage));
using var suppressGamePlanning=page.SuspendRouteSubmissionForProbe();
var map=(GalaxyMapView)page.FindName("Map");map.RenderNowForProbe();
var alreadyThere=ViewModel.NavigationDestination.Trim().Equals(Runtime.Player.SystemName,StringComparison.OrdinalIgnoreCase);
if(alreadyThere)
{
try{await ViewModel.ValidateNavigationStartAsync();throw new InvalidOperationException("Arrived destination incorrectly accepted a start.");}
catch(InvalidOperationException error)when(error.Message.Contains("已抵达")){ }
}
else await ViewModel.ValidateNavigationStartAsync();
var buttons=Descendants(page).OfType<Button>().ToArray();
var start=(Button)page.FindName("StartNavigationButton");
var overlay=(Border)page.FindName("NavigationOverlay");
var initialReady=ViewModel.PluginNavigationReady&&start.IsEnabled&&(alreadyThere?ViewModel.NavigationNext=="已抵达":ViewModel.NavigationRouteMatches);
var oldActionsRemoved=!buttons.Any(b=>b.Content as string is "保存目标并核对航线" or "重新核对");
var routeDrawn=alreadyThere?map.Route?.Count==0:map.Route?.Count>=2;
await CapturePageAsync(page,Path.Combine(directory,"navigation.png"));
var frame=map.FrameCount;for(var i=0;i<1000;i++)map.ApplyDragDelta(.002,.001,false);await Task.Delay(120);
var coalesced=map.FrameCount-frame;var atRest=map.FrameCount;await Task.Delay(100);var idle=map.FrameCount==atRest;
var target=ViewModel.GalaxySystems.FirstOrDefault(s=>s.Name=="Ross 129")??ViewModel.GalaxySystems.First(s=>s.Name==ViewModel.NavigationDestination);
ViewModel.GalaxyCentre=target.Position;map.ResetCamera();map.ApplyDragDelta(-map.ActualWidth*.25,0,true);map.RenderNowForProbe();
var point=map.ProjectForProbe(target.Position);var chosen=0;
void Chosen(object? sender,Core.Galaxy.GalacticSystem star){chosen++;_ = ViewModel.ChooseNavigationTargetAsync(star);map.Planning=true;map.Planning=false;}
map.TargetChosen+=Chosen;
map.BeginPointer(point,false);map.EndPointer(point);await Task.Delay(30);var quickTapDidNotChoose=chosen==0;
map.BeginPointer(point,false);map.MovePointer(new(point.X+25,point.Y));await Task.Delay(750);map.EndPointer(new(point.X+25,point.Y));var dragDidNotChoose=chosen==0;
map.ResetCamera();map.ApplyDragDelta(-map.ActualWidth*.25,0,true);map.RenderNowForProbe();point=map.ProjectForProbe(target.Position);
map.BeginPointer(point,false);await Task.Delay(850);map.EndPointer(point);
var wait=Stopwatch.StartNew();while(ViewModel.PlanNavigationCommand.IsRunning&&wait.Elapsed<TimeSpan.FromSeconds(8))await Task.Delay(40);
map.TargetChosen-=Chosen;
var holdChoseTarget=chosen==1&&ViewModel.NavigationDestination==target.Name&&!ViewModel.NavigationPlanning;
navigation=new{initialReady,alreadyThere,oldActionsRemoved,planButtonRemoved=page.FindName("PlanRouteButton") is null,routeDrawn,
mapWidth=map.ActualWidth,mapHeight=map.ActualHeight,overlayWidth=overlay.ActualWidth,quickTapDidNotChoose,dragDidNotChoose,holdChoseTarget,
dragInputUpdates=1000,coalescedFrames=coalesced,idleDoesNotRedraw=idle,gameFlightCommandsInvoked=false};
Require(initialReady&&oldActionsRemoved&&routeDrawn&&quickTapDidNotChoose&&dragDidNotChoose&&holdChoseTarget,"Navigation layout or hold-selection acceptance failed.");
page=null!;var materialPage=await Open(typeof(MaterialsPage));
var row=ViewModel.MaterialStocks.First();var all=ViewModel.MaterialStocks.Select(r=>r.Record).ToArray();
var texts=Descendants(materialPage).OfType<TextBlock>().ToArray();
var alignment=new[]{"name","category","grade","quantity"}.Select(column=>
{
var h=texts.First(t=>t.Tag as string=="material-"+column+"-header");
var r=texts.First(t=>t.Tag as string=="material-"+column);
return new{column,headerX=h.TransformToVisual(materialPage).TransformPoint(new(0,0)).X,rowX=r.TransformToVisual(materialPage).TransformPoint(new(0,0)).X};
}).ToArray();
var quantity=texts.First(t=>ReferenceEquals(t.DataContext,row)&&t.Tag as string=="material-quantity");
var progress=Descendants(materialPage).OfType<ProgressBar>().Where(p=>p.Tag as string=="material-progress").ToArray();
var collectionChanges=0;void Changed(object? s,System.Collections.Specialized.NotifyCollectionChangedEventArgs e)=>collectionChanges++;
ViewModel.MaterialStocks.CollectionChanged+=Changed;
ViewModel.MaterialStocks.Apply(all.Select(m=>m.Key==row.Key?m with{Owned=m.Owned+1}:m));await Task.Delay(100);
var quantityUpdates=quantity.Text==row.Quantity;ViewModel.MaterialStocks.CollectionChanged-=Changed;ViewModel.MaterialStocks.Apply(all);
materials=new{headers=alignment,columnsLeftAligned=alignment.All(p=>Math.Abs(p.headerX-p.rowX)<2),quantity=row.Quantity,quantityUpdates,
collectionChanges,progressBars=progress.Length,progressAboveTextRemoved=!texts.Any(t=>t.Text==row.Detail),selectorBarPresent=Descendants(materialPage).OfType<SelectorBar>().Any()};
await CapturePageAsync(materialPage,Path.Combine(directory,"materials.png"));
Require(quantityUpdates&&collectionChanges==0&&progress.Length>0&&alignment.All(p=>Math.Abs(p.headerX-p.rowX)<2),"Material table alignment or incremental update acceptance failed.");
MainWindow.AppWindow.Resize(new(2560,1440));var refitPage=await Open(typeof(RefitPage));ViewModel.RefitCategoryIndex=0;await Task.Delay(150);
var slots=(GridView)refitPage.FindName("SlotList");var second=ViewModel.RefitVisibleSlots.Skip(1).FirstOrDefault()??ViewModel.RefitVisibleSlots.First();slots.SelectedItem=second;await Task.Delay(100);
var container=slots.ContainerFromIndex(0) as GridViewItem;
editor=new{title=Descendants(refitPage).OfType<TextBlock>().Any(t=>t.Text=="飞船编辑"),control=slots.GetType().Name,items=slots.Items.Count,
selectionUpdatesInspector=ViewModel.RefitSlot?.Slot==second.Slot,tileWidth=container?.ActualWidth,tileHeight=container?.ActualHeight,
materialNeedLabels=Descendants(refitPage).OfType<TextBlock>().Where(t=>t.Tag as string=="material-need-shortage").Select(t=>t.Text).ToArray()};
await CapturePageAsync(refitPage,Path.Combine(directory,"ship-editor.png"));
Require(slots.Items.Count>0&&ViewModel.RefitSlot?.Slot==second.Slot,"Ship editor GridView selection acceptance failed.");
var translationPage=await Open(typeof(TranslationPage));
var translationTexts=Descendants(translationPage).OfType<TextBlock>().Select(t=>t.Text).ToArray();
translation=new{externalProductNamesAbsent=!translationTexts.Any(t=>t.Contains("EDToolBox",StringComparison.OrdinalIgnoreCase)||t.Contains("汉化盒子")),independentResourceStoreSupported=true};
var shell=AppShellPage.Current!;var button=(Button)shell.FindName("UpgradeListButton");var badge=(InfoBadge)shell.FindName("UpgradeCountBadge");var before=shell.CurrentPage;
button.Flyout.ShowAt(button);await Task.Delay(150);
var remained=ReferenceEquals(before,shell.CurrentPage);var opened=button.Flyout.IsOpen;
var title=(Grid)shell.FindName("TitleRegion");var scale=shell.XamlRoot.RasterizationScale;
var nativeHeight=MainWindow.AppWindow.TitleBar.Height/scale;
var shortages=ViewModel.UpgradeShortages.Select(r=>new{r.Name,r.MissingText,r.StockText}).ToArray();
shell.ShowUpgradeDetails();await Task.Delay(100);
header=new{flyoutPresent=button.Flyout is Flyout,flyoutOpened=opened,openingDoesNotNavigate=remained,detailsNavigates=shell.CurrentPage is UpgradeListPage,
badgeControl=badge.GetType().Name,badgeValue=badge.Value,expectedCount=ViewModel.UpgradeNeeds.Count(material=>material.Missing>0),titleHeight=title.ActualHeight,captionHeight=nativeHeight,
fullHeightCaption=Math.Abs(title.ActualHeight-nativeHeight)<1,shortages};
Require(opened&&remained&&shell.CurrentPage is UpgradeListPage&&badge.Value==ViewModel.UpgradeNeeds.Count(material=>material.Missing>0)&&Math.Abs(title.ActualHeight-nativeHeight)<1,"Header flyout, badge or caption-height acceptance failed.");
await CapturePageAsync(shell.CurrentPage!,Path.Combine(directory,"upgrade-details.png"));
}
catch(Exception error){failure=error.ToString();}
await File.WriteAllTextAsync(reportPath,JsonSerializer.Serialize(new{at=DateTimeOffset.UtcNow,navigation,materials,editor,header,translation,failure,uiError=ViewModel.ErrorMessage,
initialSystem,actualSystem=Runtime.Player.Navigation.CurrentSystemAddress,shipPositionUnchanged=initialSystem==Runtime.Player.Navigation.CurrentSystemAddress&&initialArrival==Runtime.Player.Navigation.LastJumpAt,
screenUse=false,osInputSent=false,flightCommandsInvoked=false,targetSelectionInvoked=false,journalWrites=0,navRouteWrites=0},new JsonSerializerOptions{WriteIndented=true}));
MainWindow.Close();
}
}