JAL-3609 JAL-3775 Tests for jalview.bin.HiDPISetting
authorBen Soares <b.soares@dundee.ac.uk>
Tue, 1 Dec 2020 14:52:26 +0000 (14:52 +0000)
committerBen Soares <b.soares@dundee.ac.uk>
Tue, 1 Dec 2020 14:52:26 +0000 (14:52 +0000)
src/jalview/gui/Desktop.java
test/jalview/bin/HiDPISettingTest1.java [new file with mode: 0644]
test/jalview/bin/HiDPISettingTest2.java [new file with mode: 0644]
test/jalview/bin/hidpiTestProps.jvprops [new file with mode: 0644]

index 21092e6..2482dea 100644 (file)
@@ -25,6 +25,7 @@ import java.awt.Color;
 import java.awt.Dimension;
 import java.awt.FontMetrics;
 import java.awt.Graphics;
+import java.awt.Graphics2D;
 import java.awt.GridLayout;
 import java.awt.Point;
 import java.awt.Rectangle;
@@ -47,6 +48,7 @@ import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
+import java.awt.geom.AffineTransform;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.io.File;
@@ -159,6 +161,8 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
 
+  public static boolean nosplash = false;
+
   /**
    * news reader - null if it was never started.
    */
@@ -460,15 +464,17 @@ public class Desktop extends jalview.jbgui.GDesktop
       checkURLLinks();
 
       // Spawn a thread that shows the splashscreen
-
-      SwingUtilities.invokeLater(new Runnable()
+      if (!nosplash)
       {
-        @Override
-        public void run()
+        SwingUtilities.invokeLater(new Runnable()
         {
-          new SplashScreen(true);
-        }
-      });
+          @Override
+          public void run()
+          {
+            new SplashScreen(true);
+          }
+        });
+      }
 
       // Thread off a new instance of the file chooser - this reduces the time
       // it
@@ -533,7 +539,6 @@ public class Desktop extends jalview.jbgui.GDesktop
       }
     });
     desktop.addMouseListener(ma);
