code: def test_add_tile(test_input, provider):
      plot = figure(
          x_range=(-2000000, 6000000),
          y_range=(-1000000, 7000000),
          x_axis_type="mercator",
          y_axis_type="mercator",
      )
      plot.add_tile(test_input)
      tile_source = plot.renderers[0].tile_source
      sf = "@2x" if "RETINA" in test_input else None
      assert tile_source.url == provider.build_url(scale_factor=sf)
      assert tile_source.attribution == provider.html_attribution
      if hasattr(provider, "max_zoom"):
          assert tile_source.max_zoom == provider.max_zoom
  
      # test retina keyword
      if "RETINA" not in test_input and "{r}" in provider.url:
          plot2 = figure(
              x_range=(-2000000, 6000000),
              y_range=(-1000000, 7000000),
              x_axis_type="mercator",
              y_axis_type="mercator",
          )
          plot2.add_tile(test_input, retina=True)
          tile_source2 = plot2.renderers[0].tile_source
          assert tile_source2.url == provider.build_url(
              scale_factor="@2x"
          )
  
imports: from __future__ import annotations
  import pytest
  from math import isnan
  from unittest import mock
  from unittest.mock import MagicMock, patch
  import xyzservices.providers
  from bokeh.core.validation import check_integrity, process_validation_issues
  from bokeh.models import CategoricalScale, CustomJS, DataRange1d, FactorRange, GlyphRenderer, Label, LinearAxis, LinearScale, LogScale, PanTool, Plot, Range1d, ResetTool, Title, WMTSTileSource, ZoomInTool, ZoomOutTool
  from bokeh.plotting import figure
  import bokeh.models.plots
label_desc: Use image node_id 14 for calling a global function with example usage: test_add_tile(test_input, provider) without return types