Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
#of the National Weather Service.
#NWS often uses Generative AI (GenAI) for code development and refactoring. Whenever GenAI is used, NWS requires a full human review of code before it is added to its repositories.
#@author Main Author(s) : Aldgisl (AI Persona), Hendrik L. Tolman
#@author Contributors : Jules (Agentic AI), Kit Stokes
#@date Initial : 2026-03-20
#@date Last update : 2026-06-08
#@author Contributors : Jules (Agentic AI), Kit Stokes, Jessica Meixner
#@date Initial : 2026-07-09
#@date Last update : 2026-07-13

cmake_minimum_required(VERSION 3.25)

Expand Down Expand Up @@ -158,11 +158,17 @@ ww4_add_test(test_ww4_std_out tests/ww4_utils/L1_test_ww4_std_out.cpp ww4_utils)
ww4_add_test(test_ww4_logfile tests/ww4_utils/L1_test_ww4_logfile.cpp ww4_utils)
ww4_add_test(test_ww4_standalone_config tests/ww4_utils/L1_test_ww4_standalone_config.cpp ww4_utils)
ww4_add_test(test_ww4_run_config tests/ww4_utils/L1_test_ww4_run_config.cpp ww4_utils)
ww4_add_test(test_ww4_input_utils tests/ww4_utils/L1_test_ww4_input_utils.cpp ww4_utils)
ww4_add_test(test_ww4_output_utils tests/ww4_utils/L1_test_ww4_output_utils.cpp ww4_utils)
ww4_add_test(test_ww4_service tests/ww4_utils/L1_test_ww4_service.cpp ww4_utils)
ww4_add_test(test_ww4_constants tests/ww4_utils/L1_test_ww4_constants.cpp ww4_utils)

# Core tests
ww4_add_test(test_ww4_standalone tests/ww4_core/L1_test_ww4_standalone.cpp ww4_core)
target_compile_definitions(test_ww4_standalone PRIVATE STANDALONE_EXE_PATH="${CMAKE_SOURCE_DIR}/exe/ww4_standalone")
ww4_add_test(test_w4core_init tests/ww4_core/L1_test_w4core_init.cpp ww4_core)
ww4_add_test(test_w4core_wave tests/ww4_core/L1_test_w4core_wave.cpp ww4_core)
ww4_add_test(test_w4core_finalize tests/ww4_core/L1_test_w4core_finalize.cpp ww4_core)
ww4_add_test(test_w4core_time tests/ww4_core/L2_test_w4core_time.cpp ww4_core)
ww4_add_test(test_w4core_wave_output tests/ww4_core/L2_test_w4core_wave_output.cpp ww4_core)
ww4_add_test(test_w4core_zero_step tests/ww4_core/L2_test_w4core_zero_step.cpp ww4_core)
Expand Down
63 changes: 63 additions & 0 deletions tests/ww4_core/L1_test_w4core_finalize.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* +--------------------------------------------------------+
* | WAVEWATCH IV, open source, code management by NOAA/NWS |
* +--------------------------------------------------------+
*
* @file L1_test_w4core_finalize.cpp
* @brief Unit tests for the WAVEWATCH IV core finalization.
* @details This file tests the w4core_finalize routine.
* @copyright © 2026 National Weather Service, National Oceanic and Atmospheric
* Administration. WAVEWATCH IV (TM) and WW4 (TM) are trademarks of the National
* Weather Service.
* NWS often uses Generative AI (GenAI) for code development and refactoring.
* Whenever GenAI is used, NWS requires a full human review of code before it is
* added to its repositories.
* @author Main Author(s): Aldgisl (AI Persona), Hendrik L. Tolman
* @author Contributors: Jules (Agentic AI)
* @date Initial, 2026-07-09
* @date Last update : 2026-07-13
*/

#include "ww4_core/w4core_finalize.h"
#include "ww4_core/w4core_init.h"
#include <fstream>
#include <gtest/gtest.h>

