public void TestGetScanCountsByScanType(
            string rawFileName,
            int scanStart,
            int scanEnd,
            int expectedMS1,
            int expectedMS2,
            int expectedTotalScanCount)
        {
            // Keys in this Dictionary are filename, values are ScanCounts by collision mode, where the key is a Tuple of ScanType and FilterString
            var expectedData = new Dictionary<string, Dictionary<Tuple<string, string>, int>>();

            // Keys in this dictionary are scan type, values are a Dictionary of FilterString and the number of scans with that filter string
            AddExpectedTupleAndCount(expectedData, "B5_50uM_MS_r1", "Q1MS", "+ c NSI Q1MS", 20);

            AddExpectedTupleAndCount(expectedData, "MNSLTFKK_ms", "Q1MS", "+ p NSI Q1MS", 88);

            AddExpectedTupleAndCount(expectedData, "QCShew200uL", "Q3MS", "+ c NSI Q3MS", 101);

            AddExpectedTupleAndCount(expectedData, "Wrighton_MT2_SPE_200avg_240k_neg_330-380", "SIM ms", "FTMS - p NSI SIM ms", 200);

            const string file5 = "1229_02blk1";
            AddExpectedTupleAndCount(expectedData, file5, "MS", "ITMS + c NSI Full ms", 8);
            AddExpectedTupleAndCount(expectedData, file5, "CID-MSn", "ITMS + c NSI d Full ms2 [email protected]", 24);
            AddExpectedTupleAndCount(expectedData, file5, "SIM ms", "ITMS + p NSI SIM ms", 69);

            const string file6 = "MCF7_histone_32_49B_400min_HCD_ETD_01172014_b";
            AddExpectedTupleAndCount(expectedData, file6, "HMS", "FTMS + p NSI Full ms", 9);
            AddExpectedTupleAndCount(expectedData, file6, "ETD-HMSn", "FTMS + p NSI d Full ms2 [email protected]", 46);
            AddExpectedTupleAndCount(expectedData, file6, "HCD-HMSn", "FTMS + p NSI d Full ms2 [email protected]", 37);
            AddExpectedTupleAndCount(expectedData, file6, "SIM ms", "FTMS + p NSI d SIM ms", 9);

            const string file7 = "lowdose_IMAC_iTRAQ1_PQDMSA";
            AddExpectedTupleAndCount(expectedData, file7, "HMS", "FTMS + p NSI Full ms", 16);
            AddExpectedTupleAndCount(expectedData, file7, "CID-MSn", "ITMS + c NSI d Full ms2 [email protected]", 43);
            AddExpectedTupleAndCount(expectedData, file7, "PQD-MSn", "ITMS + c NSI d Full ms2 [email protected]", 42);

            const string file8 = "MZ20150721blank2";
            AddExpectedTupleAndCount(expectedData, file8, "HMS", "FTMS + p NSI Full ms", 62);
            AddExpectedTupleAndCount(expectedData, file8, "ETD-HMSn", "FTMS + p NSI d Full ms2 [email protected]", 186);
            AddExpectedTupleAndCount(expectedData, file8, "ETD-HMSn", "FTMS + p NSI d Full ms2 [email protected]", 186);

            const string file9 = "OG_CEPC_PU_22Oct13_Legolas_13-05-12";
            AddExpectedTupleAndCount(expectedData, file9, "HMS", "FTMS + p NSI Full ms", 9);
            AddExpectedTupleAndCount(expectedData, file9, "CID-MSn", "ITMS + c NSI d Full ms2 [email protected]", 46);
            AddExpectedTupleAndCount(expectedData, file9, "ETD-MSn", "ITMS + c NSI d Full ms2 [email protected]", 1);
            AddExpectedTupleAndCount(expectedData, file9, "ETD-MSn", "ITMS + c NSI d Full ms2 [email protected]", 1);
            AddExpectedTupleAndCount(expectedData, file9, "ETD-MSn", "ITMS + c NSI d Full ms2 [email protected]", 8);
            AddExpectedTupleAndCount(expectedData, file9, "ETD-MSn", "ITMS + c NSI d Full ms2 [email protected]", 8);
            AddExpectedTupleAndCount(expectedData, file9, "ETD-MSn", "ITMS + c NSI d Full ms2 [email protected]", 56);
            AddExpectedTupleAndCount(expectedData, file9, "SA_ETD-MSn", "ITMS + c NSI d sa Full ms2 [email protected]", 5);
            AddExpectedTupleAndCount(expectedData, file9, "SA_ETD-MSn", "ITMS + c NSI d sa Full ms2 [email protected]", 1);
            AddExpectedTupleAndCount(expectedData, file9, "SA_ETD-MSn", "ITMS + c NSI d sa Full ms2 [email protected]", 1);
            AddExpectedTupleAndCount(expectedData, file9, "SA_ETD-MSn", "ITMS + c NSI d sa Full ms2 [email protected]", 14);
            AddExpectedTupleAndCount(expectedData, file9, "SA_ETD-MSn", "ITMS + c NSI d sa Full ms2 [email protected]", 32);
            AddExpectedTupleAndCount(expectedData, file9, "SA_ETD-MSn", "ITMS + c NSI d sa Full ms2 [email protected]", 260);

            const string file10 = "blank_MeOH-3_18May16_Rainier_Thermo_10344958";
            AddExpectedTupleAndCount(expectedData, file10, "HMS", "FTMS - p ESI Full ms", 190);
            AddExpectedTupleAndCount(expectedData, file10, "CID-HMSn", "FTMS - c ESI d Full ms2 [email protected]", 207);
            AddExpectedTupleAndCount(expectedData, file10, "CID-HMSn", "FTMS - c ESI d Full ms3 [email protected] [email protected]", 4);

            const string file11 = "HCC-38_ETciD_EThcD_07Jan16_Pippin_15-08-53";
            AddExpectedTupleAndCount(expectedData, file11, "HMS", "FTMS + p NSI Full ms", 20);
            AddExpectedTupleAndCount(expectedData, file11, "CID-MSn", "ITMS + c NSI r d Full ms2 [email protected]", 231);
            AddExpectedTupleAndCount(expectedData, file11, "ETciD-MSn", "ITMS + c NSI r d sa Full ms2 [email protected]@cid20.00", 46);
            AddExpectedTupleAndCount(expectedData, file11, "ETciD-MSn", "ITMS + c NSI r d sa Full ms2 [email protected]@cid20.00", 4);
            AddExpectedTupleAndCount(expectedData, file11, "ETD-MSn", "ITMS + c NSI r d Full ms2 [email protected]", 46);
            AddExpectedTupleAndCount(expectedData, file11, "ETD-MSn", "ITMS + c NSI r d Full ms2 [email protected]", 4);
            AddExpectedTupleAndCount(expectedData, file11, "EThcD-MSn", "ITMS + c NSI r d sa Full ms2 [email protected]@hcd20.00", 46);
            AddExpectedTupleAndCount(expectedData, file11, "EThcD-MSn", "ITMS + c NSI r d sa Full ms2 [email protected]@hcd20.00", 4);

            const string file12 = "MeOHBlank03POS_11May16_Legolas_HSS-T3_A925";
            AddExpectedTupleAndCount(expectedData, file12, "HMS", "FTMS + p ESI Full ms", 8);
            AddExpectedTupleAndCount(expectedData, file12, "CID-MSn", "ITMS + c ESI d Full ms2 [email protected]", 47);
            AddExpectedTupleAndCount(expectedData, file12, "HCD-HMSn", "FTMS + c ESI d Full ms2 [email protected]", 38);
            AddExpectedTupleAndCount(expectedData, file12, "HCD-HMSn", "FTMS + c ESI d Full ms2 [email protected]", 8);

            AddExpectedTupleAndCount(expectedData, "IPA-blank-07_25Oct13_Gimli", "Zoom-MS", "ITMS + p NSI Z ms", 101);

            var dataFile = GetRawDataFile(rawFileName);

            using (var reader = new XRawFileIO(dataFile.FullName))
            {
                Console.WriteLine("Parsing scan headers for {0}", dataFile.Name);

                var scanCount = reader.GetNumScans();
                Console.WriteLine("Total scans: {0}", scanCount);
                Assert.AreEqual(expectedTotalScanCount, scanCount, "Total scan count mismatch");
                Console.WriteLine();

                var scanCountMS1 = 0;
                var scanCountMS2 = 0;
                var scanTypeCountsActual = new Dictionary<Tuple<string, string>, int>();

                for (var scanNumber = scanStart; scanNumber <= scanEnd; scanNumber++)
                {
                    clsScanInfo scanInfo;
                    var success = reader.GetScanInfo(scanNumber, out scanInfo);

                    Assert.IsTrue(success, "GetScanInfo returned false for scan {0}", scanNumber);

                    var scanType = XRawFileIO.GetScanTypeNameFromFinniganScanFilterText(scanInfo.FilterText);
                    var genericScanFilter = XRawFileIO.MakeGenericFinniganScanFilter(scanInfo.FilterText);

                    var scanTypeKey = new Tuple<string, string>(scanType, genericScanFilter);

                    int observedScanCount;
                    if (scanTypeCountsActual.TryGetValue(scanTypeKey, out observedScanCount))
                    {
                        scanTypeCountsActual[scanTypeKey] = observedScanCount + 1;
                    }
                    else
                    {
                        scanTypeCountsActual.Add(scanTypeKey, 1);
                    }

                    if (scanInfo.MSLevel > 1)
                        scanCountMS2++;
                    else
                        scanCountMS1++;

                }

                Console.WriteLine("scanCountMS1={0}", scanCountMS1);
                Console.WriteLine("scanCountMS2={0}", scanCountMS2);

                Assert.AreEqual(expectedMS1, scanCountMS1, "MS1 scan count mismatch");
                Assert.AreEqual(expectedMS2, scanCountMS2, "MS2 scan count mismatch");

                Dictionary<Tuple<string, string>, int> expectedScanInfo;
                if (!expectedData.TryGetValue(Path.GetFileNameWithoutExtension(dataFile.Name), out expectedScanInfo))
                {
                    Assert.Fail("Dataset {0} not found in dictionary expectedData", dataFile.Name);
                }

                Console.WriteLine("{0,-5} {1,5} {2}", "Valid", "Count", "ScanType");

                foreach (var scanType in (from item in scanTypeCountsActual orderby item.Key select item))
                {
                    int expectedScanCount;
                    if (expectedScanInfo.TryGetValue(scanType.Key, out expectedScanCount))
                    {
                        var isValid = scanType.Value == expectedScanCount;

                        Console.WriteLine("{0,-5} {1,5} {2}", isValid, scanType.Value, scanType.Key);

                        Assert.AreEqual(expectedScanCount, scanType.Value, "Scan type count mismatch");
                    }
                    else
                    {
                        Console.WriteLine("Unexpected scan type found: {0}", scanType.Key);
                        Assert.Fail("Unexpected scan type found: {0}", scanType.Key);
                    }
                }
            }
        }
        public void TestCorruptDataHandling(
            string rawFileName,
            int scanStart,
            int scanEnd,
            int expectedMS1,
            int expectedMS2,
            int corruptScanStart,
            int corruptScanEnd)
        {
            var dataFile = GetRawDataFile(rawFileName);

            try
            {

                using (var reader = new XRawFileIO(dataFile.FullName))
                {
                    var scanCount = reader.GetNumScans();
                    Console.WriteLine("Scan count for {0}: {1}", dataFile.Name, scanCount);

                    if (expectedMS1 + expectedMS2 == 0)
                    {
                        Assert.IsTrue(reader.FileInfo.CorruptFile, "CorruptFile is false while we expected it to be true");
                        Assert.IsTrue(scanCount == 0, "ScanCount is non-zero, while we expected it to be 0");
                    }
                    else
                    {
                        Assert.IsFalse(reader.FileInfo.CorruptFile, "CorruptFile is true while we expected it to be false");
                        Assert.IsTrue(scanCount > 0, "ScanCount is zero, while we expected it to be > 0");
                    }

                    var scanCountMS1 = 0;
                    var scanCountMS2 = 0;

                    for (var scanNumber = scanStart; scanNumber <= scanEnd; scanNumber++)
                    {
                        try
                        {
                            clsScanInfo scanInfo;
                            reader.GetScanInfo(scanNumber, out scanInfo);

                            if (reader.FileInfo.CorruptFile)
                            {
                                Assert.IsTrue(string.IsNullOrEmpty(scanInfo.FilterText), "FilterText is not empty but should be since corrupt file");
                            }
                            else
                            {
                                Assert.IsFalse(string.IsNullOrEmpty(scanInfo.FilterText), "FilterText is empty but should not be");

                                if (scanInfo.MSLevel > 1)
                                    scanCountMS2++;
                                else
                                    scanCountMS1++;
                            }

                            double[] mzList;
                            double[] intensityList;

                            // Note: this function call will fail randomly with file Corrupt_Scans6920-7021_AID_STM_013_101104_06_LTQ_16Nov04_Earth_0904-8.raw
                            // Furthermore, we are unable to catch the exception that occurs (or no exception is thrown) and adding
                            // [System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions] to the function does not help
                            var dataPointCount = reader.GetScanData(scanNumber, out mzList, out intensityList);

                            if (reader.FileInfo.CorruptFile)
                            {
                                Assert.IsTrue(dataPointCount == 0, "GetScanData unexpectedly reported a non-zero data count for scan {0}", scanNumber);
                                Assert.IsTrue(mzList.Length == 0, "GetScanData unexpectedly returned m/z data for scan {0}", scanNumber);
                                Assert.IsTrue(intensityList.Length == 0, "GetScanData unexpectedly returned intensity data for scan {0}", scanNumber);
                            }
                            else
                            {
                                if (dataPointCount == 0)
                                {
                                    Console.WriteLine("Corrupt scan encountered: {0}", scanNumber);

                                    Assert.IsTrue(scanNumber >= corruptScanStart && scanNumber <= corruptScanEnd, "Unexpected corrupt scan found, scan {0}", scanNumber);
                                    Assert.IsTrue(mzList.Length == 0, "GetScanData unexpectedly returned m/z data for scan {0}", scanNumber);
                                    Assert.IsTrue(intensityList.Length == 0, "GetScanData unexpectedly returned intensity data for scan {0}", scanNumber);
                                }
                                else
                                {
                                    Assert.IsTrue(dataPointCount > 0, "GetScanData reported a data point count of 0 for scan {0}", scanNumber);
                                    Assert.IsTrue(mzList.Length > 0, "GetScanData unexpectedly returned no m/z data for scan {0}", scanNumber);
                                    Assert.IsTrue(intensityList.Length > 0, "GetScanData unexpectedly returned no intensity data for scan {0}", scanNumber);
                                    Assert.IsTrue(mzList.Length == intensityList.Length, "Array length mismatch for m/z and intensity data for scan {0}", scanNumber);
                                    Assert.IsTrue(dataPointCount == mzList.Length, "Array length does not agree with dataPointCount for scan {0}", scanNumber);
                                }
                            }

                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("Exception reading scan {0}: {1}", scanNumber, ex.Message);
                            Assert.Fail("Exception reading scan {0}", scanNumber);
                        }
                    }

                    Console.WriteLine("scanCountMS1={0}", scanCountMS1);
                    Console.WriteLine("scanCountMS2={0}", scanCountMS2);

                    Assert.AreEqual(expectedMS1, scanCountMS1, "MS1 scan count mismatch");
                    Assert.AreEqual(expectedMS2, scanCountMS2, "MS2 scan count mismatch");

                }
            }
            catch (Exception ex)
            {
                var msg = string.Format("Exception opening .raw file {0}:\n{1}", rawFileName, ex.Message);
                Console.WriteLine(msg);
                Assert.Fail(msg);
            }
        }
        public void TestGetNumScans(string rawFileName, int expectedResult)
        {
            var dataFile = GetRawDataFile(rawFileName);

            using (var reader = new XRawFileIO(dataFile.FullName))
            {
                var scanCount = reader.GetNumScans();

                Console.WriteLine("Scan count for {0}: {1}", dataFile.Name, scanCount);
                Assert.AreEqual(expectedResult, scanCount, "Scan count mismatch");
            }
        }
        private static void TestReader(string rawFilePath, bool centroid = false, bool testSumming = false, int scanStart = 0, int scanEnd = 0)
        {
            try {
                if (!File.Exists(rawFilePath)) {
                    Console.WriteLine("File not found, skipping: " + rawFilePath);
                    return;
                }

                using (var oReader = new XRawFileIO(rawFilePath))
                {

                    foreach(var method in oReader.FileInfo.InstMethods) {
                        Console.WriteLine(method);
                    }

                    var iNumScans = oReader.GetNumScans();

                    var strCollisionEnergies = string.Empty;

                    ShowMethod(oReader);

                    var scanStep = 1;

                    if (scanStart < 1)
                        scanStart = 1;
                    if (scanEnd < 1) {
                        scanEnd = iNumScans;
                        scanStep = 21;
                    } else {
                        if (scanEnd < scanStart) {
                            scanEnd = scanStart;
                        }
                    }

                    for (var iScanNum = scanStart; iScanNum <= scanEnd; iScanNum += scanStep) {
                        clsScanInfo oScanInfo;

                        var bSuccess = oReader.GetScanInfo(iScanNum, out oScanInfo);
                        if (bSuccess) {
                            Console.Write("Scan " + iScanNum + " at " + oScanInfo.RetentionTime.ToString("0.00") + " minutes: " + oScanInfo.FilterText);
                            var lstCollisionEnergies = oReader.GetCollisionEnergy(iScanNum);

                            if (lstCollisionEnergies.Count == 0) {
                                strCollisionEnergies = string.Empty;
                            } else if (lstCollisionEnergies.Count >= 1) {
                                strCollisionEnergies = lstCollisionEnergies[0].ToString("0.0");

                                if (lstCollisionEnergies.Count > 1) {
                                    for (var intIndex = 1; intIndex <= lstCollisionEnergies.Count - 1; intIndex++) {
                                        strCollisionEnergies += ", " + lstCollisionEnergies[intIndex].ToString("0.0");
                                    }
                                }
                            }

                            if (string.IsNullOrEmpty(strCollisionEnergies)) {
                                Console.WriteLine();
                            } else {
                                Console.WriteLine("; CE " + strCollisionEnergies);
                            }

                            string monoMZ;
                            string chargeState;
                            string isolationWidth;

                            if (oScanInfo.TryGetScanEvent("Monoisotopic M/Z:", out monoMZ, false)) {
                                Console.WriteLine("Monoisotopic M/Z: " + monoMZ);
                            }

                            if (oScanInfo.TryGetScanEvent("Charge State", out chargeState, true))
                            {
                                Console.WriteLine("Charge State: " + chargeState);
                            }

                            if (oScanInfo.TryGetScanEvent("MS2 Isolation Width", out isolationWidth, true))
                            {
                                Console.WriteLine("MS2 Isolation Width: " + isolationWidth);
                            }

                            if (iScanNum % 50 == 0 || scanEnd - scanStart <= 50) {
                                // Get the data for scan iScanNum

                                Console.WriteLine();
                                Console.WriteLine("Spectrum for scan " + iScanNum);

                                double[] dblMzList;
                                double[] dblIntensityList;
                                var intDataCount = oReader.GetScanData(iScanNum, out dblMzList, out dblIntensityList, 0, centroid);

                                var mzDisplayStepSize = 50;
                                if (centroid) {
                                    mzDisplayStepSize = 1;
                                }

                                for (var iDataPoint = 0; iDataPoint <= dblMzList.Length - 1; iDataPoint += mzDisplayStepSize) {
                                    Console.WriteLine("  " + dblMzList[iDataPoint].ToString("0.000") + " mz   " + dblIntensityList[iDataPoint].ToString("0"));
                                }
                                Console.WriteLine();

                                const int scansToSum = 15;

                                if (iScanNum + scansToSum < iNumScans & testSumming) {
                                    // Get the data for scan iScanNum through iScanNum + 15

                                    double[,] dblMassIntensityPairs;
                                    var dataCount = oReader.GetScanDataSumScans(iScanNum, iScanNum + scansToSum, out dblMassIntensityPairs, 0, centroid);

                                    Console.WriteLine("Summed spectrum, scans " + iScanNum + " through " + (iScanNum + scansToSum));

                                    for (var iDataPoint = 0; iDataPoint <= dblMassIntensityPairs.GetLength(1) - 1; iDataPoint += 50) {
                                        Console.WriteLine("  " + dblMassIntensityPairs[0, iDataPoint].ToString("0.000") + " mz   " + dblMassIntensityPairs[1, iDataPoint].ToString("0"));
                                    }

                                    Console.WriteLine();
                                }

                                if (oScanInfo.IsFTMS) {
                                    udtFTLabelInfoType[] ftLabelData;

                                    var dataCount = oReader.GetScanLabelData(iScanNum, out ftLabelData);

                                    Console.WriteLine();
                                    Console.WriteLine("{0,12}{1,12}{2,12}{3,12}{4,12}{5,12}", "Mass", "Intensity", "Resolution", "Baseline", "Noise", "Charge");

                                    for (var iDataPoint = 0; iDataPoint <= dataCount - 1; iDataPoint += 50) {
                                        Console.WriteLine("{0,12}{1,12}{2,12}{3,12}{4,12}{5,12}", ftLabelData[iDataPoint].Mass.ToString("0.000"), ftLabelData[iDataPoint].Intensity.ToString("0"), ftLabelData[iDataPoint].Resolution.ToString("0"), ftLabelData[iDataPoint].Baseline.ToString("0.0"), ftLabelData[iDataPoint].Noise.ToString("0"), ftLabelData[iDataPoint].Charge.ToString("0"));
                                    }

                                    udtMassPrecisionInfoType[] ftPrecisionData;

                                    dataCount = oReader.GetScanPrecisionData(iScanNum, out ftPrecisionData);

                                    Console.WriteLine();
                                    Console.WriteLine("{0,12}{1,12}{2,12}{3,12}{4,12}", "Mass", "Intensity", "AccuracyMMU", "AccuracyPPM", "Resolution");

                                    for (var iDataPoint = 0; iDataPoint <= dataCount - 1; iDataPoint += 50) {
                                        Console.WriteLine("{0,12}{1,12}{2,12}{3,12}{4,12}", ftPrecisionData[iDataPoint].Mass.ToString("0.000"), ftPrecisionData[iDataPoint].Intensity.ToString("0"), ftPrecisionData[iDataPoint].AccuracyMMU.ToString("0.000"), ftPrecisionData[iDataPoint].AccuracyPPM.ToString("0.000"), ftPrecisionData[iDataPoint].Resolution.ToString("0"));
                                    }
                                }

                            }

                        }
                    }

                }

            } catch (Exception ex) {
                Console.WriteLine("Error in sub TestReader: " + ex.Message);
            }
        }