/*************************************************************************
* *
* This file is part of the 20n/act project. *
* 20n/act enables DNA prediction for synthetic biology/bioengineering. *
* Copyright (C) 2017 20n Labs, Inc. *
* *
* Please direct all queries to act@20n.com. *
* *
* This program 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. *
* *
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>. *
* *
*************************************************************************/
package com.act.lcms.v2;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class MassChargeCalculatorTest {
private static final double MASS_ERROR_TOLERANCE = 0.0000001;
// Use the fuzzy FP assertEquals over lists of values
private static void assertEqualsWithFPErr(String msg, List<Double> expected, List<Double> actual) {
if (expected.size() != actual.size()) {
fail(msg + ": unequal list sizes");
}
for (int i = 0; i < expected.size(); i++) {
assertEquals(msg, expected.get(i), actual.get(i), MASS_ERROR_TOLERANCE);
}
}
private static void assertEqualsWithFPErr(String msg, Set<Double> expected, Set<Double> actual) {
List<Double> expectedList = new ArrayList<>(expected);
Collections.sort(expectedList);
List<Double> actualList = new ArrayList<>(actual);
Collections.sort(actualList);
assertEqualsWithFPErr(msg, expectedList, actualList);
}
private static <T> void assertEqualsPairWithFPErr(
String msg, List<Pair<T, Double>> expected, List<Pair<T, Double>> actual) {
if (expected.size() != actual.size()) {
fail(msg + ": unequal list sizes");
}
for (int i = 0; i < expected.size(); i++) {
assertEquals(msg, expected.get(i).getLeft(), actual.get(i).getLeft());
assertEquals(msg, expected.get(i).getRight(), actual.get(i).getRight(), MASS_ERROR_TOLERANCE);
}
}
@Test
public void testMakeMassChargeMap() throws Exception {
List<MassChargeCalculator.MZSource> sources = Arrays.asList(
new MassChargeCalculator.MZSource("InChI=1S/C7H7NO2/c8-6-3-1-5(2-4-6)7(9)10/h1-4H,8H2,(H,9,10)"), // PABA
new MassChargeCalculator.MZSource("InChI=1S/C7H7NO2/c1-10-7(9)6-3-2-4-8-5-6/h2-5H,1H3")); // Something crazy.
MassChargeCalculator.MassChargeMap massChargeMap = MassChargeCalculator.makeMassChargeMap(
sources,
new HashSet<>(Arrays.asList("M+H", "M+Na"))
);
Double expectedMonoMass = 137.047679;
List<Double> expectedIonMZs = Arrays.asList(138.054979, 160.036879); // M+H and M+Na of PABA
// This is package private, but we'll use its ordering to speed testing.
List<Double> actualIonMasses = massChargeMap.ionMZsSorted();
assertEqualsWithFPErr("Unique ionic masses match expected list",
expectedIonMZs,
actualIonMasses
);
assertEqualsPairWithFPErr("M+H ion mz maps to source mass and ion name",
Arrays.asList(Pair.of("M+H", expectedMonoMass)),
massChargeMap.ionMZtoMonoMassAndIonName(actualIonMasses.get(0))
);
assertEqualsPairWithFPErr("M+Na ion mz maps to source mass and ion name",
Arrays.asList(Pair.of("M+Na", expectedMonoMass)),
massChargeMap.ionMZtoMonoMassAndIonName(actualIonMasses.get(1))
);
// Test reverse mapping.
for (Double ionMZ : actualIonMasses) {
assertEquals(String.format("Ionic masses for %f map to two MZSources", ionMZ),
new HashSet<>(sources),
massChargeMap.ionMZToMZSources(ionMZ)
);
}
// Test iterators cover all values.
assertEqualsWithFPErr("Iterable ionMZs match expected",
new HashSet<>(expectedIonMZs),
StreamSupport.stream(massChargeMap.ionMZIter().spliterator(), false).collect(Collectors.toSet())
);
assertEqualsWithFPErr("Iterable monoisotopic masses match expected",
Collections.singleton(expectedMonoMass),
StreamSupport.stream(massChargeMap.monoisotopicMassIter().spliterator(), false).collect(Collectors.toSet())
);
assertEquals("Iterable mzSources match expected",
new HashSet<>(sources),
StreamSupport.stream(massChargeMap.mzSourceIter().spliterator(), false).collect(Collectors.toSet())
);
}
@Test
public void testComputeMass() throws Exception {
List<Pair<MassChargeCalculator.MZSource, Double>> testCases = Arrays.asList(
Pair.of(new MassChargeCalculator.MZSource("InChI=1S/C8H9NO2/c1-6(10)9-7-2-4-8(11)5-3-7/h2-5,11H,1H3,(H,9,10)"),
151.063329),
Pair.of(new MassChargeCalculator.MZSource(151.063329), 151.063329),
Pair.of(new MassChargeCalculator.MZSource(Pair.of("APAP", 151.063329)), 151.063329)
);
for (Pair<MassChargeCalculator.MZSource, Double> testCase : testCases) {
Double actualMass = MassChargeCalculator.computeMass(testCase.getLeft());
assertEquals(
String.format("(Case %d) Actual mass is within bounds: %.6f ~ %.6f",
testCase.getLeft().getId(), testCase.getRight(), actualMass),
testCase.getRight(), actualMass, MASS_ERROR_TOLERANCE
);
}
}
}