Merge branch 'improvement/JAL-3830_remove_dock_icon_in_headless_mode' into develop
authorJames Procter <j.procter@dundee.ac.uk>
Mon, 10 Jul 2023 09:12:48 +0000 (10:12 +0100)
committerJames Procter <j.procter@dundee.ac.uk>
Mon, 10 Jul 2023 09:12:48 +0000 (10:12 +0100)
22 files changed:
help/help/html/privacy.html
help/markdown/releases/release-2_11_2_7.md [new file with mode: 0644]
help/markdown/releases/release-2_11_3_0.md
help/markdown/whatsnew/whatsnew-2_11_2_7.md [new file with mode: 0644]
help/markdown/whatsnew/whatsnew-2_11_3_0.md
src/jalview/analysis/AlignSeq.java
src/jalview/analysis/AlignmentUtils.java
src/jalview/analytics/Plausible.java
src/jalview/api/RotatableCanvasI.java
src/jalview/bin/Commands.java
src/jalview/datamodel/DBRefSource.java
src/jalview/datamodel/Sequence.java
src/jalview/ext/jmol/JmolParser.java
src/jalview/gui/AppJmol.java
src/jalview/gui/Desktop.java
src/jalview/gui/ImageExporter.java
src/jalview/gui/PopupMenu.java
src/jalview/ws/sifts/SiftsClient.java
test/jalview/analysis/TestAlignSeq.java
test/jalview/bin/CommandsTest.java
test/jalview/datamodel/SequenceTest.java
test/jalview/ws/sifts/SiftsClientTest.java

index 9ad61b5..8704ce8 100644 (file)
   <p>Usage data is collected from the logs of various web services
     that the Jalview Desktop contacts through its normal operation.
     These are described below:</p>
-  <ul>
-    <li><em>HTTP logs on the Jalview website</em><br> We
-      record IP addresses of machines which access the web site, either
-      via the browser when downloading the application, or when the
-      Jalview Desktop user interface is launched.<br> <br>
-      <ul>
-        <li><i>The Jalview Getdown Launcher</i> (Since 2.11.0) examines release
-          channels every time Jalview launches to determine if a new
-          release is available.</li>
-        <li><i>The questionnaire web service at
-            www.jalview.org/cgi-bin/questionnaire.pl is checked and a
-            unique cookie for the current questionnaire is stored in the
-            Jalview properties file.</i></li>
-        <li><i>The Jalview web services stack is contacted to
-            retrieve the currently available web services. All
-            interactions with the public Jalview web services are
-            logged, but we delete all job data (input data and results)
-            after about two weeks.</i></li>
-      </ul> <br></li>
-    <li><em>Google Analytics</em><br> Since Jalview 2.4.0b2,
-      the Jalview Desktop records usage data with Google Analytics via
-      the <a href="http://code.google.com/p/jgoogleanalytics/">JGoogleAnalytics</a>
-      class.<br> The Google Analytics logs for Jalview version 2.4
-      only record the fact that the application was started, but in the
-      future, we will use this mechanism to improve the Desktop user
-      interface, by tracking which parts of the user interface are being
-      used most often.</li>
-  </ul>
-  </p>
+       <ul>
+               <li><em>HTTP logs on the Jalview website</em><br> We record
+                       IP addresses of machines which access the web site, either via the
+                       browser when downloading the application, or when the Jalview Desktop
+                       user interface is launched.<br> <br>
+                       <ul>
+                               <li><i>The Jalview Getdown Launcher</i> (Since 2.11.0) examines
+                                       release channels every time Jalview launches to determine if a new
+                                       release is available.</li>
+                               <li><i>The questionnaire web service at
+                                               www.jalview.org/cgi-bin/questionnaire.pl is checked and a unique
+                                               cookie for the current questionnaire is stored in the Jalview
+                                               properties file.</i></li>
+                               <li><i>The Jalview web services stack is contacted to
+                                               retrieve the currently available web services. All interactions
+                                               with the public Jalview web services are logged, but we delete all
+                                               job data (input data and results) after about two weeks.</i></li>
+                       </ul> <br></li>
+               <li><em>Usage Analytics</em><br> Since Jalview 2.11.2.7, the
+                       Jalview Desktop records usage data with a self-hosted instance of the
+                       analytics stack <a href="https://plausible.io">Plausible.io</a> via a
+                       custom GPLv3 client developed by Ben Soares. Prior to this, Jalview
+                       versions as far back as 2.4 recorded application launches via <a
+                       href="http://code.google.com/p/jgoogleanalytics/">JGoogleAnalytics</a>
+                       .<br> Usage logs for Jalview record the fact that the
+                       application was started, and details about the OS, installed Jalview
+                       launcher (if any) and java version used. In the future, we will use
+                       this mechanism to improve the Desktop user interface, by tracking
+                       which parts of the user interface are being used most often.</li>
+       </ul>
   <p>
     <strong>Stopping Jalview from calling home</strong><br> If you
     run Jalview in 'headless mode' via the command line, then the
