adjustmentMapMock = $this->createMock(TimerAdjustmentMapInterface::class); $this->timerMock = $this->mockQtiTimer($this->adjustmentMapMock); $this->testSessionMock = $this->mockTestSession($this->timerMock); $serviceLocatorMock = $this->getServiceLocatorMock([ StorageManager::SERVICE_ID => $this->createMock(StorageManager::class), ]); $this->subject = new TimerAdjustmentService(); $this->subject->setServiceLocator($serviceLocatorMock); } public function testGetAdjustment(): void { $source = $this->createMock(QtiIdentifiable::class); $source->method('getIdentifier')->willReturn('PHPUnitItemId'); $qtiTimer = $this->createMock(QtiTimer::class); $adjustmentMap = $this->createMock(AdjustmentMap::class); $adjustmentMap->method('get')->willReturn(10); $qtiTimer->method('getAdjustmentMap')->willReturn($adjustmentMap); $service = new TimerAdjustmentService(); $this->assertSame(10, $service->getAdjustment($source, $qtiTimer)); } public function testIncrease_AppliesIncreaseToGivenSource(): void { $increaseValue = 10; $increaseType = 'DUMMY_TYPE'; $sourceId = 'DUMMY_SOURCE_ID'; $source = $this->mockTimedComponent(SectionPart::class, $sourceId, true); $this->adjustmentMapMock->expects(self::once()) ->method('increase') ->with($sourceId, $increaseType, $increaseValue); $result = $this->subject->increase( $this->testSessionMock, $increaseValue, $increaseType, $source ); self::assertTrue($result, 'Method must return correct response in case of success.'); } public function testDecrease_AppliesIncreaseToGivenSource_WithCorrectMaximumDecreaseValue(): void { $increaseValue = 150; $increaseType = 'DUMMY_TYPE'; $sourceId = 'DUMMY_SOURCE_ID'; $source = $this->mockTimedComponent(SectionPart::class, $sourceId, true); $constraintRemainingTime = 100; $timeConstraintsMock = $this->mockTimeConstraint($sourceId, $constraintRemainingTime); $this->testSessionMock->method('getTimeConstraints') ->willReturn([$timeConstraintsMock]); $this->adjustmentMapMock->expects(self::once()) ->method('decrease') ->with($sourceId, $increaseType, $constraintRemainingTime); $result = $this->subject->decrease( $this->testSessionMock, $increaseValue, $increaseType, $source ); self::assertTrue($result, 'Method must return correct response in case of successful time decrease.'); } public function testIncrease_SourceDoesntHaveMaxTimeLimit(): void { $increaseValue = 5; $increaseType = 'DUMMY_TYPE'; $sourceId = 'DUMMY_SOURCE_ID'; $source = $this->mockTimedComponent(SectionPart::class, $sourceId, false); $this->adjustmentMapMock->expects(self::never()) ->method('increase'); $result = $this->subject->increase( $this->testSessionMock, $increaseValue, $increaseType, $source ); self::isNull($result, 'Method must return correct response if source doesn\'t have max time limit.'); } public function testIncrease_WithoutSourceIdAppliesToAllSources(): void { $increaseValue = 15; $increaseType = 'DUMMY_TYPE'; // Mock tests session to return timed components. $itemRef = $this->mockTimedComponent(AssessmentItemRef::class, 'DUMMY_SOURCE_ID', true); $assessmentSection = $this->mockTimedComponent(SectionPart::class, 'DUMMY_SECTION_ID', true); $assessmentPart = $this->mockTimedComponent(TestPart::class, 'DUMMY_PART_ID', true); $assessmentTest = $this->mockTimedComponent(AssessmentTest::class, 'DUMMY_TEST_ID', true); $this->testSessionMock->method('getCurrentAssessmentItemRef') ->willReturn($itemRef); $this->testSessionMock->method('getCurrentAssessmentSection') ->willReturn($assessmentSection); $this->testSessionMock->method('getCurrentTestPart') ->willReturn($assessmentPart); $this->testSessionMock->method('getAssessmentTest') ->willReturn($assessmentTest); // Assert that time adjustment is stored for each component. $this->adjustmentMapMock->expects(self::exactly(4)) ->method('increase') ->withConsecutive( ['DUMMY_SOURCE_ID', $increaseType, $increaseValue], ['DUMMY_SECTION_ID', $increaseType, $increaseValue], ['DUMMY_PART_ID', $increaseType, $increaseValue], ['DUMMY_TEST_ID', $increaseType, $increaseValue] ); $result = $this->subject->increase( $this->testSessionMock, $increaseValue, $increaseType ); self::assertTrue( $result, 'Method must return correct response in case of success when source is not provided.' ); } /** * @param string $class * @param string $identifier * @param bool $hasMaxLimit * @return QtiIdentifiable|MockObject */ private function mockTimedComponent(string $class, string $identifier, bool $hasMaxLimit): QtiIdentifiable { $timeLimitsMock = $this->createMock(TimeLimits::class); $timeLimitsMock->method('hasMaxTime') ->willReturn($hasMaxLimit); $timedComponentMock = $this->createMock($class); $timedComponentMock->method('getTimeLimits') ->willReturn($timeLimitsMock); $timedComponentMock->method('getIdentifier') ->willReturn($identifier); return $timedComponentMock; } /** * @return QtiTimer|MockObject */ private function mockQtiTimer(TimerAdjustmentMapInterface $adjustmentMap): QtiTimer { $qtiTimerMock = $this->createMock(QtiTimer::class); $qtiTimerMock->method('getAdjustmentMap') ->willReturn($adjustmentMap); return $qtiTimerMock; } /** * @return TestSession|MockObject */ private function mockTestSession(QtiTimer $qtiTimer): TestSession { $testSessionMock = $this->createMock(TestSession::class); $testSessionMock->method('getTimer') ->willReturn($qtiTimer); return $testSessionMock; } /** * @param string $sourceId * @param int $maximumRemainingTime * @return QtiTimeConstraint|MockObject */ private function mockTimeConstraint(string $sourceId, int $maximumRemainingTime): QtiTimeConstraint { $durationMock = $this->createMock(QtiDuration::class); $durationMock->method('getSeconds') ->willReturn($maximumRemainingTime); $sourceMock = $this->mockTimedComponent(SectionPart::class, $sourceId, true); $qtiTimeConstraintMock = $this->createMock(QtiTimeConstraint::class); $qtiTimeConstraintMock->method('getMaximumRemainingTime') ->willReturn($durationMock); $qtiTimeConstraintMock->method('getSource') ->willReturn($sourceMock); return $qtiTimeConstraintMock; } }