leoniv/ass_launcher

View on GitHub
examples/enterprise_ole_example.rb

Summary

Maintainability
A
0 mins
Test Coverage
require 'example_helper'

module Examples
  module EnterpriseOle
    fail 'OLE in UNIX??? :)' if PLATFORM::LINUX
    module HRESULT
      RPC_SERVER_UNAVALIBLE = '0x800706ba'
    end

    # 1C:Enterprise provides three OLE servers:
    # v8x.Application - thick client local OLE server
    # v8xC.Application - thin client local OLE server
    # v8x.ComConnector - in-process OLE server for connect to:
    #  - 1C:Enterprise application aka infobases
    #  - 1C:Enterprise server agent
    #  - 1C:Enterprise server working process

    describe 'AssLauncher provides wrappers for 1C:Enterprise Ole servers' do
      extend AssLauncher::Api

      # Wrappers returned AssLauncher::Api#ole method

      thick_app = ole(:thick, PLATFORM_VER)
      it 'Wrapper for "Thick application ole server" (aka v83.Application)' do
        thick_app.must_be_instance_of AssLauncher::Enterprise::Ole::ThickApplication
      end

      thin_app = ole(:thin, PLATFORM_VER)
      it 'Wrapper for "Thin application ole server" (aka v83c.Application)' do
        thin_app.must_be_instance_of AssLauncher::Enterprise::Ole::ThinApplication
      end

      external = ole(:external, PLATFORM_VER)
      it 'Wrapper for "External connection with infobase" (childe v83.ComConnector)' do
        external.must_be_instance_of AssLauncher::Enterprise::Ole::IbConnection
      end

      wpconn = ole(:wprocess, PLATFORM_VER)
      it 'Wrapper for "1C:Enterprise server working process connection" (childe v83.ComConnector)' do
        wpconn.must_be_instance_of AssLauncher::Enterprise::Ole::WpConnection
      end

      saconn = ole(:sagent, PLATFORM_VER)
      it 'Wrapper for "1C:Enterprise server agent connection" (childe v83.ComConnector)' do
        saconn.must_be_instance_of AssLauncher::Enterprise::Ole::AgentConnection
      end
    end

    describe 'Example for basic to use' do
      extend AssLauncher::Api

      # Get ole wrapper
      external = ole(:external, PLATFORM_VER)

      it 'Example' do
        # Open connection with connection string
        # For WpConnection and AgentConnection __connect__
        # expects URI string
        external.__open__ TMP::EMPTY_IB_CS

        # Call 1C:Enterprise method #InfoBaseConnectionString
        external.InfoBaseConnectionString.must_equal\
          TMP::EMPTY_IB_CS.to_ole_string

        # Close connection
        external.__close__
      end

      after do
        external.__close__
      end
    end

    describe 'Closing connection' do
      describe 'For local server it working perfect' do
        extend AssLauncher::Api
        thick_app = ole(:thick, PLATFORM_VER)

        it 'Connection realy closed of thick_app' do
          thick_app.__open__ TMP::EMPTY_IB_CS

          ole_array = thick_app.newObject('Array')
          ole_array.Count.must_equal 0

          thick_app.__close__

          # Fails because OLE server is down
          e = proc {
            ole_array.Count
          }.must_raise NoMethodError

          e.message.must_match %r{error code:#{HRESULT::RPC_SERVER_UNAVALIBLE}}
        end

        after do
          thick_app.__close__
        end
      end

      describe 'For in-process server close connection working with restrictions' do
        # 1C in-process OLE servers haven't method for closing connection!
        # Connection keep alive while live ruby process.
        #
        # If in one ruby script we want to use in-process connection for some work
        # do in the infobase and after them we want run other connection
        # or application or designer with flag of exclusive mode, in this case
        # opened in-process connection doesn't give us to do it
        #
        # AssLauncher provide feature for closing in-process connection  but
        # it working with restrictions.
        #
        # AssLauncher patching WIN32OLE and collect all ole objects which
        # generated and try kill refs calling #ole_free method for them when
        # #__close__ method call.
        #
        # But it works not always. Connection keep alive while have any alive
        # WIN32OLE refs generated this connection

        describe 'Case when in-process server connection realy closed' do
          extend AssLauncher::Api

          # Get local ole server wrapper
          # It is service connector
          thick_app = ole(:thick, PLATFORM_VER)

          # Get in-process ole server wrapper
          # It object under test
          external = ole(:external, PLATFORM_VER)

          it 'External connection realy closed' do
            thick_app.__open__ TMP::EMPTY_IB_CS
            external.__open__ TMP::EMPTY_IB_CS

            # We have two working sessions
            thick_app.GetInfoBaseSessions.Count.must_equal 2

            ole_array = external.newObject('Array')
            ole_array.Count.must_equal 0

            external.__close__

            # External connection was closed
            # and only one working session is alive
            thick_app.GetInfoBaseSessions.Count.must_equal 1

            # Fails 'failed to get Dispatch Interface'
            # because #ole_free was called while external.__close__
            e = proc {
              ole_array.Count
            }.must_raise RuntimeError
            e.message.must_match %r{failed to get Dispatch Interface}i
          end

          after do
            thick_app.__close__
            external.__close__
          end
        end
      end
    end

    describe 'We can chose version of 1C:Enterprise ole server' do
      # We can choosing 1C ole server's version.
      # AssLauncher automatically register needed server version
      # and returns suitable wrapper.
      #
      # Registration version of ole server working correct only for local
      # servers such as Thin and Thick applications.
      #
      # We don't get new version of in-process ole server until old version
      # is loaded in memory

      it "Fail if 1C:Enterprise version doesn't instaled" do
        extend AssLauncher::Api
        external = ole(:external, '~> 999')
        e = proc {
          external.__open__ ''
        }.must_raise RuntimeError
        e.message.must_match %r{Platform version `~> 999' not instaled}
      end

      describe 'Choosing version of local ole server working perfect' do
        extend AssLauncher::Api

        thick_app = ole(:thick, PLATFORM_VER)

        it 'Ole server have suitable version' do
          thick_app.__open__ TMP::EMPTY_IB_CS
          system_info = thick_app.newObject('SystemInfo')

          real_ole_app_version = Gem::Version.new(system_info.AppVersion)

          real_ole_app_version.must_equal\
            thick_app.instance_variable_get(:@__ole_binary__).version

          thick_app.__close__
        end

        after do
          thick_app.__close__
        end
      end
    end

    describe 'AssLauncher patching WIN32OLE for some reason' do
      describe 'Getting real Ruby objects from WIN32OLE wrapper' do

        # Ruby WIN32OLE automatically convert Ruby objects into IDispatch
        # when they passed as parameter in to OLE server's side.
        # When such objects returns on the Ruby's side they will keep as WIN32OLE
        # AssLauncher provides feature for getting real Ruby object

        it 'Example' do
          ole_server = WIN32OLE.new('Scripting.Dictionary')

          ruby_obj = Object.new

          skip "It usually Segmentation fault in ruby:\n"\
          " 2.0.0p645 (2015-04-13 revision 50299) [i386-cygwin]\n"\
          " 2.3.6p384 (2017-12-14 revision 9808) [i386-cygwin]"

          # Call OLE server
          ole_server.add(1, ruby_obj)
          wrapped_obj = ole_server.items[0]

          # Ruby object wrapped into WIN32OLE
          wrapped_obj.must_be_instance_of WIN32OLE

          # Ask: Is it Ryuby object?
          wrapped_obj.__ruby__?.must_equal true

          # Get Ruby object
          wrapped_obj.__real_obj__.must_equal ruby_obj
        end
      end
    end
  end
end