namespace ww4_core {

class W4CoreFinalizeL1Test : public ::testing::Test {
protected:
void SetUp() override { resetInternalState(); }

void TearDown() override {
resetInternalState();
std::remove("ww4_run_config.yaml");
std::remove("ww4_log.txt");
}
};

TEST_F(W4CoreFinalizeL1Test, BasicFinalize) {
// Create dummy run configuration file
std::ofstream runFile("ww4_run_config.yaml");
runFile << "general:\n";
runFile << " calendar_type: Standard\n";
runFile << " time_step: 3600.0\n";
runFile << "forcing:\n";
runFile << " water_levels: none\n";
runFile << " currents: none\n";
runFile << " winds: none\n";
runFile << " ice_concentrations: none\n";
runFile << " bottom_depth: none\n";
runFile.close();

ww4_utils::DateTime startTime = {20260101, 0.0};
w4core_init(startTime, "test_finalize", std::cout);

ww4_utils::DateTime endTime = {20260101, 3600.0};
EXPECT_NO_THROW(w4core_finalize(endTime, std::cout));

// State should be completely cleared after finalization
EXPECT_EQ(getProgramName(), "");
}

} // namespace ww4_core
94 changes: 94 additions & 0 deletions tests/ww4_core/L1_test_w4core_init.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* +--------------------------------------------------------+
* | WAVEWATCH IV, open source, code management by NOAA/NWS |
* +--------------------------------------------------------+
*
* @file L1_test_w4core_init.cpp
* @brief Unit tests for the WAVEWATCH IV core initialization.
* @details This file tests initialization and state management in ww4_core.
* @copyright © 2026 National Weather Service, National Oceanic and Atmospheric
* Administration. WAVEWATCH IV (TM) and WW4 (TM) are trademarks of the National
* Weather Service.
* NWS often uses Generative AI (GenAI) for code development and refactoring.
* Whenever GenAI is used, NWS requires a full human review of code before it is
* added to its repositories.
* @author Main Author(s): Aldgisl (AI Persona), Hendrik L. Tolman
* @author Contributors: Jules (Agentic AI)
* @date Initial, 2026-07-09
* @date Last update : 2026-07-13
*/

#include "ww4_core/w4core_init.h"
#include <fstream>
#include <gtest/gtest.h>

namespace ww4_core {

class W4CoreInitTest : public ::testing::Test {
protected:
void SetUp() override { resetInternalState(); }

void TearDown() override {
resetInternalState();
std::remove("ww4_run_config.yaml");
std::remove("ww4_log.txt");
}
};

TEST_F(W4CoreInitTest, ResetInternalStateAndAccessors) {
resetInternalState();
EXPECT_EQ(getProgramName(), "");
EXPECT_EQ(getRunConfig().timeStep, -1.0);
EXPECT_FALSE(getLogFileStream().is_open());
}

TEST_F(W4CoreInitTest, UpdateWaveModelAndInputTime) {
ww4_utils::DateTime t = {20260101, 120000.0};
updateWaveModelTime(t);
EXPECT_EQ(*getWaveTimeData().modelTime, t);

ww4_utils::intTimeData inputTime{};
inputTime.time1 = t;
updateWaveInputTime(ww4_utils::InputType::Winds, inputTime);
EXPECT_EQ(getWaveTimeData().winds.time1, t);

updateWaveInputTime(ww4_utils::InputType::WaterLevels, inputTime);
EXPECT_EQ(getWaveTimeData().waterLevels.time1, t);

updateWaveInputTime(ww4_utils::InputType::Currents, inputTime);
EXPECT_EQ(getWaveTimeData().currents.time1, t);

updateWaveInputTime(ww4_utils::InputType::IceConcentrations, inputTime);
EXPECT_EQ(getWaveTimeData().iceConcentrations.time1, t);

updateWaveInputTime(ww4_utils::InputType::BottomDepth, inputTime);
EXPECT_EQ(getWaveTimeData().bottomDepth.time1, t);
}

TEST_F(W4CoreInitTest, MutableRunConfig) {
getMutableRunConfig().timeStep = 7200.0;
EXPECT_EQ(getRunConfig().timeStep, 7200.0);
}

TEST_F(W4CoreInitTest, W4CoreInitAndReset) {
// Create dummy run configuration file
std::ofstream runFile("ww4_run_config.yaml");
runFile << "general:\n";
runFile << " calendar_type: Standard\n";
runFile << " time_step: 3600.0\n";
runFile << "forcing:\n";
runFile << " water_levels: none\n";
runFile << " currents: none\n";
runFile << " winds: none\n";
runFile << " ice_concentrations: none\n";
runFile << " bottom_depth: none\n";
runFile.close();

ww4_utils::DateTime startTime = {20260101, 0.0};
w4core_init(startTime, "test_init", std::cout);

EXPECT_EQ(getProgramName(), "test_init");
EXPECT_EQ(getRunConfig().timeStep, 3600.0);
}

} // namespace ww4_core
62 changes: 62 additions & 0 deletions tests/ww4_core/L1_test_w4core_wave.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* +--------------------------------------------------------+
* | WAVEWATCH IV, open source, code management by NOAA/NWS |
* +--------------------------------------------------------+
*
* @file L1_test_w4core_wave.cpp
* @brief Unit tests for the WAVEWATCH IV core wave time-stepping loop.
* @details This file tests the w4core_wave routine.
* @copyright © 2026 National Weather Service, National Oceanic and Atmospheric
* Administration. WAVEWATCH IV (TM) and WW4 (TM) are trademarks of the National
* Weather Service.
* NWS often uses Generative AI (GenAI) for code development and refactoring.
* Whenever GenAI is used, NWS requires a full human review of code before it is
* added to its repositories.
* @author Main Author(s): Aldgisl (AI Persona), Hendrik L. Tolman
* @author Contributors: Jules (Agentic AI)
* @date Initial, 2026-07-09
* @date Last update : 2026-07-13
*/

