class_docstr: Base class for testing FXRateReader implementations.
  
  To test a new FXRateReader implementation, subclass from this base class
  and implement the ``reader`` property, returning an FXRateReader that uses
  the data stored in ``cls.fx_rates``.
code: def test_columnar_lookup(self):
      rand = np.random.RandomState(42)
  
      dates = pd.date_range(
          self.FX_RATES_START_DATE - pd.Timedelta("2 days"),
          self.FX_RATES_END_DATE + pd.Timedelta("2 days"),
      )
      rates = self.FX_RATES_RATE_NAMES + [DEFAULT_FX_RATE]
      possible_quotes = self.FX_RATES_CURRENCIES
      possible_bases = self.FX_RATES_CURRENCIES + [None]
      reader = self.reader
  
      # For every combination of rate name and quote currency...
      for rate, quote in itertools.product(rates, possible_quotes):
          for N in 1, 2, 10, 200:
              # Choose N (date, base) pairs randomly with replacement.
              dts_raw = rand.choice(dates, N, replace=True)
              dts = pd.DatetimeIndex(dts_raw, tz="utc")
              bases = rand.choice(
                  possible_bases, N, replace=True
              ).astype(object)
  
              # ... And check that we get the expected result when querying
              # for those dates/currencies.
              result = reader.get_rates_columnar(
                  rate, quote, bases, dts
              )
              expected = self.get_expected_fx_rates_columnar(
                  rate,
                  quote,
                  bases,
                  dts,
              )
  
              assert_equal(result, expected)
  
imports: import itertools
  import pandas
  import numpy
  from zipline.data.fx import DEFAULT_FX_RATE
  from zipline.testing.predicates import assert_equal
  import zipline.testing.fixtures
label_desc: Use image node_id 5 for calling the _FXReaderTestCase obj's underlying member method code with example usage: obj.test_columnar_lookup() without return types