text: try:
      from unittest.mock import Mock
  except:
      from mock import Mock
  
  import holoviews as hv
  import panel as pn
  import numpy as np
  
  from holoviews.streams import (
      Stream, Selection1D, RangeXY, BoundsXY,
  )
  
  from bokeh.document import Document
  from pyviz_comms import Comm
  
  from .testplot import TestPlotlyPlot
  
  
  class TestDynamicMap(TestPlotlyPlot):
  
      def test_update_dynamic_map_with_stream(self):
          ys = np.arange(10)
  
          # Build stream
          Scale = Stream.define('Scale', scale=1.0)
          scale_stream = Scale()
  
          # Build DynamicMap
          def build_scatter(scale):
              return hv.Scatter(ys * scale)
  
          dmap = hv.DynamicMap(build_scatter, streams=[scale_stream])
  
          # Create HoloViews Pane using panel so that we can access the plotly pane
          # used to display the plotly figure
          dmap_pane = pn.pane.HoloViews(dmap, backend='plotly')
  
          # Call get_root to force instantiation of internal plots/models
          doc = Document()
          comm = Comm()
          dmap_pane.get_root(doc, comm)
  
          # Get reference to the plotly pane
          _, plotly_pane = next(iter(dmap_pane._plots.values()))
  
          # Check initial data
          data = plotly_pane.object['data']
          self.assertEqual(len(data), 1)
          self.assertEqual(data[0]['type'], 'scatter')
          np.testing.assert_equal(data[0]['y'], ys)
  
          # Watch object for changes
          fn = Mock()
          plotly_pane.param.watch(fn, 'object')
  
          # Update stream
          scale_stream.event(scale=2.0)
  
          # Check that figure object was updated
          data = plotly_pane.object['data']
          np.testing.assert_equal(data[0]['y'], ys * 2.0)
  
          # Check that object callback was triggered
          fn.assert_called_once()
          args, kwargs = fn.call_args_list[0]
          event = args[0]
          self.assertIs(event.obj, plotly_pane)
          self.assertIs(event.new, plotly_pane.object)
  
  
  class TestInteractiveStream(TestPlotlyPlot):
      # Note: Testing the core logic of each interactive stream should take place in
      # testcallbacks.py. Here we are testing that that callbacks are properly
      # routed to streams
  
      def test_interactive_streams(self):
          ys = np.arange(10)
          scatter1 = hv.Scatter(ys)
          scatter2 = hv.Scatter(ys)
          scatter3 = hv.Scatter(ys)
  
          # Single stream on the first scatter
          rangexy1 = RangeXY(source=scatter1)
  
          # Multiple streams of the same type on second scatter
          boundsxy2a = BoundsXY(source=scatter2)
          boundsxy2b = BoundsXY(source=scatter2)
  
          # Multiple streams of different types on third scatter
          rangexy3 = RangeXY(source=scatter3)
          boundsxy3 = BoundsXY(source=scatter3)
          selection1d3 = Selection1D(source=scatter3)
  
          # Build layout and layout Pane
          layout = scatter1 + scatter2 + scatter3
          layout_pane = pn.pane.HoloViews(layout, backend='plotly')
  
          # Get plotly pane reference
          doc = Document()
          comm = Comm()
          layout_pane.get_root(doc, comm)
          _, plotly_pane = next(iter(layout_pane._plots.values()))
  
          # Simulate zoom and check that RangeXY streams updated accordingly
          plotly_pane.viewport = {
              'xaxis.range': [1, 3],
              'yaxis.range': [2, 4],
              'xaxis2.range': [3, 5],
              'yaxis2.range': [4, 6],
              'xaxis3.range': [5, 7],
              'yaxis3.range': [6, 8],
          }
  
          self.assertEqual(rangexy1.x_range, (1, 3))
          self.assertEqual(rangexy1.y_range, (2, 4))
          self.assertEqual(rangexy3.x_range, (5, 7))
          self.assertEqual(rangexy3.y_range, (6, 8))
  
          plotly_pane.viewport = None
          self.assertIsNone(rangexy1.x_range)
          self.assertIsNone(rangexy1.y_range)
          self.assertIsNone(rangexy3.x_range)
          self.assertIsNone(rangexy3.y_range)
  
          # Simulate box selection and check that BoundsXY and Selection1D streams
          # update accordingly
  
          # Box select on second subplot
          plotly_pane.selected_data = {
              'points': [],
              'range': {
                  'x2': [10, 20],
                  'y2': [11, 22]
              }
          }
  
          self.assertEqual(boundsxy2a.bounds, (10, 11, 20, 22))
          self.assertEqual(boundsxy2b.bounds, (10, 11, 20, 22))
  
          # Box selecrt on third subplot
          plotly_pane.selected_data = {
              'points': [
                  {'curveNumber': 2, 'pointNumber': 0},
                  {'curveNumber': 2, 'pointNumber': 3},
                  {'curveNumber': 2, 'pointNumber': 7},
              ],
              'range': {
                  'x3': [0, 5],
                  'y3': [1, 6]
              }
          }
  
          self.assertEqual(boundsxy3.bounds, (0, 1, 5, 6))
          self.assertEqual(selection1d3.index, [0, 3, 7])
  
          # bounds streams on scatter 2 are None
          self.assertIsNone(boundsxy2a.bounds)
          self.assertIsNone(boundsxy2b.bounds)
  
          # Clear selection
          plotly_pane.selected_data = None
          self.assertIsNone(boundsxy3.bounds)
          self.assertIsNone(boundsxy2a.bounds)
          self.assertIsNone(boundsxy2b.bounds)
          self.assertEqual(selection1d3.index, [])
  
  