#include "ww4_core/w4core_init.h"
#include "ww4_core/w4core_wave.h"
#include <fstream>
#include <gtest/gtest.h>

namespace ww4_core {

class W4CoreWaveL1Test : public ::testing::Test {
protected:
void SetUp() override { resetInternalState(); }

void TearDown() override {
resetInternalState();
std::remove("ww4_run_config.yaml");
std::remove("ww4_log.txt");
}
};

TEST_F(W4CoreWaveL1Test, BasicWaveExecution) {
// Create dummy run configuration file
std::ofstream runFile("ww4_run_config.yaml");
runFile << "general:\n";
runFile << " calendar_type: Standard\n";
runFile << " time_step: 3600.0\n";
runFile << "forcing:\n";
runFile << " water_levels: none\n";
runFile << " currents: none\n";
runFile << " winds: none\n";
runFile << " ice_concentrations: none\n";
runFile << " bottom_depth: none\n";
runFile.close();

ww4_utils::DateTime startTime = {20260101, 0.0};
w4core_init(startTime, "test_wave", std::cout);

ww4_utils::DateTime endTime = {20260101,
20000.0}; // 2 hours (2 * 3600 seconds)
EXPECT_NO_THROW(w4core_wave(startTime, endTime, std::cout));
EXPECT_EQ(*getWaveTimeData().modelTime, endTime);
}

} // namespace ww4_core
97 changes: 97 additions & 0 deletions tests/ww4_core/L1_test_ww4_standalone.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* +--------------------------------------------------------+
* | WAVEWATCH IV, open source, code management by NOAA/NWS |
* +--------------------------------------------------------+
*
* @file L1_test_ww4_standalone.cpp
* @brief Unit/integration tests for the WAVEWATCH IV stand-alone program.
* @details This file tests the main execution of the ww4_standalone program.
* @copyright © 2026 National Weather Service, National Oceanic and Atmospheric
* Administration. WAVEWATCH IV (TM) and WW4 (TM) are trademarks of the National
* Weather Service.
* NWS often uses Generative AI (GenAI) for code development and refactoring.
* Whenever GenAI is used, NWS requires a full human review of code before it is
* added to its repositories.
* @author Main Author(s): Aldgisl (AI Persona), Hendrik L. Tolman
* @author Contributors: Jules (Agentic AI), Kit Stokes, Jessica Meixner
* @date Initial, 2026-07-13
* @date Last update : 2026-07-13
*/

