using System; using System.Runtime.InteropServices; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace MonoGame.Extended.Tests.Fixtures; /// /// A test fixture that provides a fully initialized MonoGame environment /// for unit testing graphics-related functionality in headless CI environments. /// public sealed class GraphicsTestFixture : IDisposable { public Game Game { get; } public GraphicsDevice GraphicsDevice => Game.GraphicsDevice; public SpriteBatch SpriteBatch { get; } public GraphicsTestFixture() { Game = new TestGame(); // Initialize the game completely Game.RunOneFrame(); // Ensure we have a valid graphics device if (Game.GraphicsDevice == null) { throw new InvalidOperationException("Failed to initialize GraphicsDevice. This may indicate an issue with the headless environment setup."); } Console.WriteLine("==================="); Console.WriteLine("Graphics Adapter: " + GraphicsDevice.Adapter.Description); Console.WriteLine("==================="); // Create commonly used graphics objects for tests SpriteBatch = new SpriteBatch(Game.GraphicsDevice); } public void Dispose() { SpriteBatch?.Dispose(); Game?.Dispose(); } /// /// Creates a test texture with the specified dimensions and color. /// Useful for setting up test scenarios. /// public Texture2D CreateTestTexture(int width, int height, Color color) { Texture2D texture = new Texture2D(GraphicsDevice, width, height); Color[] data = new Color[width * height]; Array.Fill(data, color); texture.SetData(data); return texture; } /// /// Creates a simple 1x1 white pixel texture for testing. /// public Texture2D CreatePixelTexture() => CreateTestTexture(1, 1, Color.White); /// /// Validates that the graphics device is properly initialized and functional. /// public void AssertGraphicsDeviceIsValid() { Assert.NotNull(GraphicsDevice); Assert.True(GraphicsDevice.Viewport.Width > 0); Assert.True(GraphicsDevice.Viewport.Height > 0); Assert.NotNull(GraphicsDevice.PresentationParameters); } private sealed class TestGame : Game { public GraphicsDeviceManager GraphicsDeviceManager { get; } public TestGame() { GraphicsDeviceManager = new GraphicsDeviceManager(this); // Configure for headless testing GraphicsDeviceManager.GraphicsProfile = GraphicsProfile.Reach; // Don't sync with vertical retrace for faster test execution GraphicsDeviceManager.SynchronizeWithVerticalRetrace = false; // On Windows, explicitly prefer WARP adapter for headless CI environments if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // Force selection of WARP (software renderer) adapter // This is done before initialization to ensure WARP is used in headless environments GraphicsDeviceManager.PreparingDeviceSettings += OnPreparingDeviceSettings; } } private void OnPreparingDeviceSettings(object? sender, PreparingDeviceSettingsEventArgs e) { // Try to find the WARP adapter (Microsoft Basic Render Driver) GraphicsAdapter? warpAdapter = null; foreach (GraphicsAdapter adapter in GraphicsAdapter.Adapters) { if (adapter.Description.Contains("Microsoft Basic Render Driver") || adapter.Description.Contains("WARP")) { warpAdapter = adapter; break; } } // If WARP adapter is found, use it if (warpAdapter != null) { e.GraphicsDeviceInformation.Adapter = warpAdapter; } } protected override void Draw(GameTime gameTime) { // Minimal draw for testing GraphicsDevice.Clear(Color.CornflowerBlue); base.Draw(gameTime); } } }