-
   }
 
   /**
@@ -2278,6 +2283,9 @@ public class Desktop extends jalview.jbgui.GDesktop
                   10, getHeight() - fm.getHeight());
         }
       }
+
+      // output debug scale message. Important for jalview.bin.HiDPISettingTest2
+      Desktop.debugScaleMessage(Desktop.getDesktop().getGraphics());
     }
   }
 
@@ -3367,4 +3375,37 @@ public class Desktop extends jalview.jbgui.GDesktop
     }
     return result;
   }
+
+  public static final String debugScaleMessage = "Desktop graphics transform scale=";
+
+  private static boolean debugScaleMessageDone = false;
+
+  public static void debugScaleMessage(Graphics g)
+  {
+    if (debugScaleMessageDone)
+    {
+      return;
+    }
+    // output used by tests to check HiDPI scaling settings in action
+    try
+    {
+      Graphics2D gg = (Graphics2D) g;
+      if (gg != null)
+      {
+        AffineTransform t = gg.getTransform();
+        double scaleX = t.getScaleX();
+        double scaleY = t.getScaleY();
+        Cache.debug(debugScaleMessage + scaleX + " (X)");
+        Cache.debug(debugScaleMessage + scaleY + " (Y)");
+        debugScaleMessageDone = true;
+      }
+      else
+      {
+        Cache.debug("Desktop graphics null");
+      }
+    } catch (Exception e)
+    {
+      Cache.debug(Cache.getStackTraceString(e));
+    }
+  }
 }
diff --git a/test/jalview/bin/HiDPISettingTest1.java b/test/jalview/bin/HiDPISettingTest1.java
new file mode 100644 (file)
index 0000000..fd27a34
--- /dev/null
@@ -0,0 +1,116 @@
+package jalview.bin;
+
+import static org.testng.Assert.assertEquals;
+
+import org.mockito.Mockito;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
+import jalview.gui.JvOptionPane;
+import jalview.io.DataSourceType;
+import jalview.io.FileLoader;
+
+/*
+ * Testing a HiDPI display is difficult without running in a HiDPI display.
+ * The gathering of screen size and resolution performed by jalview.bin.HiDPISetting
+ * is now farmed out into a separate class jalview.bin.ScreenInfo so it can be more
+ * easily Mocked in tests.
+ * Two sets of tests are performed.
+ * 1) testLinuxScalePropertyToActualTransform() sets the property that HiDPISetting
+ * uses (via jalview.bin.Launcher or getdown) to scale up Jalview, and then looks at
+ * the alignment panel graphics2d transform to see if it's been scaled by the same
+ * amount (in this case 4 -- unlikely to happen by accident!).
+ * 2) testHiDPISettingInit() which tests the calculation that HiDPISetting uses to
+ * decide from apparent screen information (mocked using Mockito in the tests) what
+ * scaling factor to set (it doesn't actually set it, just suggests it to
+ * jalview.bin.Launcher)
+ */
+public class HiDPISettingTest1
+{
+
+  AlignFrame af = null;
+
+  @BeforeMethod(alwaysRun = true)
+  public void setUp()
+  {
+    JvOptionPane.setInteractiveMode(false);
+    JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
+    Cache.loadProperties("test/jalview/bin/hidpiTestProps.jvprops");
+    Jalview.main(
+            new String[]
+            { "-nosplash", "-nonews", "-noquestionnaire",
+                "-nowebservicediscovery" });
+
+    af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
+            DataSourceType.FILE);
+
+    /*
+     * wait for Consensus thread to complete
+     */
+    do
+    {
+      try
+      {
+        Thread.sleep(50);
+      } catch (InterruptedException x)
+      {
+      }
+    } while (af.getViewport().getCalcManager().isWorking());
+  }
+
+  @AfterClass(alwaysRun = true)
+  public void tearDown()
+  {
+    Desktop.instance.closeAll_actionPerformed(null);
+  }
+
+  @Test(groups = { "Functional" })
+  public void testHiDPISettingInit()
+  {
+    String scalePropertyName = "sun.java2d.uiScale";
+    // ensure scale property is cleared (otherwise it will be re-used)
+    System.clearProperty(HiDPISetting.scalePropertyName);
+
+    { // keep the mock under lock
+      // Ancient monitor -- no property change set
+      setMockScreen(1024, 768, 72);
+      assertEquals(HiDPISetting.getScalePropertyArg(), null);
+
+      // Old monitor -- no property change set
+      setMockScreen(1200, 800, 96);
+      assertEquals(HiDPISetting.getScalePropertyArg(), null);
+
+      // HD screen -- no property change set
+      setMockScreen(1920, 1080, 96);
+      assertEquals(HiDPISetting.getScalePropertyArg(), null);
+
+      // 4K screen -- scale by 2
+      setMockScreen(3180, 2160, 80);
+      assertEquals(HiDPISetting.getScalePropertyArg(),
+              "-D" + scalePropertyName + "=2");
+
+      // 4K screen with high dpi -- scale by 3
+      setMockScreen(3180, 2160, 450);
+      assertEquals(HiDPISetting.getScalePropertyArg(),
+              "-D" + scalePropertyName + "=3");
+
+      // stupidly big screen -- scale by 8
+      setMockScreen(19200, 10800, 72);
+      assertEquals(HiDPISetting.getScalePropertyArg(),
+              "-D" + scalePropertyName + "=8");
+    }
+  }
+
+  private void setMockScreen(int width, int height, int dpi)
+  {
+    HiDPISetting.clear();
+    ScreenInfo mockScreenInfo = Mockito.spy(HiDPISetting.getScreenInfo());
+    Mockito.doReturn(height).when(mockScreenInfo).getScreenHeight();
+    Mockito.doReturn(width).when(mockScreenInfo).getScreenWidth();
+    Mockito.doReturn(dpi).when(mockScreenInfo).getScreenResolution();
+    HiDPISetting.setScreenInfo(mockScreenInfo);
+  }
+}
diff --git a/test/jalview/bin/HiDPISettingTest2.java b/test/jalview/bin/HiDPISettingTest2.java
new file mode 100644 (file)
index 0000000..9851b09
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.bin;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.concurrent.TimeUnit;
+
+import org.testng.Assert;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import io.github.classgraph.ClassGraph;
+import io.github.classgraph.ScanResult;
+import jalview.gui.Desktop;
+
+public class HiDPISettingTest2
+{
+  private static final int TIMEOUT = 10;
+
+  private static class Worker extends Thread
+  {
+    private final Process process;
+
+    private BufferedReader outputReader;
+
+    private BufferedReader errorReader;
+
+    private boolean exited;
+
+    private Worker(Process process)
+    {
+      this.process = process;
+    }
+
+    @Override
+    public void run()
+    {
+      try
+      {
+        exited = process.waitFor(TIMEOUT, TimeUnit.SECONDS);
+      } catch (InterruptedException ignore)
+      {
+        return;
+      }
+      this.interrupt();
+      this.process.destroy();
+    }
+
+    public BufferedReader getOutputReader()
+    {
+      return outputReader;
+    }
+
+    public void setOutputReader(BufferedReader outputReader)
+    {
+      this.outputReader = outputReader;
+    }
+
+    public BufferedReader getErrorReader()
+    {
+      return errorReader;
+    }
+
+    public void setErrorReader(BufferedReader errorReader)
+    {
+      this.errorReader = errorReader;
+    }
+  }
+
+  private static ClassGraph scanner = null;
+
+  private static String classpath = null;
+
+  private static String java_exe = null;
+
+  public synchronized static String getClassPath()
+  {
+    if (scanner == null)
+    {
+      scanner = new ClassGraph();
+      ScanResult scan = scanner.scan();
+      classpath = scan.getClasspath();
+      java_exe = System.getProperty("java.home") + File.separator + "bin"
+              + File.separator + "java";
+
+    }
+    while (classpath == null)
+    {
+      try
+      {
+        Thread.sleep(10);
+      } catch (InterruptedException x)
+      {
+
+      }
+    }
+    return classpath;
+  }
+
+  private Worker getJalviewDesktopRunner(String jvmArgs, String appArgs)
+  {
+    String classpath = getClassPath();
+    String cmd = java_exe + " " + " -classpath " + classpath + " " + jvmArgs
+            + " jalview.bin.Jalview " + " "
+            + "-props test/jalview/bin/hidpiTestProps.jvprops " + appArgs;
+    Process proc = null;
+    Worker worker = null;
+    try
+    {
+      proc = Runtime.getRuntime().exec(cmd);
+    } catch (Throwable e)
+    {
+      e.printStackTrace();
+    }
+    if (proc != null)
+    {
+      BufferedReader outputReader = new BufferedReader(
+              new InputStreamReader(proc.getInputStream()));
+      BufferedReader errorReader = new BufferedReader(
+              new InputStreamReader(proc.getErrorStream()));
+      worker = new Worker(proc);
+      worker.start();
+      worker.setOutputReader(outputReader);
+      worker.setErrorReader(errorReader);
+    }
+    return worker;
+  }
+
+  @BeforeTest(alwaysRun = true)
+  public void initialize()
+  {
+    new HiDPISettingTest2();
+  }
+
+  @Test(groups = { "Functional" }, dataProvider = "hidpiScaleArguments")
+  public void testHiDPISettings(int scale)
+  {
+    String jvmArgs = HiDPISetting.getScalePropertyArg(scale);
+
+    String appArgs = " -open examples/uniref50.fa -nosplash -nonews -noquestionnaire -nousagestats -nowebservicediscovery";
+
+    Worker worker = getJalviewDesktopRunner(jvmArgs, appArgs);
+    assertNotNull(worker, "worker is null");
+
+    String ln = null;
+    int count = 0;
+    boolean scaleFound = false;
+    try
+    {
+      while ((ln = worker.getErrorReader().readLine()) != null)
+      {
+        if (++count > 100)
+        {
+          break;
+        }
+        if (ln.contains(Desktop.debugScaleMessage))
+        {
+          String number = ln.substring(ln.indexOf(Desktop.debugScaleMessage)
+                  + Desktop.debugScaleMessage.length());
+          number = number.substring(0, number.indexOf(' '));
+          try
+          {
+            double d = Double.valueOf(number);
+
+            assertEquals(d, scale * 1.0);
+            scaleFound = true;
+          } catch (NumberFormatException e)
+          {
+            e.printStackTrace();
+            Assert.fail(
+                    "Debug scale message line '" + ln + "' gives number '"
+                            + number + "' which could not be parsed");
+          }
+          break;
+        }
+      }
+    } catch (IOException e)
+    {
+      e.printStackTrace();
+    }
+    if (worker != null && worker.exited == false)
+    {
+      worker.interrupt();
+      worker.process.destroy();
+    }
+    if (!scaleFound)
+    {
+      Assert.fail("Did not find Debug scale message line '"
+              + Desktop.debugScaleMessage + "'");
+    }
+  }
+
+  @DataProvider(name = "hidpiScaleArguments")
+  public static Object[][] getHiDPIScaleArguments()
+  {
+    return new Object[][] { { 1 }, { 2 }, { 4 }, { 10 } };
+  }
+}
diff --git a/test/jalview/bin/hidpiTestProps.jvprops b/test/jalview/bin/hidpiTestProps.jvprops
new file mode 100644 (file)
index 0000000..c640dd1
--- /dev/null
@@ -0,0 +1,15 @@
+#---JalviewX Properties File---
+# HiDPI screen
+SCREENGEOMETRY_WIDTH=3840
+SCREENGEOMETRY_HEIGHT=2160
+SCREEN_WIDTH=3000
+SCREEN_HEIGHT=2000
+USAGESTATS=false
+STARTUP_FILE=examples/uniref50.fa
+SHOW_STARTUP_FILE=false
+FONT_STYLE=plain
+FONT_SIZE=10
+ANTI_ALIAS=false
+SHOW_JAVA_CONSOLE=false
+DAS_REGISTRY_URL=http\://www.ebi.ac.uk/das-srv/registry/das/
+logs.Jalview.level=DEBUG