SystemOrganization addCategory: #'Gofer-Tests'! TestResource subclass: #GoferResource instanceVariableNames: 'versionReferences monticelloRepository' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Tests'! !GoferResource methodsFor: 'accessing' stamp: 'lr 12/12/2009 10:37'! monticelloRepository ^ monticelloRepository! ! !GoferResource methodsFor: 'running' stamp: 'lr 12/13/2009 16:38'! setUp super setUp. self setUpVersionReferences; setUpMonticelloRepository! ! !GoferResource methodsFor: 'running' stamp: 'lr 12/13/2009 17:25'! setUpMonticelloRepository "This method builds a fake repository with the version references from #buildReferences." monticelloRepository := MCDictionaryRepository new. versionReferences do: [ :reference | monticelloRepository basicStoreVersion: (MCVersion new setPackage: (MCPackage new name: reference packageName) info: (MCVersionInfo name: reference name id: UUID new message: 'This is a mock version' date: Date today time: Time now author: reference author ancestors: #()) snapshot: (MCSnapshot fromDefinitions: (Array with: (MCOrganizationDefinition categories: (Array with: reference packageName asSymbol)) with: (MCClassDefinition name: reference packageName asSymbol superclassName: #Object category: reference packageName asSymbol instVarNames: #() comment: ''))) dependencies: #()) ]! ! !GoferResource methodsFor: 'running' stamp: 'lr 12/13/2009 17:25'! setUpVersionReferences "This method answers a set of Gofer references in the order they should be sorted. It includes two different packages (Gofer-Foo, Gofer-Bar), linear series of packages (Gofer-Foo-lr.1, Gofer-Foo-lr.2, Gofer-Foo-lr.4), packages with a branch (Gofer-Bar-lr.branch.1,Gofer-Bar-lr.branch.2), and packages with the same version but different authors (Gofer-Bar-jf.1, Gofer-Bar-lr.1)." versionReferences := OrderedCollection new. versionReferences add: (GoferVersionReference name: 'GoferBar-lr.branch.1'); add: (GoferVersionReference name: 'GoferBar-lr.branch.2'); add: (GoferVersionReference name: 'GoferBar-jf.1'); add: (GoferVersionReference name: 'GoferBar-lr.1'); add: (GoferVersionReference name: 'GoferFoo-lr.1'); add: (GoferVersionReference name: 'GoferFoo-lr.2'); add: (GoferVersionReference name: 'GoferFoo-lr.4')! ! !GoferResource methodsFor: 'accessing' stamp: 'lr 12/12/2009 10:37'! versionReferences ^ versionReferences! ! TestCase subclass: #GoferTest instanceVariableNames: 'gofer' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Tests'! GoferTest subclass: #GoferApiTest instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Tests'! !GoferApiTest methodsFor: 'utilities' stamp: 'TestRunner 12/13/2009 16:50'! assert: aGofer repositories: anArray self assert: aGofer repositories size = anArray size. aGofer repositories with: anArray do: [ :first :second | self assert: first description = second ]! ! !GoferApiTest methodsFor: 'testing-references' stamp: 'lr 12/13/2009 17:25'! testConstraintReference gofer repository: self monticelloRepository; package: 'GoferBar' constraint: [ :ref | ref branch = 'branch' ]. self assert: (gofer resolved size = 1). self assert: (gofer resolved first isKindOf: GoferResolvedReference). self assert: (gofer resolved first packageName = 'GoferBar'). self assert: (gofer resolved first author = 'lr'). self assert: (gofer resolved first branch = 'branch'). self assert: (gofer resolved first versionNumber = 2). self assert: (gofer resolved first repository = self monticelloRepository)! ! !GoferApiTest methodsFor: 'testing-repositories-places' stamp: 'lr 12/13/2009 13:36'! testCroquet gofer croquet: 'Hermes'. self assert: gofer repositories: #('http://hedgehog.software.umn.edu:8888/Hermes')! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'TestRunner 12/13/2009 16:51'! testCustomRepository gofer repository: self monticelloRepository. self assert: gofer repositories: (Array with: self monticelloRepository description). self assert: (gofer repositories first = self monticelloRepository)! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 12/13/2009 16:51'! testDirectoryRepository gofer directory: FileDirectory default pathName. self assert: gofer repositories: (Array with: FileDirectory default pathName). self assert: (gofer repositories first isKindOf: MCDirectoryRepository)! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'dkh 12/15/2009 10:44'! testFtpRepository Smalltalk at: #MCFtpRepository ifPresent: [:ftpRepositoryClass | gofer url: 'ftp://squeakfoundation.org/39a' username: 'foo' password: 'bar'. self assert: gofer repositories: #('ftp://foo@squeakfoundation.org/39a'). self assert: (gofer repositories first isKindOf: ftpRepositoryClass) ]! ! !GoferApiTest methodsFor: 'testing-repositories-places' stamp: 'lr 12/13/2009 13:36'! testGemsource gofer gemsource: 'Seaside29'. self assert: gofer repositories: #('http://seaside.gemstone.com/ss/Seaside29')! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 12/13/2009 16:51'! testHttpRepository gofer url: 'http://source.lukas-renggli.ch/pier' username: 'foo' password: 'bar'. self assert: gofer repositories: #('http://source.lukas-renggli.ch/pier'). self assert: (gofer repositories first isKindOf: MCHttpRepository)! ! !GoferApiTest methodsFor: 'testing-repositories-places' stamp: 'lr 12/13/2009 13:36'! testImpara gofer impara: 'Tweak'. self assert: gofer repositories: #('http://source.impara.de/Tweak')! ! !GoferApiTest methodsFor: 'testing' stamp: 'lr 12/13/2009 17:40'! testInitialReferences self assert: gofer references isEmpty! ! !GoferApiTest methodsFor: 'testing' stamp: 'dkh 12/15/2009 11:26'! testInitialRepositories gofer := Gofer new. self assert: (gofer repositories size = 1). self assert: (gofer repositories first isKindOf: MCCacheRepository default class)! ! !GoferApiTest methodsFor: 'testing-repositories-options' stamp: 'lr 12/13/2009 17:44'! testPackageCache gofer squeaksource: 'r1'; squeaksource: 'r2'. gofer enablePackageCache. self assert: gofer repositories: (Array with: MCCacheRepository default description) , #('http://www.squeaksource.com/r1' 'http://www.squeaksource.com/r2'). gofer disablePackageCache. self assert: gofer repositories: #('http://www.squeaksource.com/r1' 'http://www.squeaksource.com/r2')! ! !GoferApiTest methodsFor: 'testing-references' stamp: 'lr 12/13/2009 17:27'! testPackageReference gofer repository: self monticelloRepository; package: 'GoferFoo'. self assert: (gofer resolved size = 1). self assert: (gofer resolved first isKindOf: GoferResolvedReference). self assert: (gofer resolved first packageName = 'GoferFoo'). self assert: (gofer resolved first author = 'lr'). self assert: (gofer resolved first branch isEmpty). self assert: (gofer resolved first versionNumber = 4). self assert: (gofer resolved first repository = self monticelloRepository)! ! !GoferApiTest methodsFor: 'testing-repositories-places' stamp: 'lr 12/13/2009 13:36'! testRenggli gofer renggli: 'pier'. self assert: gofer repositories: #('http://source.lukas-renggli.ch/pier')! ! !GoferApiTest methodsFor: 'testing-repositories-options' stamp: 'dkh 12/15/2009 11:24'! testRepositoryErrors gofer url: 'http://www.google.com'; repository: self monticelloRepository. gofer package: 'GoferFoo'. gofer enableRepositoryErrors. self should: [ gofer resolved ] raise: GoferRepositoryError. gofer disableRepositoryErrors. self shouldnt: [ gofer resolved ] raise: GoferRepositoryError! ! !GoferApiTest methodsFor: 'testing-repositories-places' stamp: 'lr 12/13/2009 13:36'! testSaltypickle gofer saltypickle: 'GraphViz'. self assert: gofer repositories: #('http://squeak.saltypickle.com/GraphViz')! ! !GoferApiTest methodsFor: 'testing-repositories-places' stamp: 'lr 12/13/2009 13:36'! testSqueakfoundation gofer squeakfoundation: '39a'. self assert: gofer repositories: #('http://source.squeakfoundation.org/39a')! ! !GoferApiTest methodsFor: 'testing-repositories-places' stamp: 'lr 12/13/2009 13:36'! testSqueaksource gofer squeaksource: 'Seaside29'. self assert: gofer repositories: #('http://www.squeaksource.com/Seaside29')! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'dkh 12/15/2009 10:44'! testSubDirectoryRepository Smalltalk at: #MCSubDirectoryRepository ifPresent: [:subDirectoryRepositoryClass | gofer directory: FileDirectory default pathName , FileDirectory slash , '*'. self assert: gofer repositories: (Array with: FileDirectory default pathName , FileDirectory slash , '*'). self assert: (gofer repositories first isKindOf: subDirectoryRepositoryClass) ]! ! !GoferApiTest methodsFor: 'testing-references' stamp: 'lr 12/13/2009 17:27'! testVersionReference gofer repository: self monticelloRepository; version: 'GoferFoo-lr.2'. self assert: (gofer resolved size = 1). self assert: (gofer resolved first isKindOf: GoferResolvedReference). self assert: (gofer resolved first packageName = 'GoferFoo'). self assert: (gofer resolved first author = 'lr'). self assert: (gofer resolved first branch isEmpty). self assert: (gofer resolved first versionNumber = 2). self assert: (gofer resolved first repository = self monticelloRepository)! ! !GoferApiTest methodsFor: 'testing-repositories-places' stamp: 'lr 12/13/2009 13:36'! testWiresong gofer wiresong: 'ob'. self assert: gofer repositories: #('http://source.wiresong.ca/ob')! ! GoferTest subclass: #GoferOperationTest instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Tests'! !GoferOperationTest methodsFor: 'utilities' stamp: 'lr 12/13/2009 17:52'! hasClass: aSymbol ^ Smalltalk includesKey: aSymbol! ! !GoferOperationTest methodsFor: 'utilities' stamp: 'lr 12/13/2009 18:21'! hasClass: aSymbol selector: aSelector ^ (Smalltalk classNamed: aSymbol) includesSelector: aSelector! ! !GoferOperationTest methodsFor: 'utilities' stamp: 'lr 8/20/2009 21:14'! hasPackage: aString | package | package := MCWorkingCopy allManagers detect: [ :each | each packageName = aString ] ifNone: [ nil ]. ^ package notNil! ! !GoferOperationTest methodsFor: 'utilities' stamp: 'TestRunner 12/13/2009 17:56'! hasVersion: aString | version | version := MCWorkingCopy allManagers detect: [ :each | each ancestry ancestorString = aString ] ifNone: [ nil ]. ^ version notNil! ! !GoferOperationTest methodsFor: 'running' stamp: 'lr 12/13/2009 17:49'! setUp super setUp. gofer repository: self monticelloRepository! ! !GoferOperationTest methodsFor: 'running' stamp: 'lr 12/13/2009 17:50'! tearDown (self hasPackage: 'GoferFoo') ifTrue: [ Gofer new package: 'GoferFoo'; unload ]. (self hasPackage: 'GoferBar') ifTrue: [ Gofer new package: 'GoferBar'; unload ]! ! !GoferOperationTest methodsFor: 'testing' stamp: 'lr 12/13/2009 19:45'! testCleanup gofer package: 'GoferFoo'; package: 'GoferBar'; load. self shouldnt: [ gofer cleanup ] raise: Error! ! !GoferOperationTest methodsFor: 'testing' stamp: 'TestRunner 12/13/2009 19:02'! testCommit | repository | repository := MCDictionaryRepository new. gofer package: 'GoferFoo'; package: 'GoferBar'; load. gofer := Gofer new. gofer disablePackageCache. gofer repository: repository. gofer package: 'GoferFoo'; package: 'GoferBar'. self shouldnt: [ gofer commit: 'A test commit' ] raise: Error. self assert: repository allVersionInfos size = 2! ! !GoferOperationTest methodsFor: 'testing' stamp: 'dkh 12/15/2009 11:06'! testFetch gofer package: 'GoferFoo'; package: 'GoferBar'. self shouldnt: [ gofer fetch ] raise: Error! ! !GoferOperationTest methodsFor: 'testing' stamp: 'TestRunner 12/13/2009 18:19'! testLoad gofer version: 'GoferFoo-lr.1'; version: 'GoferBar-jf.1'. self shouldnt: [ gofer load ] raise: Error. self assert: (self hasVersion: 'GoferFoo-lr.1'). self assert: (self hasClass: #GoferFoo). self assert: (self hasVersion: 'GoferBar-jf.1'). self assert: (self hasClass: #GoferBar)! ! !GoferOperationTest methodsFor: 'testing' stamp: 'lr 12/14/2009 23:54'! testLocalChanges | changes | gofer package: 'GoferFoo'; package: 'GoferBar'; load. (Smalltalk classNamed: #GoferBar) compile: 'foo'. self shouldnt: [ changes := gofer localChanges ] raise: Error. self assert: changes operations size = 1! ! !GoferOperationTest methodsFor: 'testing' stamp: 'lr 12/13/2009 19:04'! testMerge | initial | initial := gofer copy. initial version: 'GoferFoo-lr.1'; version: 'GoferBar-jf.1'; load. gofer package: 'GoferFoo'; package: 'GoferBar'; load. (Smalltalk classNamed: #GoferBar) compile: 'foo'. self shouldnt: [ gofer merge ] raise: Error. self assert: (self hasClass: #GoferBar selector: #foo)! ! !GoferOperationTest methodsFor: 'testing' stamp: 'TestRunner 12/13/2009 20:11'! testPush | repository | gofer := Gofer new. gofer disablePackageCache. gofer repository: (repository := MCDictionaryRepository new). gofer package: 'GoferFoo'; package: 'GoferBar'. self shouldnt: [ gofer push ] raise: Error! ! !GoferOperationTest methodsFor: 'testing' stamp: 'lr 1/5/2010 11:03'! testRecompile gofer package: 'Gofer-Core'. self shouldnt: [ gofer recompile ] raise: Error! ! !GoferOperationTest methodsFor: 'testing' stamp: 'lr 1/5/2010 11:03'! testReinitialize gofer package: 'Gofer-Core'. self shouldnt: [ gofer reinitialize ] raise: Error! ! !GoferOperationTest methodsFor: 'testing' stamp: 'lr 12/14/2009 23:54'! testRemoteChanges | changes | gofer package: 'GoferFoo'; package: 'GoferBar'; load. (Smalltalk classNamed: #GoferBar) compile: 'foo'. self shouldnt: [ changes := gofer remoteChanges ] raise: Error. self assert: changes operations size = 1! ! !GoferOperationTest methodsFor: 'testing' stamp: 'dkh 12/15/2009 11:27'! testRevert gofer package: 'GoferFoo'; package: 'GoferBar'; load. (Smalltalk classNamed: #GoferBar) category: 'GoferFoo'. self shouldnt: [ gofer revert ] raise: Error. self assert: (Smalltalk classNamed: #GoferFoo) category asSymbol = #GoferFoo. self assert: (Smalltalk classNamed: #GoferBar) category asSymbol = #GoferBar! ! !GoferOperationTest methodsFor: 'testing' stamp: 'TestRunner 12/13/2009 19:07'! testUnload gofer package: 'GoferFoo'; package: 'GoferBar'; load. self shouldnt: [ gofer unload ] raise: Error. self deny: (self hasPackage: 'GoferFoo'); deny: (self hasClass: #GoferFoo). self deny: (self hasPackage: 'GoferBar'); deny: (self hasClass: #GoferBar)! ! !GoferOperationTest methodsFor: 'testing' stamp: 'lr 12/13/2009 18:17'! testUpdate | initial | initial := gofer copy. initial version: 'GoferFoo-lr.1'; version: 'GoferBar-jf.1'; load. gofer package: 'GoferFoo'; package: 'GoferBar'. self shouldnt: [ gofer update ] raise: Error. self assert: (self hasVersion: 'GoferFoo-lr.4'). self assert: (self hasVersion: 'GoferBar-lr.1')! ! GoferTest subclass: #GoferReferenceTest instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Tests'! !GoferReferenceTest methodsFor: 'running' stamp: 'lr 12/13/2009 17:53'! setUp super setUp. gofer repository: self monticelloRepository! ! !GoferReferenceTest methodsFor: 'testing-reference' stamp: 'TestRunner 12/13/2009 17:30'! testContraintShouldFindLatestVersion | constraintReference reference | constraintReference := GoferConstraintReference name: 'GoferBar' constraint: [ :ref | true ]. self assert: (constraintReference resolveAllWith: gofer) size = 4. reference := constraintReference resolveWith: gofer. self assert: reference packageName = 'GoferBar'. self assert: reference author = 'lr'. self assert: reference branch isEmpty. self assert: reference versionNumber = 1. self assert: reference repository = self monticelloRepository. constraintReference := GoferConstraintReference name: 'GoferBar' constraint: [ :ref | ref branch = 'branch' ]. self assert: (constraintReference resolveAllWith: gofer) size = 2. reference := constraintReference resolveWith: gofer. self assert: reference packageName = 'GoferBar'. self assert: reference author = 'lr'. self assert: reference branch = 'branch'. self assert: reference versionNumber = 2. self assert: reference repository = self monticelloRepository. constraintReference := GoferConstraintReference name: 'GoferBar' constraint: [ :ref | ref author = 'jf' ]. self assert: (constraintReference resolveAllWith: gofer) size = 1. reference := constraintReference resolveWith: gofer. self assert: reference packageName = 'GoferBar'. self assert: reference author = 'jf'. self assert: reference branch isEmpty. self assert: reference versionNumber = 1. self assert: reference repository = self monticelloRepository. constraintReference := GoferConstraintReference name: 'GoferBar' constraint: [ :ref | false ]. self assert: (constraintReference resolveAllWith: gofer) isEmpty. self should: [ constraintReference resolveWith: gofer ] raise: Error.! ! !GoferReferenceTest methodsFor: 'testing-working' stamp: 'lr 1/5/2010 11:06'! testContraintShouldFindWorkingCopy | constraintReference workingCopy | constraintReference := GoferConstraintReference name: 'Gofer-Core' constraint: [ :reference | false ]. workingCopy := constraintReference workingCopy. self assert: workingCopy packageName = 'Gofer-Core'! ! !GoferReferenceTest methodsFor: 'testing' stamp: 'lr 12/13/2009 17:27'! testLoadableShouldSortCorrectly | sorted | sorted := self versionReferences collect: [ :each | each resolveWith: gofer ]. sorted withIndexDo: [ :first :firstIndex | sorted withIndexDo: [ :second :secondIndex | firstIndex <= secondIndex ifTrue: [ self assert: first <= second ]. firstIndex >= secondIndex ifTrue: [ self assert: second <= first ] ] ]! ! !GoferReferenceTest methodsFor: 'testing-reference' stamp: 'lr 12/13/2009 17:29'! testPackageShouldFindLatestVersion | packageReference reference | packageReference := GoferPackageReference name: 'GoferFoo'. reference := packageReference resolveWith: gofer. self assert: reference packageName = 'GoferFoo'. self assert: reference author = 'lr'. self assert: reference branch isEmpty. self assert: reference versionNumber = 4. self assert: reference repository = self monticelloRepository! ! !GoferReferenceTest methodsFor: 'testing-working' stamp: 'lr 1/5/2010 11:03'! testPackageShouldFindWorkingCopy | packageReference workingCopy | packageReference := GoferPackageReference name: 'Gofer-Core'. workingCopy := packageReference workingCopy. self assert: workingCopy packageName = 'Gofer-Core'! ! !GoferReferenceTest methodsFor: 'testing-reference' stamp: 'lr 12/13/2009 17:28'! testResolvedShouldFindLatestVersion | versionReference reference | versionReference := GoferResolvedReference name: 'GoferFoo-lr.2' repository: self monticelloRepository. reference := versionReference resolveWith: gofer. self assert: reference packageName = 'GoferFoo'. self assert: reference author = 'lr'. self assert: reference branch isEmpty. self assert: reference versionNumber = 2. self assert: reference repository = self monticelloRepository! ! !GoferReferenceTest methodsFor: 'testing-working' stamp: 'lr 1/5/2010 11:08'! testResolvedShouldFindWorkingCopy | versionReference workingCopy | versionReference := GoferResolvedReference name: 'Gofer-Core-lr.18' repository: self monticelloRepository. workingCopy := versionReference workingCopy. self assert: workingCopy packageName = 'Gofer-Core'! ! !GoferReferenceTest methodsFor: 'testing-reference' stamp: 'lr 12/13/2009 17:29'! testVersionShouldFindLatestVersion | versionReference reference | versionReference := GoferVersionReference name: 'GoferFoo-lr.2'. reference := versionReference resolveWith: gofer. self assert: reference packageName = 'GoferFoo'. self assert: reference author = 'lr'. self assert: reference versionNumber = 2. self assert: reference branch isEmpty. self assert: reference repository = self monticelloRepository. versionReference := GoferVersionReference name: 'GoferFoo-lr.3'. self should: [ versionReference resolveWith: gofer ] raise: Error! ! !GoferReferenceTest methodsFor: 'testing-working' stamp: 'lr 1/5/2010 11:08'! testVersionShouldFindWorkingCopy | versionReference workingCopy | versionReference := GoferVersionReference name: 'Gofer-Core-lr.18'. workingCopy := versionReference workingCopy. self assert: workingCopy packageName = 'Gofer-Core'! ! !GoferReferenceTest methodsFor: 'testing' stamp: 'lr 12/13/2009 17:22'! testVersionShouldParseComplexName | queryReference | queryReference := GoferVersionReference name: 'Seaside2.8b5'. self assert: queryReference packageName = 'Seaside2.8b5'. self assert: queryReference author isEmpty. self assert: queryReference branch isEmpty. self assert: queryReference versionNumber = 0. queryReference := GoferVersionReference name: 'Seaside2.8b5-avi.1'. self assert: queryReference packageName = 'Seaside2.8b5'. self assert: queryReference author = 'avi'. self assert: queryReference branch isEmpty. self assert: queryReference versionNumber = 1. queryReference := GoferVersionReference name: 'Seaside-Core-pmm.2'. self assert: queryReference packageName = 'Seaside-Core'. self assert: queryReference author = 'pmm'. self assert: queryReference branch isEmpty. self assert: queryReference versionNumber = 2. queryReference := GoferVersionReference name: 'Seaside-Core-jf.configcleanup.3'. self assert: queryReference packageName = 'Seaside-Core'. self assert: queryReference author = 'jf'. self assert: queryReference branch = 'configcleanup'. self assert: queryReference versionNumber = 3. queryReference := GoferVersionReference name: 'Seaside-Core-lr.configcleanup.extraspeedup.69'. self assert: queryReference packageName = 'Seaside-Core'. self assert: queryReference author = 'lr'. self assert: queryReference branch = 'configcleanup.extraspeedup'. self assert: queryReference versionNumber = 69. queryReference := GoferVersionReference name: 'Seaside-Core-lr.configcleanup42.extraspeedup.69'. self assert: queryReference packageName = 'Seaside-Core'. self assert: queryReference author = 'lr'. self assert: queryReference branch = 'configcleanup42.extraspeedup'. self assert: queryReference versionNumber = 69 ! ! !GoferTest class methodsFor: 'testing' stamp: 'lr 10/1/2009 22:00'! isAbstract ^ self name = #GoferTest! ! !GoferTest class methodsFor: 'accessing' stamp: 'lr 1/5/2010 11:06'! packageNamesUnderTest ^ #('Gofer-Core')! ! !GoferTest class methodsFor: 'accessing' stamp: 'lr 12/11/2009 23:54'! resources ^ Array with: GoferResource! ! !GoferTest methodsFor: 'accessing' stamp: 'lr 12/12/2009 10:44'! monticelloRepository ^ GoferResource current monticelloRepository! ! !GoferTest methodsFor: 'running' stamp: 'lr 12/13/2009 17:38'! setUp super setUp. gofer := Gofer new. gofer disablePackageCache! ! !GoferTest methodsFor: 'accessing' stamp: 'lr 12/12/2009 10:46'! versionReferences ^ GoferResource current versionReferences! !