/* * libfud * Copyright 2024 Dominick Allen * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fud_csv.hpp" #include "fud_print.hpp" #include "gtest/gtest.h" //NOLINTBEGIN(readability-magic-numbers) namespace fud { const StringView happyCsvFilename{u8"fud-happy-test.csv"}; const StringView happyData{ u8"foo,bar,baz\n" u8"1,Unquoted Text,\"Quoted Text with embedded \"\" quote and embedded newline \nsee\"\n" u8",,\"Prior two fields are empty\"\n"}; Csv expectedHappyCsv() { Csv csv{Csv::makeDefault()}; csv.numLines = 3; csv.numColumns = 3; auto resizeStatus = csv.entries.resize(csv.numLines * csv.numColumns); fudAssert(resizeStatus == FudStatus::Success); csv.entries[0] = StringView{u8"foo"}; csv.entries[1] = StringView{u8"bar"}; csv.entries[2] = StringView{u8"baz"}; csv.entries[3] = StringView{u8"1"}; csv.entries[4] = StringView{u8"Unquoted Text"}; csv.entries[5] = StringView{u8"Quoted Text with embedded \" quote and embedded newline \nsee"}; csv.entries[6] = StringView{}; csv.entries[7] = StringView{}; csv.entries[8] = StringView{u8"Prior two fields are empty"}; for (const auto& entry : csv.entries) { if (entry.length() > 0) { auto extendStatus = csv.buffer.extend(entry.asSpan()); fudAssert(extendStatus == FudStatus::Success); } } size_t totalSize = 0; for (auto& entry : csv.entries) { entry.m_data = csv.buffer.data() + totalSize; totalSize += entry.length(); } return csv; } auto writeHappyCsv() -> FudStatus { auto fileResult{RegularFile::create( happyCsvFilename, FileAccessMode::Write, OpenFlags{OpenFlagEnum::Truncate}, PermUserRwGroupRead, false, NullOpt)}; if (fileResult.isError()) { debugPrint(u8"Error opening file: {}\n", FudStatusToString(fileResult.getError())); return fileResult.takeError(); } auto file{fileResult.takeOkay()}; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) auto writeResult = file.write(reinterpret_cast(happyData.data()), happyData.length()); if (writeResult.status != FudStatus::Success) { return writeResult.status; } if (writeResult.bytesDrained != happyData.length()) { return FudStatus::Failure; } return FudStatus::Success; } TEST(FudCsv, ParseCsvFromFilename) { Csv csv{Csv::makeDefault()}; ASSERT_EQ(writeHappyCsv(), FudStatus::Success); auto parseStatus = Csv::parseFromFilenameUnbuffered(csv, happyCsvFilename); if (parseStatus != FudStatus::Success) { debugPrint(u8"Error parsing file: {}\n", FudStatusToString(parseStatus)); } ASSERT_EQ(parseStatus, FudStatus::Success); auto expectedCsv{expectedHappyCsv()}; ASSERT_EQ(expectedCsv.numLines, csv.numLines); ASSERT_EQ(expectedCsv.numColumns, csv.numColumns); ASSERT_EQ(expectedCsv.buffer.size(), csv.buffer.size()); ASSERT_EQ(expectedCsv.entries.size(), csv.entries.size()); EXPECT_EQ(0, compareMem(expectedCsv.buffer.data(), expectedCsv.buffer.size(), csv.buffer.data(), csv.buffer.size()).takeOkayOr(-1)); for (size_t line = 0; line < csv.numLines; ++line) { for (size_t column = 0; column < csv.numColumns; ++column) { auto expectedEntryResult = expectedCsv.entry(line, column); ASSERT_TRUE(expectedEntryResult.isOkay()); auto expectedEntryOpt{expectedEntryResult.takeOkay()}; auto csvEntryResult = csv.entry(line, column); EXPECT_TRUE(csvEntryResult.isOkay()); if (csvEntryResult.isError()) { continue; } auto csvEntryOpt{csvEntryResult.takeOkay()}; EXPECT_EQ(expectedEntryOpt.hasValue(), csvEntryOpt.hasValue()); if (expectedEntryOpt.isNone()) { continue; } auto expectedEntry{expectedEntryOpt.value()}; auto csvEntry{csvEntryOpt.value()}; EXPECT_EQ(expectedEntry.length(), csvEntry.length()); EXPECT_EQ(0, compareMem(expectedEntry.data(), expectedEntry.length(), csvEntry.data(), csvEntry.length()).takeOkayOr(-1)); } } } TEST(FudCsv, ParseNuclides) { Vector expectedHeaders{}; auto pushExpected = [&](StringView item) { auto pushStatus = expectedHeaders.pushBack(StringView{u8"z"}); fudAssert(pushStatus == FudStatus::Success); }; pushExpected(StringView{u8"z"}); pushExpected(StringView{u8"n"}); pushExpected(StringView{u8"symbol"}); pushExpected(StringView{u8"radius"}); pushExpected(StringView{u8"unc_r"}); pushExpected(StringView{u8"abundance"}); pushExpected(StringView{u8"unc_a"}); pushExpected(StringView{u8"energy_shift"}); pushExpected(StringView{u8"energy"}); pushExpected(StringView{u8"unc_e"}); pushExpected(StringView{u8"ripl_shift"}); pushExpected(StringView{u8"jp"}); pushExpected(StringView{u8"half_life"}); pushExpected(StringView{u8"operator_hl"}); pushExpected(StringView{u8"unc_hl"}); pushExpected(StringView{u8"unit_hl"}); pushExpected(StringView{u8"half_life_sec"}); pushExpected(StringView{u8"unc_hls"}); pushExpected(StringView{u8"decay_1"}); pushExpected(StringView{u8"decay_1_%"}); pushExpected(StringView{u8"unc_1"}); pushExpected(StringView{u8"decay_2"}); pushExpected(StringView{u8"decay_2_%"}); pushExpected(StringView{u8"unc_2"}); pushExpected(StringView{u8"decay_3"}); pushExpected(StringView{u8"decay_3_%"}); pushExpected(StringView{u8"unc_3"}); pushExpected(StringView{u8"isospin"}); pushExpected(StringView{u8"magnetic_dipole"}); pushExpected(StringView{u8"unc_md"}); pushExpected(StringView{u8"electric_quadrupole"}); pushExpected(StringView{u8"unc_eq"}); pushExpected(StringView{u8"qbm"}); pushExpected(StringView{u8"unc_qb"}); pushExpected(StringView{u8"qbm_n"}); pushExpected(StringView{u8"unc_qbmn"}); pushExpected(StringView{u8"qa"}); pushExpected(StringView{u8"unc_qa"}); pushExpected(StringView{u8"qec"}); pushExpected(StringView{u8"unc_qec"}); pushExpected(StringView{u8"sn"}); pushExpected(StringView{u8"unc_sn"}); pushExpected(StringView{u8"sp"}); pushExpected(StringView{u8"unc_sp"}); pushExpected(StringView{u8"binding"}); pushExpected(StringView{u8"unc_ba"}); pushExpected(StringView{u8"atomic_mass"}); pushExpected(StringView{u8"unc_am"}); pushExpected(StringView{u8"massexcess"}); pushExpected(StringView{u8"unc_me"}); pushExpected(StringView{u8"me_systematics"}); pushExpected(StringView{u8"discovery"}); pushExpected(StringView{u8"ENSDFpublicationcut-off"}); pushExpected(StringView{u8"ENSDFauthors"}); pushExpected(StringView{u8"Extraction_date"}); StringView nuclidesFilename{u8"test/nuclides.csv"}; Csv csv{Csv::makeDefault()}; auto parseStatus = Csv::parseFromFilenameUnbuffered(csv, nuclidesFilename); if (parseStatus != FudStatus::Success) { debugPrint(u8"Error parsing file: {}\n", FudStatusToString(parseStatus)); } ASSERT_EQ(parseStatus, FudStatus::Success); } } // namespace fud //NOLINTEND(readability-magic-numbers)