diff --git a/help/markdown/releases/release-2_11_2_7.md b/help/markdown/releases/release-2_11_2_7.md
new file mode 100644 (file)
index 0000000..361d39a
--- /dev/null
@@ -0,0 +1,12 @@
+---
+version: 2.11.2.7
+date: 2023-06-30
+channel: "release"
+---
+
+## New Features
+- <!-- JAL-4001 --> Jalview now reports usage statistics via Plausible.io
+
+## Issues Resolved
+- <!-- JAL-4116 --> PDB structures slow to view when Jalview Java console is open
+- <!-- JAL-4216 --> chains in PDB or mmCIF files with negative RESNUMs not correctly parsed
index 1309483..fbd7b9f 100644 (file)
@@ -1,6 +1,6 @@
 ---
 version: 2.11.3.0
-date: 2023-06-07
+date: 2023-07-19
 channel: "release"
 ---
 
@@ -20,6 +20,9 @@ channel: "release"
 - <!-- JAL-4089 --> Use selected columns for superposition
 - <!-- JAL-4086 --> Highlight aligned positions on all associated structures when mousing over a column
 
+- <!-- JAL-4221 --> sequence descriptions are updated from database reference sources if not already defined
+
+
 ### Improved support for working with computationally determined models
 
 - <!-- JAL-3895 --> Alphafold red/orange/yellow/green colourscheme for structures
@@ -35,6 +38,8 @@ channel: "release"
 - <!-- JAL-3858 --> Import and display alphafold alignment uncertainty matrices from JSON
 - <!-- JAL-4134,JAL-4158 --> Column-wise alignment groups and selections and interactive tree viewer for PAE matrices
 - <!-- JAL-4124 --> Store/Restore PAE data and visualisation settings from Jalview Project
+- <!-- JAL-4083 --> Multiple residue sidechain highlighting in structure viewers from PAE mouseovers
+
 
 ### Jalview on the command line
 
@@ -44,6 +49,21 @@ channel: "release"
 ### Other improvements
 
 - <!-- JAL-3119 --> Name of alignment and view included in overview window's title
+- <!-- JAL-4213 --> "add reference annotation" add all positions in reference annotation tracks, not just positions in the currently highlighted columns/selection range
+- <!-- JAL-4119 --> EMBL-EBI SIFTS file downloads now use split directories
+
+- <!-- JAL-4195,JAL-4194,JAL-4193 --> sensible responses from the CLI when things go wrong during image export
+Add a command line option to set Jalview properties for this session only
+Add a command line option to suppress opening the startup file for this session
+
+
+JAL-4187        Powershell launcher script fails when given no arguments with the old ArgsParser
+
+known issue ? <!-- JAL-4127    --> 'Reload' for a jalview project results in all windows being duplicated
+
+
+- <!-- JAL-3830 --> Command-line wrapper script for macOS bundle, linux and Windows installations (bash, powershell and .bat wrappers)
+- <!-- JAL-3820 --> In Linux desktops' task-managers, the grouped Jalview windows get a generic name
 
 ## Still in progress (delete on release)
 
diff --git a/help/markdown/whatsnew/whatsnew-2_11_2_7.md b/help/markdown/whatsnew/whatsnew-2_11_2_7.md
new file mode 100644 (file)
index 0000000..f884134
--- /dev/null
@@ -0,0 +1,5 @@
+Jalview 2.11.2.7 is a minor patch release - it includes patches affecting efficiency when importing structures and a small revision to the import processing of structures with negative residue numbering. 
+
+With this release, Jalview usage statistics are now collected by a jalview.org hosted instance of the open source privacy-preserving analytics stack, Plausible.io. 
+
+
index 59495d5..ec82f83 100644 (file)
@@ -1,5 +1,7 @@
 The 2.11.3 series includes support for in-depth exploration of predicted alignment error matrices from AlphaFold in the context of multiple alignments, along with support for standard colourschemes for shading models according to their pLDDT.
 
