Skip to content

Survive a missing UniProt proteome catalogue at startup - #2700

Open
trishorts wants to merge 2 commits into
smith-chem-wisc:masterfrom
trishorts:fix/guard-available-proteomes-load
Open

Survive a missing UniProt proteome catalogue at startup#2700
trishorts wants to merge 2 commits into
smith-chem-wisc:masterfrom
trishorts:fix/guard-available-proteomes-load

Conversation

@trishorts

Copy link
Copy Markdown
Contributor

Companion to smith-chem-wisc/mzLib#1126, and useful on its own.

The problem

GlobalVariables.LoadAvailableProteomes read the proteome catalogue with no guard:

AvailableUniProtProteomes = ProteinDbRetriever.UniprotProteomesList(
    Path.Combine(DataDir, @"Proteomes", @"availableUniProtProteomes.txt.gz"));

SetUpGlobalVariables is called from the MainWindow constructor and from Program.Main outside any try/catch, so anything that stops that file being read takes the whole program down at launch rather than costing one GUI feature.

That was survivable while mzLib answered a missing file with null. It is not once it throws — which is what mzLib#1126 does, stopping ProteinDbRetriever from reporting every failure (bad ID, unsupported format, UniProt outage, missing file) as the same null.

The file is shipped beside the executable, but DataDir does not always resolve there:

  • a Program Files install resolves it to %LOCALAPPDATA%\MetaMorpheus, which an upgrade can leave incomplete;
  • --customDataDir pointed at a folder that already exists skips CopyFilesRecursively entirely (SetUpDataDirectory only copies when the directory is absent), so Proteomes\ is simply not there.

The fix

The catalogue is a convenience for the "Download UniProt Database" window, not a prerequisite for searching. Failing to read it now degrades to an empty dictionary and a console message.

Empty rather than null is deliberate, and fixes a second latent bug. DownloadUniProtDatabaseWindow enumerates this property and calls FirstOrDefault on it with no null check:

foreach (var item in GlobalVariables.AvailableUniProtProteomes)   // :44
GlobalVariables.AvailableUniProtProteomes.FirstOrDefault(...)     // :145

so the old null was already a NullReferenceException waiting for someone to open that window.

The catch is deliberately broad: absent, wrong extension, truncated, locked and unreadable all have the same consequence for an optional catalogue, and none is worth refusing to start over.

Merge order

This is forward- and backward-compatible — it handles both the current null and the post-#1126 throw — so it can merge before or after the mzLib release, and before the version bump. Merging it first is safest.

Tests

TestStartUpSurvivesAMissingProteomeCatalogue hides the Proteomes directory, asserts SetUpGlobalVariables() does not throw, and asserts the property is non-null and empty. Verified it fails without the guard (Expected: not null / But was: null) and passes with it. TestProteomeCatalogueIsReadWhenPresent covers the normal path. All 9 tests in the fixture pass.

LoadAvailableProteomes read the proteome catalogue with no guard, and
SetUpGlobalVariables runs in the MainWindow constructor and in Program.Main
outside any try/catch. So anything that stopped the file being read took
the whole program down at launch rather than costing one GUI feature.

That was survivable while mzLib answered a missing file with null; it is
not once it throws (smith-chem-wisc/mzLib#1126, which stops
ProteinDbRetriever reporting every failure as null). The file is shipped
beside the executable, but DataDir does not always resolve there: a Program
Files install uses %LOCALAPPDATA%\MetaMorpheus, and --customDataDir pointed
at a folder that already exists skips CopyFilesRecursively entirely, so in
both cases Proteomes\ can simply be absent.

The catalogue is a convenience for the "Download UniProt Database" window,
not a prerequisite for searching, so failing to read it now degrades to an
empty dictionary and a message on the console.

Empty rather than null is deliberate: DownloadUniProtDatabaseWindow
enumerates AvailableUniProtProteomes and calls FirstOrDefault on it with no
null check, so the old null was already a latent NullReferenceException the
moment that window was opened. This fixes that too.

The catch is deliberately broad — absent, wrong extension, truncated,
locked, unreadable all have the same consequence for an optional catalogue,
and none of them is worth refusing to start over.

Tested by hiding the catalogue and asserting startup still completes with a
non-null empty dictionary; verified the test fails without the guard.
@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.88889% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 93.28%. Comparing base (7fb8cdb) to head (86be802).

Files with missing lines Patch % Lines
MetaMorpheus/EngineLayer/GlobalVariables.cs 88.88% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2700      +/-   ##
==========================================
- Coverage   93.28%   93.28%   -0.01%     
==========================================
  Files         214      214              
  Lines       21795    21803       +8     
  Branches     4078     4079       +1     
==========================================
+ Hits        20331    20338       +7     
  Misses        910      910              
- Partials      554      555       +1     
Flag Coverage Δ
unittests 93.28% <88.88%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
MetaMorpheus/EngineLayer/GlobalVariables.cs 91.22% <88.88%> (-0.10%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant