code: def test_yearly(self, base_terms, calendar_name):
      downsampled_terms = tuple(
          t.downsample("year_start") for t in base_terms
      )
      all_terms = base_terms + downsampled_terms
  
      all_sessions = self.trading_sessions[calendar_name]
      end_session = all_sessions[-1]
  
      years = all_sessions.year
      sessions_in_2012 = all_sessions[years == 2012]
      sessions_in_2013 = all_sessions[years == 2013]
      sessions_in_2014 = all_sessions[years == 2014]
  
      # Simulate requesting computation where the unaltered lookback would
      # land exactly on the first date in 2014.  We shouldn't request any
      # additional rows for the regular terms or the downsampled terms.
      for i in range(0, 30, 5):
          start_session = sessions_in_2014[i]
          self.check_extra_row_calculations(
              all_terms,
              all_sessions,
              start_session,
              end_session,
              min_extra_rows=i,
              expected_extra_rows=i,
          )
  
      # Simulate requesting computation where the unaltered lookback would
      # land on the second date in 2014.  We should request one more extra
      # row in the downsampled terms to push us back to the first date in
      # 2014.
      for i in range(0, 30, 5):
          start_session = sessions_in_2014[i + 1]
          self.check_extra_row_calculations(
              downsampled_terms,
              all_sessions,
              start_session,
              end_session,
              min_extra_rows=i,
              expected_extra_rows=i + 1,
          )
          self.check_extra_row_calculations(
              base_terms,
              all_sessions,
              start_session,
              end_session,
              min_extra_rows=i,
              expected_extra_rows=i,
          )
  
      # Simulate requesting computation where the unaltered lookback would
      # land on the last date of 2013. The downsampled terms should request
      # enough extra rows to push us back to the start of 2013.
      for i in range(0, 30, 5):
          start_session = sessions_in_2014[i]
          self.check_extra_row_calculations(
              downsampled_terms,
              all_sessions,
              start_session,
              end_session,
              min_extra_rows=i + 1,
              expected_extra_rows=i + len(sessions_in_2013),
          )
          self.check_extra_row_calculations(
              base_terms,
              all_sessions,
              start_session,
              end_session,
              min_extra_rows=i + 1,
              expected_extra_rows=i + 1,
          )
  
      # Simulate requesting computation where the unaltered lookback would
      # land on the last date of 2012. The downsampled terms should request
      # enough extra rows to push us back to the first known date, which is
      # in the middle of 2012.
      for i in range(0, 30, 5):
          start_session = sessions_in_2013[i]
          self.check_extra_row_calculations(
              downsampled_terms,
              all_sessions,
              start_session,
              end_session,
              min_extra_rows=i + 1,
              expected_extra_rows=i + len(sessions_in_2012),
          )
          self.check_extra_row_calculations(
              base_terms,
              all_sessions,
              start_session,
              end_session,
              min_extra_rows=i + 1,
              expected_extra_rows=i + 1,
          )
  
      # Simulate requesting computation where the unaltered lookback would
      # land prior to the first date of 2012. The downsampled terms will fail
      # to request enough extra rows.
      for i in range(0, 30, 5):
          with self.assertRaisesRegex(
              NoFurtherDataError,
              r"\s*Insufficient data to compute Pipeline",
          ):
              self.check_extra_row_calculations(
                  downsampled_terms,
                  all_sessions,
                  all_sessions[i],
                  end_session,
                  min_extra_rows=i + 1,
                  expected_extra_rows=i + 1,
              )
  
          self.check_extra_row_calculations(
              base_terms,
              all_sessions,
              all_sessions[i],
              end_session,
              min_extra_rows=i + 1,
              expected_extra_rows=i + 1,
          )
  
imports: from functools import partial
  import pandas
  from pandas.util.testing import assert_frame_equal
  from zipline.errors import NoFurtherDataError
  from zipline.pipeline import Pipeline, CustomFactor, CustomFilter, CustomClassifier, SimplePipelineEngine
  from zipline.pipeline.data.testing import TestingDataSet
  from zipline.pipeline.domain import CA_EQUITIES, EquitySessionDomain, GB_EQUITIES, US_EQUITIES
  from zipline.pipeline.factors import SimpleMovingAverage
  from zipline.pipeline.filters.smoothing import All
  from zipline.testing import ZiplineTestCase, parameter_space, ExplodingObject
  from zipline.testing.fixtures import WithTradingSessions, WithSeededRandomPipelineEngine, WithAssetFinder
  from zipline.utils.classproperty import classproperty
  from zipline.utils.input_validation import _qualified_name
  from zipline.utils.numpy_utils import int64_dtype
label_desc: Use image node_id 1 for calling the ComputeExtraRowsTestCase obj's underlying member method code with example usage: obj.test_yearly(base_terms, calendar_name) without return types