+We're launching this release at ISMB 2023 - come find us !
+
 It also introduces new support for native ARM-based OSX architectures, and a few other goodies!
 
 
index 65fd110..02b3f41 100755 (executable)
@@ -890,7 +890,8 @@ public class AlignSeq
         pdbpos++;
       }
 
-      if (allowmismatch || c1 == c2)
+      // ignore case differences
+      if (allowmismatch || (c1 == c2) || (Math.abs(c2-c1)==('a'-'A')))
       {
         // extend mapping interval
         if (lp1 + 1 != alignpos || lp2 + 1 != pdbpos)
index 1158c53..6ab49b2 100644 (file)
@@ -1512,7 +1512,7 @@ public class AlignmentUtils
    * @param alignment
    *          the alignment to add them to
    * @param selectionGroup
-   *          current selection group (or null if none)
+   *          current selection group - may be null, if provided then any added annotation will be trimmed to just those columns in the selection group
    */
   public static void addReferenceAnnotations(
           Map<SequenceI, List<AlignmentAnnotation>> annotations,
@@ -1536,7 +1536,7 @@ public class AlignmentUtils
    * @param seq
    * @param ann
    * @param selectionGroup
-   *          - may be null
+   *          current selection group - may be null, if provided then any added annotation will be trimmed to just those columns in the selection group
    * @return annotation added to {@code seq and {@code alignment}
    */
   public static AlignmentAnnotation addReferenceAnnotationTo(
index 945b53b..ab2de77 100644 (file)
@@ -1,3 +1,23 @@
+/*
+ * 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.analytics;
 
 import java.io.BufferedReader;
@@ -19,6 +39,7 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Random;
 
 import jalview.bin.Cache;
 import jalview.bin.Console;
@@ -27,19 +48,20 @@ import jalview.util.HttpUtils;
 
 public class Plausible
 {
-  private static final String USER_AGENT = HttpUtils.getUserAgent(
-          MethodHandles.lookup().lookupClass().getCanonicalName());
+  private static final String USER_AGENT;
 
   private static final String JALVIEW_ID = "Jalview Desktop";
 
   private static final String DOMAIN = "jalview.org";
 
-  private static final String CONFIG_API_BASE_URL = "https://www.jalview.org/config/analytics/url";
+  private static final String CONFIG_API_BASE_URL = "https://www.jalview.org/services/config/analytics/url";
 
-  private static final String DEFAULT_API_BASE_URL = "https://plausible.io/api/event";
+  private static final String DEFAULT_API_BASE_URL = "https://analytics.jalview.org/api/event";
 
   private static final String API_BASE_URL;
 
+  private static final String clientId;
+
   public static final String APPLICATION_BASE_URL = "desktop://localhost";
 
   private List<Map.Entry<String, String>> queryStringValues;
@@ -96,6 +118,13 @@ public class Plausible
 
     // ascertain the API_BASE_URL
     API_BASE_URL = getAPIBaseURL();
+
+    // random clientId to make User-Agent unique (to register analytic)
+    clientId = String.format("%08x", new Random().nextInt());
+
+    USER_AGENT = HttpUtils.getUserAgent(
+            MethodHandles.lookup().lookupClass().getCanonicalName() + " "
+                    + clientId);
   }
 
   private Plausible()
@@ -570,4 +599,4 @@ public class Plausible
     }
     return DEFAULT_API_BASE_URL;
   }
-}
\ No newline at end of file
+}
index c6eb6de..1646d89 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2b1)
- * Copyright (C) 2014 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
index 7f493e0..d7d1ea3 100644 (file)
@@ -574,17 +574,28 @@ public class Commands
             Console.error("Failed to import and open structure view.");
             continue;
           }
-          while (sv.isBusy())
+          try
           {
-            try {
+            long tries=1000;
+            while (sv.isBusy() && tries>0)
+            {
               Thread.sleep(25);
+              if (sv.isBusy())
+              {
+                tries--;
+                Console.debug(
+                        "Waiting for viewer for " + structureFilepath);
+              }
             }
-            catch (Exception x)
+            if (tries==0 && sv.isBusy())
             {
-              
+              Console.warn("Gave up waiting for structure viewer to load. Something may have gone wrong.");
             }
+          } catch (Exception x)
+          {
+            Console.warn("Exception whilst waiting for structure viewer "+structureFilepath,x);
           }
-
+          Console.debug("Successfully opened viewer for "+structureFilepath);
           String structureImageFilename = ArgParser.getValueFromSubValOrArg(
                   avm, av, Arg.STRUCTUREIMAGE, subVals);
           if (sv != null && structureImageFilename != null)
@@ -624,12 +635,13 @@ public class Commands
             }
             BitmapImageSizing userBis = ImageMaker
                     .parseScaleWidthHeightStrings(scale, width, height);
+            // TODO MAKE THIS VIEWER INDEPENDENT!!
             switch (StructureViewer.getViewerType())
             {
             case JMOL:
               try
               {
-                Thread.sleep(1000);
+                Thread.sleep(1000); // WHY ???
               } catch (InterruptedException e)
               {
                 // TODO Auto-generated catch block
@@ -641,8 +653,11 @@ public class Commands
               {
                 AppJmol jmol = (AppJmol) sview;
                 try { 
+                  Console.debug("Rendering image to "+structureImageFile);
                   jmol.makePDBImage(structureImageFile, imageType, renderer,
                         userBis);
+                  Console.debug("Finished Rendering image to "+structureImageFile);
+
                 }
                 catch (ImageOutputException ioexc)
                 {
index f384b1e..3b1757b 100755 (executable)
@@ -154,6 +154,11 @@ public class DBRefSource
 
   public static boolean isPrimaryCandidate(String ucversion)
   {
+    if (ucversion==null)
+    {
+      // Null/empty version is not a real reference ?
+      return false;
+    }
     // tricky - this test really needs to search the sequence's set of dbrefs to
     // see if there is a primary reference that derived this reference.
     for (int i = allSources.length; --i >= 0;)
index 32d295a..5bb55e5 100755 (executable)
@@ -1754,6 +1754,13 @@ public class Sequence extends ASequence implements SequenceI
       transferAnnotation(entry.getDatasetSequence(), mp);
       return;
     }
+    // transfer from entry to sequence
+    // if entry has a description and sequence doesn't, then transfer
+    if (entry.getDescription()!=null && (description==null || description.trim().length()==0))
+    {
+      description = entry.getDescription();
+    }
+    
     // transfer any new features from entry onto sequence
     if (entry.getSequenceFeatures() != null)
     {
index c64dac1..57b406e 100644 (file)
@@ -488,7 +488,7 @@ public class JmolParser extends StructureFile implements JmolStatusListener
   {
     int length = sq.getLength();
     boolean ssFound = false;
-    Annotation asecstr[] = new Annotation[length + firstResNum - 1];
+    Annotation asecstr[] = new Annotation[length + (firstResNum-sq.getStart())];
     for (int p = 0; p < length; p++)
     {
       if (secstr[p] >= 'A' && secstr[p] <= 'z')
index 1758b5b..49eae98 100644 (file)
@@ -28,9 +28,16 @@ import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.RenderingHints;
 import java.io.File;
+import java.lang.reflect.InvocationTargetException;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
 
 import javax.swing.JPanel;
 import javax.swing.JSplitPane;
@@ -472,11 +479,48 @@ public class AppJmol extends StructureViewerBase
     };
     String view = MessageManager.getString("action.view")
             .toLowerCase(Locale.ROOT);
-    ImageExporter exporter = new ImageExporter(writer,
+    final ImageExporter exporter = new ImageExporter(writer,
             getProgressIndicator(), type, getTitle());
     
-    exporter.doExport(file, this, width, height, view, renderer, userBis);
-    
+    final Throwable[] exceptions = new Throwable[1];
+    exceptions[0] = null;
+    final AppJmol us = this;
+    try
+    {
+      Thread runner = Executors.defaultThreadFactory().newThread(new Runnable()
+      {
+        @Override
+        public void run()
+        {
+          try
+          {
+            exporter.doExport(file, us, width, height, view, renderer,
+                    userBis);
+          } catch (Throwable t)
+          {
+            exceptions[0] = t;
+          }
+        }
+      });
+      runner.start();
+      do { Thread.sleep(25); } while (runner.isAlive());
+    } catch (Throwable e)
+    {
+      throw new ImageOutputException(
+              "Unexpected error when generating image", e);
+    }
+    if (exceptions[0] != null)
+    {
+      if (exceptions[0] instanceof ImageOutputException)
+      {
+        throw ((ImageOutputException) exceptions[0]);
+      }
+      else
+      {
+        throw new ImageOutputException(
+                "Unexpected error when generating image", exceptions[0]);
+      }
+    }
   }
 
   @Override
index b901ae4..12ff20b 100644 (file)
@@ -1078,7 +1078,36 @@ public class Desktop extends jalview.jbgui.GDesktop
 
     setKeyBindings(frame);
 
-    desktop.add(frame);
+    // Since the latest FlatLaf patch, we occasionally have problems showing structureViewer frames...
+    int tries=3;
+    boolean shown=false;
+    Exception last=null;
+    do
+    {
+      try
+      {
+        desktop.add(frame);
+        shown=true;
+      } catch (IllegalArgumentException iaex)
+      {
+        last=iaex;
+        tries--;
+        jalview.bin.Console.info(
+                "Squashed IllegalArgument Exception (" + tries + " left) for "+frame.getTitle(),
+                iaex);
+        try
+        {
+          Thread.sleep(5);
+        } catch (InterruptedException iex)
+        {
+        }
+        ;
+      }
+    } while (!shown && tries > 0);
+    if (!shown)
+    {
+      jalview.bin.Console.error("Serious Problem whilst showing window "+frame.getTitle(),last);
+    }
 
     windowMenu.add(menuItem);
 
@@ -3623,8 +3652,22 @@ public class Desktop extends jalview.jbgui.GDesktop
     {
       Desktop.instance.closeAll_actionPerformed(null);
       Desktop.instance.setVisible(false);
-      Desktop.instance.dispose();
+      Desktop us = Desktop.instance;
       Desktop.instance = null;
+      // call dispose in a separate thread - try to avoid indirect deadlocks
+      new Thread(new Runnable() {
+        @Override
+        public void run()
+        {
+          ExecutorService dex = us.dialogExecutor;
+          if (dex!=null) {
+            dex.shutdownNow();
+            us.dialogExecutor=null;
+            us.block.drainPermits();
+          }
+          us.dispose();
+        }
+      }).start();
     }
   }
 
index f337b39..4ea30d9 100644 (file)
@@ -124,6 +124,11 @@ public class ImageExporter
      */
     if (file == null && !Jalview.isHeadlessMode())
     {
+      if (Desktop.instance.isInBatchMode())
+      {
+        // defensive error report - we could wait for user input.. I  guess ?
+        throw(new ImageOutputException("Need an output file to render to when exporting images in batch mode!"));
+      }
       JalviewFileChooser chooser = imageType.getFileChooser();
       chooser.setFileView(new JalviewFileView());
       MessageManager.formatMessage("label.create_image_of",
index be9293f..88c1292 100644 (file)
@@ -1734,10 +1734,9 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
   protected void addReferenceAnnotations_actionPerformed(
           Map<SequenceI, List<AlignmentAnnotation>> candidates)
   {
-    final SequenceGroup selectionGroup = this.ap.av.getSelectionGroup();
     final AlignmentI alignment = this.ap.getAlignment();
     AlignmentUtils.addReferenceAnnotations(candidates, alignment,
-            selectionGroup);
+            null);
     refresh();
   }
 
index 0c707e5..037854b 100644 (file)
@@ -116,7 +116,7 @@ public class SiftsClient implements SiftsClientI
 
   private static final String NOT_OBSERVED = "Not_Observed";
 
-  private static final String SIFTS_FTP_BASE_URL = "http://ftp.ebi.ac.uk/pub/databases/msd/sifts/xml/";
+  private static final String SIFTS_SPLIT_FTP_BASE_URL = "https://ftp.ebi.ac.uk/pub/databases/msd/sifts/split_xml/";
 
   private final static String NEWLINE = System.lineSeparator();
 
@@ -305,7 +305,7 @@ public class SiftsClient implements SiftsClientI
       pdbId = pdbId.replace(".cif", "");
     }
     String siftFile = pdbId + ".xml.gz";
-    String siftsFileFTPURL = SIFTS_FTP_BASE_URL + siftFile;
+    String siftsFileFTPURL = getDownloadUrlFor(siftFile);
 
     /*
      * Download the file from URL to either
@@ -348,6 +348,11 @@ public class SiftsClient implements SiftsClientI
     return downloadTo;
   }
 
+  public static String getDownloadUrlFor(String siftFile)
+  {
+    return SIFTS_SPLIT_FTP_BASE_URL +siftFile.substring(1, 3)+"/"+siftFile;
+  }
+
   /**
    * Delete the SIFTs file for the given PDB Id in the local SIFTs download
    * directory
index 5134511..44bea81 100644 (file)
@@ -29,11 +29,15 @@ import jalview.datamodel.SequenceI;
 import jalview.gui.JvOptionPane;
 
 import java.io.PrintStream;
+import java.nio.charset.Charset;
+import java.util.Locale;
 
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.google.common.base.Charsets;
+
 /**
  * Test the alignment -> Mapping routines
  * 
@@ -83,16 +87,44 @@ public class TestAlignSeq
     assertEquals(as.getAStr1(), as.getAStr2());
 
     Mapping s1tos2 = as.getMappingFromS1(false);
+    checkMapping(s1tos2,s1,s2);
+  }
+
+  public void checkMapping(Mapping s1tos2,SequenceI _s1,SequenceI _s2)
+  {
     System.out.println(s1tos2.getMap().toString());
-    for (int i = s2.getStart(); i < s2.getEnd(); i++)
+    for (int i = _s2.getStart(); i < _s2.getEnd(); i++)
     {
-      System.out.println("Position in s2: " + i
-              + " maps to position in s1: " + s1tos2.getPosition(i));
-      // TODO fails: getCharAt doesn't allow for the start position??
-      // assertEquals(String.valueOf(s2.getCharAt(i)),
-      // String.valueOf(s1.getCharAt(s1tos2.getPosition(i))));
+      int p=s1tos2.getPosition(i);
+      char s2c=_s2.getCharAt(i-_s2.getStart());
+      char s1c=_s1.getCharAt(p-_s1.getStart());
+      System.out.println("Position in s2: " + i +s2c 
+      + " maps to position in s1: " +p+s1c);
+      assertEquals(s1c,s2c);
     }
   }
+  @Test(groups = { "Functional" })
+  /**
+   * simple test that mapping from alignment corresponds identical positions.
+   */
+  public void testGetMappingForS1_withLowerCase()
+  {
+    // make one of the sequences lower case
+    SequenceI ns2 = new Sequence(s2);
+    ns2.replace('D', 'd');
+    AlignSeq as = AlignSeq.doGlobalNWAlignment(s1, ns2, AlignSeq.PEP);
+    System.out.println("s1: " + as.getAStr1());
+    System.out.println("s2: " + as.getAStr2());
+
+    // aligned results match
+    assertEquals("ASDFA", as.getAStr1());
+    assertEquals(as.getAStr1(), as.getAStr2().toUpperCase(Locale.ROOT));
+
+    Mapping s1tos2 = as.getMappingFromS1(false);
+    assertEquals("ASdFA",as.getAStr2());
+    // verify mapping is consistent between original all-caps sequences
+    checkMapping(s1tos2,s1,s2);
+  }
 
   @Test(groups = { "Functional" })
   public void testExtractGaps()
index 61892df..81f0e1f 100644 (file)
@@ -19,7 +19,6 @@ import jalview.gui.Desktop;
 import jalview.gui.JvOptionPane;
 import jalview.util.ArrayUtils;
 
-@Test
 public class CommandsTest
 {
   private static final String testfiles = "test/jalview/bin/argparser/testfiles";
@@ -77,39 +76,49 @@ public class CommandsTest
   public void commandsOpenTest(String cmdLine, boolean cmdArgs,
           int numFrames, String[] sequences)
   {
-    String[] args = (cmdLine + " --gui").split("\\s+");
-    callJalviewMain(args);
-    Commands cmds = Jalview.getInstance().getCommands();
-    Assert.assertNotNull(cmds);
-    Assert.assertEquals(cmds.commandArgsProvided(), cmdArgs,
-            "Commands were not provided in the args");
-    Assert.assertEquals(cmds.argsWereParsed(), cmdArgs,
-            "Overall command parse and operation is false");
+    try
+    {
+      String[] args = (cmdLine + " --gui").split("\\s+");
+      callJalviewMain(args);
+      Commands cmds = Jalview.getInstance().getCommands();
+      Assert.assertNotNull(cmds);
+      Assert.assertEquals(cmds.commandArgsProvided(), cmdArgs,
+              "Commands were not provided in the args");
+      Assert.assertEquals(cmds.argsWereParsed(), cmdArgs,
+              "Overall command parse and operation is false");
 
-    Assert.assertEquals(Desktop.getAlignFrames().length, numFrames,
-            "Wrong number of AlignFrames");
+      Assert.assertEquals(Desktop.getAlignFrames().length, numFrames,
+              "Wrong number of AlignFrames");
 
-    if (sequences != null)
-    {
-      Set<String> openedSequenceNames = new HashSet<>();
-      AlignFrame[] afs = Desktop.getAlignFrames();
-      for (AlignFrame af : afs)
+      if (sequences != null)
       {
-        openedSequenceNames
-                .addAll(af.getViewport().getAlignment().getSequenceNames());
-      }
-      for (String sequence : sequences)
-      {
-        Assert.assertTrue(openedSequenceNames.contains(sequence),
-                "Sequence '" + sequence
-                        + "' was not found in opened alignment files: "
-                        + cmdLine + ".\nOpened sequence names are:\n"
-                        + String.join("\n", openedSequenceNames));
+        Set<String> openedSequenceNames = new HashSet<>();
+        AlignFrame[] afs = Desktop.getAlignFrames();
+        for (AlignFrame af : afs)
+        {
+          openedSequenceNames.addAll(
+                  af.getViewport().getAlignment().getSequenceNames());
+        }
+        for (String sequence : sequences)
+        {
+          Assert.assertTrue(openedSequenceNames.contains(sequence),
+                  "Sequence '" + sequence
+                          + "' was not found in opened alignment files: "
+                          + cmdLine + ".\nOpened sequence names are:\n"
+                          + String.join("\n", openedSequenceNames));
+        }
       }
-    }
 
-    Assert.assertFalse(
-            lookForSequenceName("THIS_SEQUENCE_ID_DOESN'T_EXIST"));
+      Assert.assertFalse(
+              lookForSequenceName("THIS_SEQUENCE_ID_DOESN'T_EXIST"));
+    } catch (Exception x)
+    {
+      Assert.fail("Unexpected exception during commandsOpenTest", x);
+    } finally
+    {
+      tearDown();
+
+    }
   }
 
   @Test(groups = "Functional", dataProvider = "structureImageOutputFiles")
@@ -118,26 +127,35 @@ public class CommandsTest
   {
     cleanupFiles(filenames);
     String[] args = (cmdLine + " --gui").split("\\s+");
-    callJalviewMain(args);
-    Commands cmds = Jalview.getInstance().getCommands();
-    Assert.assertNotNull(cmds);
-    File lastFile = null;
-    for (String filename : filenames)
+    try
     {
-      File file = new File(filename);
-      Assert.assertTrue(file.exists(), "File '" + filename
-              + "' was not created by '" + cmdLine + "'");
-      Assert.assertTrue(file.isFile(), "File '" + filename
-              + "' is not a file from '" + cmdLine + "'");
-      Assert.assertTrue(Files.size(file.toPath()) > 0, "File '" + filename
-              + "' has no content from '" + cmdLine + "'");
-      // make sure the successive output files get bigger!
-      if (lastFile != null)
-        Assert.assertTrue(
-                Files.size(file.toPath()) > Files.size(lastFile.toPath()));
+      callJalviewMain(args);
+      Commands cmds = Jalview.getInstance().getCommands();
+      Assert.assertNotNull(cmds);
+      File lastFile = null;
+      for (String filename : filenames)
+      {
+        File file = new File(filename);
+        Assert.assertTrue(file.exists(), "File '" + filename
+                + "' was not created by '" + cmdLine + "'");
+        Assert.assertTrue(file.isFile(), "File '" + filename
+                + "' is not a file from '" + cmdLine + "'");
+        Assert.assertTrue(Files.size(file.toPath()) > 0, "File '" + filename
+                + "' has no content from '" + cmdLine + "'");
+        // make sure the successive output files get bigger!
+        if (lastFile != null)
+          Assert.assertTrue(Files.size(file.toPath()) > Files
+                  .size(lastFile.toPath()));
+      }
+    } catch (Exception x)
+    {
+      Assert.fail("Unexpected exception during structureImageOutputTest",
+              x);
+    } finally
+    {
+      cleanupFiles(filenames);
+      tearDown();
     }
-    cleanupFiles(filenames);
-    tearDown();
   }
 
   @Test(groups = "Functional", dataProvider = "argfileOutputFiles")
@@ -146,6 +164,7 @@ public class CommandsTest
   {
     cleanupFiles(filenames);
     String[] args = (cmdLine + " --gui").split("\\s+");
+    try {
     callJalviewMain(args);
     Commands cmds = Jalview.getInstance().getCommands();
     Assert.assertNotNull(cmds);
@@ -164,8 +183,15 @@ public class CommandsTest
         Assert.assertTrue(
                 Files.size(file.toPath()) > Files.size(lastFile.toPath()));
     }
-    cleanupFiles(filenames);
-    tearDown();
+    } catch (Exception x)
+    {
+      Assert.fail("Unexpected exception during argFilesGlobAndSubstitutions",
+              x);
+    } finally
+    {
+      cleanupFiles(filenames);
+      tearDown();
+    }
   }
 
   @DataProvider(name = "structureImageOutputFiles")
index 344d74d..7bbb9f3 100644 (file)
@@ -2315,8 +2315,16 @@ public class SequenceTest
   {
     Sequence origSeq = new Sequence("MYSEQ", "THISISASEQ");
     Sequence toSeq = new Sequence("MYSEQ", "THISISASEQ");
+    origSeq.setDescription("DESCRIPTION");
     origSeq.addDBRef(new DBRefEntry("UNIPROT", "0", "Q12345", null, true));
+
+    toSeq.transferAnnotation(origSeq, null);
+    assertEquals("DESCRIPTION",toSeq.getDescription());
+    toSeq = new Sequence("MYSEQ", "THISISASEQ");
+    toSeq.setDescription("unchanged");
     toSeq.transferAnnotation(origSeq, null);
+    assertEquals("unchanged",toSeq.getDescription());
+    
     assertTrue(toSeq.getDBRefs().size() == 1);
 
     assertTrue(toSeq.getDBRefs().get(0).isCanonical());
index 44a6a02..0f5bd4d 100644 (file)
@@ -195,8 +195,10 @@ public class SiftsClientTest
     SiftsSettings.setCacheThresholdInDays("2");
     SiftsSettings.setFailSafePIDThreshold("70");
     PDBfile pdbFile;
+    
     pdbFile = new PDBfile(false, false, false,
             "test/jalview/io/" + testPDBId + ".pdb", DataSourceType.FILE);
+    // TODO: this uses a network connection - we should mock the sifts testPDBId.xml.gz
     siftsClient = new SiftsClient(pdbFile);
   }
 
@@ -205,6 +207,12 @@ public class SiftsClientTest
   {
     siftsClient = null;
   }
+  
+  @Test(groups= {"Functional"})
+  public void testSIFTsDownloadURL() {
+    String expectedUrl = "https://ftp.ebi.ac.uk/pub/databases/msd/sifts/split_xml/xy/1xyz.sifts.xml.gz";
+    Assert.assertEquals(SiftsClient.getDownloadUrlFor("1xyz.sifts.xml.gz"), expectedUrl);
+  }
 
   @Test(groups = { "Network" })
   public void getSIFTsFileTest() throws SiftsException, IOException
@@ -215,7 +223,7 @@ public class SiftsClientTest
     long t1 = siftsFile.lastModified();
 
     // re-read file should be returned from cache
-    siftsFile = SiftsClient.downloadSiftsFile(testPDBId);
+    siftsFile = SiftsClient.getSiftsFile(testPDBId);
     FileAssert.assertFile(siftsFile);
     long t2 = siftsFile.lastModified();
     assertEquals(t1, t2);
@@ -368,7 +376,7 @@ public class SiftsClientTest
   {
     SequenceI invalidTestSeq = new Sequence("testSeq", "ABCDEFGH");
     DBRefEntry invalidDBRef = new DBRefEntry();
-    invalidDBRef.setAccessionId("BLAR");
+    invalidDBRef.setAccessionId("BLAR"); // note no version is set, so also invalid
     invalidTestSeq.addDBRef(invalidDBRef);
     siftsClient.getValidSourceDBRef(invalidTestSeq);
   }