Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions repository/BioTools-Tests/BioGtUnixSubprocessTest.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"
Tests for BioGtUnixSubprocess execution API compatibility.
"
Class {
#name : 'BioGtUnixSubprocessTest',
#superclass : 'BioAbstractTest',
#category : 'BioTools-Tests-Adapters',
#package : 'BioTools-Tests',
#tag : 'Adapters'
}

{ #category : 'tests' }
BioGtUnixSubprocessTest >> testExecuteCommandChained [
| command output |

command := Smalltalk os isWindows
ifTrue: [ 'cmd /c "echo bio&&echo smalltalk"' ]
ifFalse: [ 'sh -c ''echo bio; echo smalltalk''' ].
output := BioGtUnixSubprocess executeCommand: command.
self assert: (output includesSubstring: 'bio').
self assert: (output includesSubstring: 'smalltalk')
]

{ #category : 'tests' }
BioGtUnixSubprocessTest >> testExecuteCommandEcho [
| output |

output := BioGtUnixSubprocess executeCommand: 'echo biosmalltalk'.
self assert: (output includesSubstring: 'biosmalltalk')
]

{ #category : 'tests' }
BioGtUnixSubprocessTest >> testExecuteCommandFailureReturnsErrorOutput [
| command output |

command := Smalltalk os isWindows
ifTrue: [ 'cmd /c "nonexistent_command_12345"' ]
ifFalse: [ 'sh -c ''nonexistent_command_12345''' ].
output := BioGtUnixSubprocess executeCommand: command.
self deny: output isEmpty
]

{ #category : 'tests' }
BioGtUnixSubprocessTest >> testExecuteCommandParameter [
| output |

output := BioGtUnixSubprocess
executeCommand: 'echo'
parameter: 'biosmalltalk'.
self assert: (output includesSubstring: 'biosmalltalk')
]

{ #category : 'tests' }
BioGtUnixSubprocessTest >> testExecuteCommandPipeline [
| command output |

command := Smalltalk os isWindows
ifTrue: [ 'cmd /c "echo biosmalltalk | findstr /c:biosmalltalk"' ]
ifFalse: [ 'sh -c ''echo biosmalltalk | grep biosmalltalk''' ].
output := BioGtUnixSubprocess executeCommand: command.
self assert: (output includesSubstring: 'biosmalltalk')
]

{ #category : 'tests' }
BioGtUnixSubprocessTest >> testExecuteCommandQuotedParameter [
| output |

output := BioGtUnixSubprocess
executeCommand: 'echo'
parameter: '"bio smalltalk"'.
self assert: (output includesSubstring: 'bio smalltalk')
]

{ #category : 'tests' }
BioGtUnixSubprocessTest >> testExecuteCommandStderrOutput [
| command output |

command := Smalltalk os isWindows
ifTrue: [ 'cmd /c "echo errormsg 1>&2 & exit /b 1"' ]
ifFalse: [ 'sh -c ''echo errormsg 1>&2; exit 1''' ].
output := BioGtUnixSubprocess executeCommand: command.
self assert: (output includesSubstring: 'errormsg')
]

{ #category : 'tests' }
BioGtUnixSubprocessTest >> testProviderClass [
self assert: BioGtUnixSubprocess providerClass equals: #GtUnixSubprocess
]
88 changes: 88 additions & 0 deletions repository/BioTools-Tests/BioOSSubProcessExecutorTest.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"
Tests for BioOSSubProcessExecutor execution behavior.
"
Class {
#name : 'BioOSSubProcessExecutorTest',
#superclass : 'BioAbstractTest',
#category : 'BioTools-Tests-Adapters',
#package : 'BioTools-Tests',
#tag : 'Adapters'
}

{ #category : 'tests' }
BioOSSubProcessExecutorTest >> testExecuteCommandChained [
| command output |

command := Smalltalk os isWindows
ifTrue: [ 'cmd /c "echo bio&&echo smalltalk"' ]
ifFalse: [ 'sh -c ''echo bio; echo smalltalk''' ].
output := BioOSSubProcessExecutor executeCommand: command.
self assert: (output includesSubstring: 'bio').
self assert: (output includesSubstring: 'smalltalk')
]

{ #category : 'tests' }
BioOSSubProcessExecutorTest >> testExecuteCommandEcho [
| output |

output := BioOSSubProcessExecutor executeCommand: 'echo biosmalltalk'.
self assert: (output includesSubstring: 'biosmalltalk')
]

{ #category : 'tests' }
BioOSSubProcessExecutorTest >> testExecuteCommandFailureReturnsErrorOutput [
| command output |

command := Smalltalk os isWindows
ifTrue: [ 'cmd /c "nonexistent_command_12345"' ]
ifFalse: [ 'sh -c ''nonexistent_command_12345''' ].
output := BioOSSubProcessExecutor executeCommand: command.
self deny: output isEmpty
]

{ #category : 'tests' }
BioOSSubProcessExecutorTest >> testExecuteCommandParameter [
| output |

output := BioOSSubProcessExecutor
executeCommand: 'echo'
parameter: 'biosmalltalk'.
self assert: (output includesSubstring: 'biosmalltalk')
]

{ #category : 'tests' }
BioOSSubProcessExecutorTest >> testExecuteCommandPipeline [
| command output |

command := Smalltalk os isWindows
ifTrue: [ 'cmd /c "echo biosmalltalk | findstr /c:biosmalltalk"' ]
ifFalse: [ 'sh -c ''echo biosmalltalk | grep biosmalltalk''' ].
output := BioOSSubProcessExecutor executeCommand: command.
self assert: (output includesSubstring: 'biosmalltalk')
]

{ #category : 'tests' }
BioOSSubProcessExecutorTest >> testExecuteCommandQuotedParameter [
| output |

output := BioOSSubProcessExecutor
executeCommand: 'echo'
parameter: '"bio smalltalk"'.
self assert: (output includesSubstring: 'bio smalltalk')
]

{ #category : 'tests' }
BioOSSubProcessExecutorTest >> testExecuteCommandStderrOutput [
| command output |

command := Smalltalk os isWindows
ifTrue: [ 'cmd /c "echo errormsg 1>&2 & exit /b 1"' ]
ifFalse: [ 'sh -c ''echo errormsg 1>&2; exit 1''' ].
output := BioOSSubProcessExecutor executeCommand: command.
self assert: (output includesSubstring: 'errormsg')
]

{ #category : 'tests' }
BioOSSubProcessExecutorTest >> testProviderClass [
self assert: BioOSSubProcessExecutor providerClass equals: #OSSUnixSubprocess
]
51 changes: 45 additions & 6 deletions repository/BioTools/BioAbstractAdapter.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,21 @@ BioAbstractAdapter class >> adapter [
^ self adapterClass new
]

{ #category : 'accessing-adapters' }
{ #category : 'instance creation' }
BioAbstractAdapter class >> adapterClass [
"Answer the preferred subclass for providing the receiver's services"

^ self validAdapters
detect: [ : cls | cls isPreferredAdapter ]
ifNone: [ self validAdapters first ]
"Answer the preferred subclass for providing the receiver's services."

| preferred |
preferred := self preferredAdapterClassForCurrentPlatform.
preferred ifNotNil: [
(self validAdapters includes: preferred) ifTrue: [ ^ preferred ] ].

^ self validAdapters
detect: [ :cls | cls isPreferredAdapter ]
ifNone: [
self validAdapters
ifEmpty: [ self signalInvalidObject: 'No provider was found' ]
ifNotEmpty: [ :adapters | adapters first ] ]
]

{ #category : 'accessing' }
Expand Down Expand Up @@ -78,6 +86,20 @@ BioAbstractAdapter class >> hasAnyProvider [
^ self allFinalClasses anySatisfy: [ : cls | cls providerIsAvailable ]
]

{ #category : 'testing' }
BioAbstractAdapter class >> isGTImage [
"Answer whether the current image is a Glamorous Toolkit image."

^ Smalltalk globals includesKey: #GtImage
]

{ #category : 'testing' }
BioAbstractAdapter class >> isPharoImage [
"Answer whether the current image is a standard Pharo image (including GT, unless explicitly handled)."

^ self isGTImage not
]

{ #category : 'accessing' }
BioAbstractAdapter class >> isPreferred [

Expand All @@ -92,6 +114,23 @@ BioAbstractAdapter class >> isPreferredAdapter [
^ false
]

{ #category : 'private' }
BioAbstractAdapter class >> preferredAdapterClassForCurrentPlatform [
"Answer the adapter class that best matches the current image/platform.
Prefer modern subprocess engines on Pharo/GToolkit, keep legacy engines as fallback."

| os |
os := BioOSInterfaceEngine adapter.

(self isGTImage and: [ os isRunningInUnix ]) ifTrue: [
^ BioGtUnixSubprocess ].

(self isPharoImage and: [ os isRunningInUnix ]) ifTrue: [
^ OSSUnixSubprocess ].

^ nil
]

{ #category : 'accessing' }
BioAbstractAdapter class >> providerClass [
" Answer a <Symbol>, the main external class name for this adapter "
Expand Down
87 changes: 87 additions & 0 deletions repository/BioTools/BioGtUnixSubprocess.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"
Execution engine backed by GToolkit subprocess API for running shell commands and retrieving output.
"
Class {
#name : 'BioGtUnixSubprocess',
#superclass : 'BioExecutionEngine',
#category : 'BioTools',
#package : 'BioTools'
}

{ #category : 'executing' }
BioGtUnixSubprocess class >> executeCommand: aCommandName [
"See superimplementor's comment"

^ [ self executeGtSubprocessCommand: aCommandName ]
on: Warning
do: [ :ex | ex resume ]
]

{ #category : 'executing' }
BioGtUnixSubprocess class >> executeCommand: aCommandName parameter: parameterName [
"See superimplementor's comment"

^ self executeGtSubprocessCommand: (String streamContents: [ :stream |
stream
<< aCommandName trimBoth;
space;
<< parameterName trimBoth ])
]

{ #category : 'private' }
BioGtUnixSubprocess class >> executeGtSubprocessCommand: aCommandName [
"Private - Execute aCommandName and answer a <String> with any output returned"

| subprocess |

subprocess := (self classFor: self providerClass) new
shellCommand: aCommandName;
runAndWait;
yourself.

^ (subprocess stderr ifNotEmpty: [ subprocess stderr ] ifEmpty: [ subprocess stdout ]) trimBoth
]

{ #category : 'executing' }
BioGtUnixSubprocess class >> executeMonitoring: aCommandName [
"See superimplementor's comment"

^ self executeCommand: aCommandName
]

{ #category : 'testing' }
BioGtUnixSubprocess class >> isPreferredAdapter [
"Private - See superimplementor's comment"

^ self isRunningInUnix
]

{ #category : 'accessing' }
BioGtUnixSubprocess class >> locateProgram: programName [
"Answer a <String> with the full qualified path to programName if can be located by the which command"

^ self programLocation isEmpty
ifTrue: [ self locateProgramNamed: programName ]
ifFalse: [ self programLocation ]
]

{ #category : 'accessing' }
BioGtUnixSubprocess class >> locateProgramNamed: programName [
"Answer a <String> with the path to programName, if located, or an empty String otherwise"

| subprocess |

subprocess := GtSubprocessWithInMemoryOutput new
shellCommand: 'where ' , programName;
runAndWait;
yourself.

^ (subprocess stderr ifNotEmpty: [ subprocess stderr ] ifEmpty: [ subprocess stdout ]) trimBoth
]

{ #category : 'private' }
BioGtUnixSubprocess class >> providerClass [
"Private - See superimplementor's comment"

^ #GtUnixSubprocess
]
Loading