code: def test_forecast(self):
      # batch forecasting RMSE = 3.001
      _, _ = self.model.train(self.train_data)
      forecast, lb, ub = self.model.forecast(
          self.max_forecast_steps, return_iqr=True
      )
      rmse = ForecastMetric.RMSE.value(self.test_data, forecast)
      logger.info(
          f"RMSE = {rmse:.4f} for {self.max_forecast_steps} step forecasting"
      )
      self.assertAlmostEqual(rmse, 3.0, delta=1)
      msis = ForecastMetric.MSIS.value(
          ground_truth=self.test_data,
          predict=forecast,
          insample=self.train_data,
          periodicity=4,
          ub=ub,
          lb=lb,
      )
      logger.info(f"MSIS = {msis:.4f}")
      self.assertAlmostEqual(msis, 14, delta=5)
  
      # make sure save/load model gets same predictions
      logger.info("Test save/load...")
      savedir = join(rootdir, "tmp", "default", "forecast", "uni")
      self.model.save(dirname=savedir)
      loaded = DefaultForecaster.load(dirname=savedir)
  
      loaded_pred, _ = loaded.forecast(self.max_forecast_steps)
      self.assertSequenceEqual(list(loaded_pred), list(forecast))
  
      # streaming forecasting RMSE = 2.4689
      test_t = self.test_data.np_time_stamps
      t, tf = to_pd_datetime([test_t[0], test_t[-1]])
      forecast_results = None
      while t < tf:
          cur_train, cur_test = self.data.bisect(t, t_in_left=False)
          cur_test = cur_test.window(t, t + self.model.timedelta)
          forecast, err = self.model.forecast(
              cur_test.time_stamps, cur_train
          )
          if forecast_results is None:
              forecast_results = forecast
          else:
              forecast_results += forecast
          t += self.model.timedelta
      rmse_onestep = ForecastMetric.RMSE.value(
          self.test_data, forecast_results
      )
      logger.info(
          f"Streaming RMSE = {rmse_onestep:.4f} for {self.max_forecast_steps} step forecasting"
      )
      self.assertAlmostEqual(rmse_onestep, 2.4, delta=1)
  
      # streaming forecasting performs better than batch forecasting
      self.assertLessEqual(rmse_onestep, rmse)
  
imports: import logging
  from os.path import abspath, dirname, join
  import sys
  import unittest
  import pandas
  from merlion.evaluate.forecast import ForecastMetric
  from merlion.models.defaults import DefaultForecaster, DefaultForecasterConfig
  from merlion.models.utils.rolling_window_dataset import RollingWindowDataset
  from merlion.transform.bound import LowerUpperClip
  from merlion.transform.normalize import MinMaxNormalize
  from merlion.transform.resample import TemporalResample
  from merlion.transform.sequence import TransformSequence
  from merlion.utils.time_series import TimeSeries, to_pd_datetime
  from ts_datasets.forecast import SeattleTrail
label_desc: Use image node_id 2 for calling the TestUnivariate obj's underlying member method code with example usage: obj.test_forecast() without return types