#include <cstdlib>
#include <fstream>
#include <gtest/gtest.h>
#include <string>

#ifndef _WIN32
#include <sys/wait.h>
#endif

namespace ww4_core {

class WW4StandaloneL1Test : public ::testing::Test {
protected:
void SetUp() override {
std::remove("ww4_standalone.yaml");
std::remove("ww4_run_config.yaml");
std::remove("ww4_log.txt");
std::remove("standalone_test.log");
}

void TearDown() override {
std::remove("ww4_standalone.yaml");
std::remove("ww4_run_config.yaml");
std::remove("ww4_log.txt");
std::remove("standalone_test.log");
}

int runStandalone() {
std::string command =
std::string(STANDALONE_EXE_PATH) + " > standalone_test.log 2>&1";
int result = std::system(command.c_str());
#ifdef _WIN32
return result;
#else
return WIFEXITED(result) ? WEXITSTATUS(result) : -1;
#endif
}
};

TEST_F(WW4StandaloneL1Test, MissingConfigFails) {
// Executing ww4_standalone main without configuration files should fail.
int exit_code = runStandalone();
EXPECT_NE(exit_code, 0);
}

TEST_F(WW4StandaloneL1Test, ValidConfigSucceeds) {
// Create a valid ww4_standalone.yaml configuration
std::ofstream standaloneFile("ww4_standalone.yaml");
standaloneFile << "simulation:\n"
<< " start_time: \"20260101 000000\"\n"
<< " end_time: \"20260101 020000\"\n";
standaloneFile.close();

// Create a valid ww4_run_config.yaml configuration
std::ofstream runFile("ww4_run_config.yaml");
runFile << "general:\n"
<< " calendar_type: \"Standard\"\n"
<< " produce_std_out: \"no\"\n"
<< " produce_log_file: \"yes\"\n"
<< " screen_output_level: \"none\"\n"
<< " time_step: 3600.0\n"
<< "physics:\n"
<< " dry_run: \"no\"\n"
<< "forcing:\n"
<< " bottom_depth: \"from_grid\"\n"
<< " water_levels: \"none\"\n"
<< " currents: \"none\"\n"
<< " winds: \"none\"\n"
<< " ice_concentrations: \"none\"\n";
runFile.close();

// Executing ww4_standalone main with valid configuration should succeed.
int exit_code = runStandalone();
EXPECT_EQ(exit_code, 0);
}

} // namespace ww4_core
26 changes: 25 additions & 1 deletion tests/ww4_utils/L1_test_memory_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* @author Main Author(s): Aldgisl (AI Persona), Hendrik L. Tolman
* @author Contributors: Jules (Agentic AI)
* @date Initial, 2026-02-27
* @date Last update : 2026-05-26
* @date Last update : 2026-07-13
*/

#include "ww4_utils/memory_utils.h"
Expand Down Expand Up @@ -96,4 +96,28 @@ TEST(MemoryUtilsTest, MockLargeValues) {
std::remove(mockPath);
}

TEST(MemoryUtilsTest, ResetMemoryStatusPath) {
const char *mockPath = "mock_status_reset.txt";
{
std::ofstream mockFile(mockPath);
mockFile << "VmPeak: 1234 kB\n";
mockFile << "VmSize: 1234 kB\n";
mockFile << "VmHWM: 1234 kB\n";
mockFile << "VmRSS: 1234 kB\n";
}

setMemoryStatusPathForTesting(mockPath);
auto usage = MemoryUtils::captureMemoryUsage();
ASSERT_TRUE(usage.has_value());
EXPECT_EQ(usage->vmPeak, 1234ULL);

resetMemoryStatusPath();
// Now should capture host/self status instead of mock
usage = MemoryUtils::captureMemoryUsage();
ASSERT_TRUE(usage.has_value());
EXPECT_NE(usage->vmPeak, 1234ULL);

std::remove(mockPath);
}

} // namespace ww4_utils
Loading
Loading