text: import pytest
  import numpy as np
  from unittest.mock import MagicMock
  
  from glue import core
  
  from .. import command as c
  from .. import roi
  from ..data_factories import tabular_data
  from .util import simple_session, simple_catalog
  
  
  class TestCommandStack(object):
  
      def setup_method(self, method):
          self.session = simple_session()
          self.stack = self.session.command_stack
  
      def make_command(self):
          return MagicMock(c.Command)
  
      def make_data(self):
          with simple_catalog() as path:
              cmd = c.LoadData(path=path, factory=tabular_data)
              data = self.stack.do(cmd)
          return data
  
      def test_do(self):
  
          c1 = self.make_command()
          self.stack.do(c1)
  
          c1.do.assert_called_once_with(self.session)
  
      def test_undo(self):
          c1, c2 = self.make_command(), self.make_command()
  
          self.stack.do(c1)
          self.stack.do(c2)
  
          self.stack.undo()
          c2.undo.assert_called_once_with(self.session)
  
          self.stack.undo()
          c1.undo.assert_called_once_with(self.session)
  
      def test_redo(self):
          c1, c2 = self.make_command(), self.make_command()
  
          self.stack.do(c1)
          self.stack.do(c2)
  
          self.stack.undo()
          self.stack.redo()
  
          c2.undo.assert_called_once_with(self.session)
  
          assert c2.do.call_count == 2
          assert c2.undo.call_count == 1
          assert c1.do.call_count == 1
          assert c1.undo.call_count == 0
  
      def test_max_undo(self):
          cmds = [self.make_command() for _ in range(c.MAX_UNDO + 1)]
  
          for cmd in cmds:
              self.stack.do(cmd)
  
          for cmd in cmds[:-1]:
              self.stack.undo()
  
          with pytest.raises(IndexError):
              self.stack.undo()
  
      def test_invalid_redo(self):
          with pytest.raises(IndexError) as exc:
              self.stack.redo()
          assert exc.value.args[0] == 'No commands to redo'
  
      def test_load_data(self):
          data = self.make_data()
          np.testing.assert_array_equal(data['a'], [1, 3])
  
      def test_add_data(self):
          data = self.make_data()
          cmd = c.AddData(data=data)
  
          self.stack.do(cmd)
          assert len(self.session.data_collection) == 1
  
          self.stack.undo()
          assert len(self.session.data_collection) == 0
  
      def test_remove_data(self):
          data = self.make_data()
  
          add = c.AddData(data=data)
          remove = c.RemoveData(data=data)
  
          self.stack.do(add)
          assert len(self.session.data_collection) == 1
  
          self.stack.do(remove)
          assert len(self.session.data_collection) == 0
  
          self.stack.undo()
          assert len(self.session.data_collection) == 1
  
      def test_new_data_viewer(self):
          cmd = c.NewDataViewer(viewer=None, data=None)
          v = self.stack.do(cmd)
  
          self.session.application.new_data_viewer.assert_called_once_with(None, None)
  
          self.stack.undo()
          v.close.assert_called_once_with(warn=False)
  
      def test_apply_roi(self):
          x = core.Data(x=[1, 2, 3])
          s = x.new_subset()
          dc = self.session.data_collection
          dc.append(x)
  
          r = MagicMock(roi.Roi)
          apply_roi = MagicMock()
  
          cmd = c.ApplyROI(data_collection=dc, roi=r,
                           apply_func=apply_roi)
  
          self.stack.do(cmd)
          apply_roi.assert_called_once_with(r)
  
          old_state = s.subset_state
          s.subset_state = MagicMock(spec_set=core.subset.SubsetState)
  
          self.stack.undo()
          assert s.subset_state is old_state
  