diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index c8bfd3f4..5235f69a 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1,382 +1,380 @@ 2, 'class' => array( 'ArcanistAliasWorkflow' => 'workflow/ArcanistAliasWorkflow.php', 'ArcanistAmendWorkflow' => 'workflow/ArcanistAmendWorkflow.php', 'ArcanistAnoidWorkflow' => 'workflow/ArcanistAnoidWorkflow.php', 'ArcanistBackoutWorkflow' => 'workflow/ArcanistBackoutWorkflow.php', 'ArcanistBaseCommitParser' => 'parser/ArcanistBaseCommitParser.php', 'ArcanistBaseCommitParserTestCase' => 'parser/__tests__/ArcanistBaseCommitParserTestCase.php', 'ArcanistBaseXHPASTLinter' => 'lint/linter/ArcanistBaseXHPASTLinter.php', 'ArcanistBookmarkWorkflow' => 'workflow/ArcanistBookmarkWorkflow.php', 'ArcanistBranchWorkflow' => 'workflow/ArcanistBranchWorkflow.php', 'ArcanistBritishTestCase' => 'configuration/__tests__/ArcanistBritishTestCase.php', 'ArcanistBrowseWorkflow' => 'workflow/ArcanistBrowseWorkflow.php', 'ArcanistBundle' => 'parser/ArcanistBundle.php', 'ArcanistBundleTestCase' => 'parser/__tests__/ArcanistBundleTestCase.php', 'ArcanistCSSLintLinter' => 'lint/linter/ArcanistCSSLintLinter.php', 'ArcanistCSSLintLinterTestCase' => 'lint/linter/__tests__/ArcanistCSSLintLinterTestCase.php', 'ArcanistCSharpLinter' => 'lint/linter/ArcanistCSharpLinter.php', 'ArcanistCallConduitWorkflow' => 'workflow/ArcanistCallConduitWorkflow.php', 'ArcanistCapabilityNotSupportedException' => 'workflow/exception/ArcanistCapabilityNotSupportedException.php', 'ArcanistCheckstyleXMLLintRenderer' => 'lint/renderer/ArcanistCheckstyleXMLLintRenderer.php', 'ArcanistChmodLinter' => 'lint/linter/ArcanistChmodLinter.php', 'ArcanistChmodLinterTestCase' => 'lint/linter/__tests__/ArcanistChmodLinterTestCase.php', 'ArcanistCloseRevisionWorkflow' => 'workflow/ArcanistCloseRevisionWorkflow.php', 'ArcanistCloseWorkflow' => 'workflow/ArcanistCloseWorkflow.php', 'ArcanistClosureLinter' => 'lint/linter/ArcanistClosureLinter.php', 'ArcanistClosureLinterTestCase' => 'lint/linter/__tests__/ArcanistClosureLinterTestCase.php', 'ArcanistCoffeeLintLinter' => 'lint/linter/ArcanistCoffeeLintLinter.php', 'ArcanistCoffeeLintLinterTestCase' => 'lint/linter/__tests__/ArcanistCoffeeLintLinterTestCase.php', 'ArcanistCommentRemover' => 'parser/ArcanistCommentRemover.php', 'ArcanistCommentRemoverTestCase' => 'parser/__tests__/ArcanistCommentRemoverTestCase.php', 'ArcanistCommitWorkflow' => 'workflow/ArcanistCommitWorkflow.php', 'ArcanistCompilerLintRenderer' => 'lint/renderer/ArcanistCompilerLintRenderer.php', 'ArcanistComprehensiveLintEngine' => 'lint/engine/ArcanistComprehensiveLintEngine.php', 'ArcanistConfiguration' => 'configuration/ArcanistConfiguration.php', 'ArcanistConfigurationDrivenLintEngine' => 'lint/engine/ArcanistConfigurationDrivenLintEngine.php', 'ArcanistConfigurationManager' => 'configuration/ArcanistConfigurationManager.php', 'ArcanistConsoleLintRenderer' => 'lint/renderer/ArcanistConsoleLintRenderer.php', 'ArcanistCoverWorkflow' => 'workflow/ArcanistCoverWorkflow.php', 'ArcanistCppcheckLinter' => 'lint/linter/ArcanistCppcheckLinter.php', 'ArcanistCppcheckLinterTestCase' => 'lint/linter/__tests__/ArcanistCppcheckLinterTestCase.php', 'ArcanistCpplintLinter' => 'lint/linter/ArcanistCpplintLinter.php', 'ArcanistCpplintLinterTestCase' => 'lint/linter/__tests__/ArcanistCpplintLinterTestCase.php', 'ArcanistDiffChange' => 'parser/diff/ArcanistDiffChange.php', 'ArcanistDiffChangeType' => 'parser/diff/ArcanistDiffChangeType.php', 'ArcanistDiffHunk' => 'parser/diff/ArcanistDiffHunk.php', 'ArcanistDiffParser' => 'parser/ArcanistDiffParser.php', 'ArcanistDiffParserTestCase' => 'parser/__tests__/ArcanistDiffParserTestCase.php', 'ArcanistDiffUtils' => 'difference/ArcanistDiffUtils.php', 'ArcanistDiffUtilsTestCase' => 'difference/__tests__/ArcanistDiffUtilsTestCase.php', 'ArcanistDiffWorkflow' => 'workflow/ArcanistDiffWorkflow.php', 'ArcanistDifferentialCommitMessage' => 'differential/ArcanistDifferentialCommitMessage.php', 'ArcanistDifferentialCommitMessageParserException' => 'differential/ArcanistDifferentialCommitMessageParserException.php', 'ArcanistDifferentialDependencyGraph' => 'differential/ArcanistDifferentialDependencyGraph.php', 'ArcanistDifferentialRevisionHash' => 'differential/constants/ArcanistDifferentialRevisionHash.php', 'ArcanistDifferentialRevisionStatus' => 'differential/constants/ArcanistDifferentialRevisionStatus.php', 'ArcanistDownloadWorkflow' => 'workflow/ArcanistDownloadWorkflow.php', 'ArcanistEventType' => 'events/constant/ArcanistEventType.php', 'ArcanistExportWorkflow' => 'workflow/ArcanistExportWorkflow.php', 'ArcanistExternalLinter' => 'lint/linter/ArcanistExternalLinter.php', 'ArcanistExternalLinterTestCase' => 'lint/linter/__tests__/ArcanistExternalLinterTestCase.php', 'ArcanistFeatureWorkflow' => 'workflow/ArcanistFeatureWorkflow.php', 'ArcanistFilenameLinter' => 'lint/linter/ArcanistFilenameLinter.php', 'ArcanistFilenameLinterTestCase' => 'lint/linter/__tests__/ArcanistFilenameLinterTestCase.php', 'ArcanistFlagWorkflow' => 'workflow/ArcanistFlagWorkflow.php', 'ArcanistFlake8Linter' => 'lint/linter/ArcanistFlake8Linter.php', 'ArcanistFlake8LinterTestCase' => 'lint/linter/__tests__/ArcanistFlake8LinterTestCase.php', 'ArcanistFutureLinter' => 'lint/linter/ArcanistFutureLinter.php', 'ArcanistGeneratedLinter' => 'lint/linter/ArcanistGeneratedLinter.php', 'ArcanistGeneratedLinterTestCase' => 'lint/linter/__tests__/ArcanistGeneratedLinterTestCase.php', 'ArcanistGetConfigWorkflow' => 'workflow/ArcanistGetConfigWorkflow.php', 'ArcanistGitAPI' => 'repository/api/ArcanistGitAPI.php', 'ArcanistGoLintLinter' => 'lint/linter/ArcanistGoLintLinter.php', 'ArcanistGoLintLinterTestCase' => 'lint/linter/__tests__/ArcanistGoLintLinterTestCase.php', 'ArcanistGoTestResultParser' => 'unit/parser/ArcanistGoTestResultParser.php', 'ArcanistGoTestResultParserTestCase' => 'unit/parser/__tests__/ArcanistGoTestResultParserTestCase.php', 'ArcanistHLintLinter' => 'lint/linter/ArcanistHLintLinter.php', 'ArcanistHLintLinterTestCase' => 'lint/linter/__tests__/ArcanistHLintLinterTestCase.php', 'ArcanistHelpWorkflow' => 'workflow/ArcanistHelpWorkflow.php', 'ArcanistHgClientChannel' => 'hgdaemon/ArcanistHgClientChannel.php', 'ArcanistHgProxyClient' => 'hgdaemon/ArcanistHgProxyClient.php', 'ArcanistHgProxyServer' => 'hgdaemon/ArcanistHgProxyServer.php', 'ArcanistHgServerChannel' => 'hgdaemon/ArcanistHgServerChannel.php', 'ArcanistInstallCertificateWorkflow' => 'workflow/ArcanistInstallCertificateWorkflow.php', 'ArcanistJSHintLinter' => 'lint/linter/ArcanistJSHintLinter.php', 'ArcanistJSHintLinterTestCase' => 'lint/linter/__tests__/ArcanistJSHintLinterTestCase.php', 'ArcanistJSONLintLinter' => 'lint/linter/ArcanistJSONLintLinter.php', 'ArcanistJSONLintLinterTestCase' => 'lint/linter/__tests__/ArcanistJSONLintLinterTestCase.php', 'ArcanistJSONLintRenderer' => 'lint/renderer/ArcanistJSONLintRenderer.php', 'ArcanistJSONLinter' => 'lint/linter/ArcanistJSONLinter.php', 'ArcanistJSONLinterTestCase' => 'lint/linter/__tests__/ArcanistJSONLinterTestCase.php', 'ArcanistJscsLinter' => 'lint/linter/ArcanistJscsLinter.php', 'ArcanistJscsLinterTestCase' => 'lint/linter/__tests__/ArcanistJscsLinterTestCase.php', 'ArcanistLandWorkflow' => 'workflow/ArcanistLandWorkflow.php', 'ArcanistLesscLinter' => 'lint/linter/ArcanistLesscLinter.php', 'ArcanistLesscLinterTestCase' => 'lint/linter/__tests__/ArcanistLesscLinterTestCase.php', 'ArcanistLiberateWorkflow' => 'workflow/ArcanistLiberateWorkflow.php', 'ArcanistLibraryTestCase' => '__tests__/ArcanistLibraryTestCase.php', 'ArcanistLintEngine' => 'lint/engine/ArcanistLintEngine.php', 'ArcanistLintMessage' => 'lint/ArcanistLintMessage.php', 'ArcanistLintPatcher' => 'lint/ArcanistLintPatcher.php', 'ArcanistLintRenderer' => 'lint/renderer/ArcanistLintRenderer.php', 'ArcanistLintResult' => 'lint/ArcanistLintResult.php', 'ArcanistLintSeverity' => 'lint/ArcanistLintSeverity.php', 'ArcanistLintWorkflow' => 'workflow/ArcanistLintWorkflow.php', 'ArcanistLinter' => 'lint/linter/ArcanistLinter.php', 'ArcanistLinterTestCase' => 'lint/linter/__tests__/ArcanistLinterTestCase.php', 'ArcanistLintersWorkflow' => 'workflow/ArcanistLintersWorkflow.php', 'ArcanistListWorkflow' => 'workflow/ArcanistListWorkflow.php', 'ArcanistMercurialAPI' => 'repository/api/ArcanistMercurialAPI.php', 'ArcanistMercurialParser' => 'repository/parser/ArcanistMercurialParser.php', 'ArcanistMercurialParserTestCase' => 'repository/parser/__tests__/ArcanistMercurialParserTestCase.php', 'ArcanistMergeConflictLinter' => 'lint/linter/ArcanistMergeConflictLinter.php', 'ArcanistMergeConflictLinterTestCase' => 'lint/linter/__tests__/ArcanistMergeConflictLinterTestCase.php', 'ArcanistMissingLinterException' => 'lint/linter/exception/ArcanistMissingLinterException.php', 'ArcanistNoEffectException' => 'exception/usage/ArcanistNoEffectException.php', 'ArcanistNoEngineException' => 'exception/usage/ArcanistNoEngineException.php', 'ArcanistNoLintLinter' => 'lint/linter/ArcanistNoLintLinter.php', 'ArcanistNoLintLinterTestCase' => 'lint/linter/__tests__/ArcanistNoLintLinterTestCase.php', 'ArcanistNoneLintRenderer' => 'lint/renderer/ArcanistNoneLintRenderer.php', 'ArcanistPEP8Linter' => 'lint/linter/ArcanistPEP8Linter.php', 'ArcanistPEP8LinterTestCase' => 'lint/linter/__tests__/ArcanistPEP8LinterTestCase.php', 'ArcanistPasteWorkflow' => 'workflow/ArcanistPasteWorkflow.php', 'ArcanistPatchWorkflow' => 'workflow/ArcanistPatchWorkflow.php', 'ArcanistPhpLinter' => 'lint/linter/ArcanistPhpLinter.php', 'ArcanistPhpLinterTestCase' => 'lint/linter/__tests__/ArcanistPhpLinterTestCase.php', 'ArcanistPhpcsLinter' => 'lint/linter/ArcanistPhpcsLinter.php', 'ArcanistPhpcsLinterTestCase' => 'lint/linter/__tests__/ArcanistPhpcsLinterTestCase.php', 'ArcanistPhpunitTestResultParser' => 'unit/parser/ArcanistPhpunitTestResultParser.php', 'ArcanistPhrequentWorkflow' => 'workflow/ArcanistPhrequentWorkflow.php', 'ArcanistPhutilLibraryLinter' => 'lint/linter/ArcanistPhutilLibraryLinter.php', - 'ArcanistPhutilTestCase' => 'unit/engine/phutil/ArcanistPhutilTestCase.php', - 'ArcanistPhutilTestCaseTestCase' => 'unit/engine/phutil/testcase/ArcanistPhutilTestCaseTestCase.php', - 'ArcanistPhutilTestSkippedException' => 'unit/engine/phutil/testcase/ArcanistPhutilTestSkippedException.php', - 'ArcanistPhutilTestTerminatedException' => 'unit/engine/phutil/testcase/ArcanistPhutilTestTerminatedException.php', 'ArcanistPhutilXHPASTLinter' => 'lint/linter/ArcanistPhutilXHPASTLinter.php', 'ArcanistPhutilXHPASTLinterTestCase' => 'lint/linter/__tests__/ArcanistPhutilXHPASTLinterTestCase.php', 'ArcanistPuppetLintLinter' => 'lint/linter/ArcanistPuppetLintLinter.php', 'ArcanistPuppetLintLinterTestCase' => 'lint/linter/__tests__/ArcanistPuppetLintLinterTestCase.php', 'ArcanistPyFlakesLinter' => 'lint/linter/ArcanistPyFlakesLinter.php', 'ArcanistPyFlakesLinterTestCase' => 'lint/linter/__tests__/ArcanistPyFlakesLinterTestCase.php', 'ArcanistPyLintLinter' => 'lint/linter/ArcanistPyLintLinter.php', 'ArcanistPyLintLinterTestCase' => 'lint/linter/__tests__/ArcanistPyLintLinterTestCase.php', 'ArcanistRepositoryAPI' => 'repository/api/ArcanistRepositoryAPI.php', 'ArcanistRepositoryAPIMiscTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIMiscTestCase.php', 'ArcanistRepositoryAPIStateTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php', 'ArcanistRevertWorkflow' => 'workflow/ArcanistRevertWorkflow.php', 'ArcanistRuboCopLinter' => 'lint/linter/ArcanistRuboCopLinter.php', 'ArcanistRuboCopLinterTestCase' => 'lint/linter/__tests__/ArcanistRuboCopLinterTestCase.php', 'ArcanistRubyLinter' => 'lint/linter/ArcanistRubyLinter.php', 'ArcanistRubyLinterTestCase' => 'lint/linter/__tests__/ArcanistRubyLinterTestCase.php', 'ArcanistScriptAndRegexLinter' => 'lint/linter/ArcanistScriptAndRegexLinter.php', 'ArcanistSetConfigWorkflow' => 'workflow/ArcanistSetConfigWorkflow.php', 'ArcanistSettings' => 'configuration/ArcanistSettings.php', 'ArcanistShellCompleteWorkflow' => 'workflow/ArcanistShellCompleteWorkflow.php', 'ArcanistSingleLintEngine' => 'lint/engine/ArcanistSingleLintEngine.php', 'ArcanistSpellingLinter' => 'lint/linter/ArcanistSpellingLinter.php', 'ArcanistSpellingLinterTestCase' => 'lint/linter/__tests__/ArcanistSpellingLinterTestCase.php', 'ArcanistStartWorkflow' => 'workflow/ArcanistStartWorkflow.php', 'ArcanistStopWorkflow' => 'workflow/ArcanistStopWorkflow.php', 'ArcanistSubversionAPI' => 'repository/api/ArcanistSubversionAPI.php', 'ArcanistSummaryLintRenderer' => 'lint/renderer/ArcanistSummaryLintRenderer.php', 'ArcanistTasksWorkflow' => 'workflow/ArcanistTasksWorkflow.php', - 'ArcanistTestCase' => 'infrastructure/testing/ArcanistTestCase.php', 'ArcanistTestResultParser' => 'unit/parser/ArcanistTestResultParser.php', 'ArcanistTestXHPASTLintSwitchHook' => 'lint/linter/__tests__/ArcanistTestXHPASTLintSwitchHook.php', 'ArcanistTextLinter' => 'lint/linter/ArcanistTextLinter.php', 'ArcanistTextLinterTestCase' => 'lint/linter/__tests__/ArcanistTextLinterTestCase.php', 'ArcanistTimeWorkflow' => 'workflow/ArcanistTimeWorkflow.php', 'ArcanistTodoWorkflow' => 'workflow/ArcanistTodoWorkflow.php', 'ArcanistUSEnglishTranslation' => 'internationalization/ArcanistUSEnglishTranslation.php', 'ArcanistUnitConsoleRenderer' => 'unit/renderer/ArcanistUnitConsoleRenderer.php', 'ArcanistUnitRenderer' => 'unit/renderer/ArcanistUnitRenderer.php', 'ArcanistUnitTestEngine' => 'unit/engine/ArcanistUnitTestEngine.php', 'ArcanistUnitTestResult' => 'unit/ArcanistUnitTestResult.php', 'ArcanistUnitTestableLintEngine' => 'lint/engine/ArcanistUnitTestableLintEngine.php', 'ArcanistUnitWorkflow' => 'workflow/ArcanistUnitWorkflow.php', 'ArcanistUpgradeWorkflow' => 'workflow/ArcanistUpgradeWorkflow.php', 'ArcanistUploadWorkflow' => 'workflow/ArcanistUploadWorkflow.php', 'ArcanistUsageException' => 'exception/ArcanistUsageException.php', 'ArcanistUserAbortException' => 'exception/usage/ArcanistUserAbortException.php', 'ArcanistVersionWorkflow' => 'workflow/ArcanistVersionWorkflow.php', 'ArcanistWhichWorkflow' => 'workflow/ArcanistWhichWorkflow.php', 'ArcanistWorkflow' => 'workflow/ArcanistWorkflow.php', 'ArcanistWorkingCopyIdentity' => 'workingcopyidentity/ArcanistWorkingCopyIdentity.php', 'ArcanistXHPASTLintNamingHook' => 'lint/linter/xhpast/ArcanistXHPASTLintNamingHook.php', 'ArcanistXHPASTLintNamingHookTestCase' => 'lint/linter/xhpast/__tests__/ArcanistXHPASTLintNamingHookTestCase.php', 'ArcanistXHPASTLintSwitchHook' => 'lint/linter/xhpast/ArcanistXHPASTLintSwitchHook.php', 'ArcanistXHPASTLinter' => 'lint/linter/ArcanistXHPASTLinter.php', 'ArcanistXHPASTLinterTestCase' => 'lint/linter/__tests__/ArcanistXHPASTLinterTestCase.php', 'ArcanistXMLLinter' => 'lint/linter/ArcanistXMLLinter.php', 'ArcanistXMLLinterTestCase' => 'lint/linter/__tests__/ArcanistXMLLinterTestCase.php', 'ArcanistXUnitTestResultParser' => 'unit/parser/ArcanistXUnitTestResultParser.php', 'CSharpToolsTestEngine' => 'unit/engine/CSharpToolsTestEngine.php', 'NoseTestEngine' => 'unit/engine/NoseTestEngine.php', 'PhpunitTestEngine' => 'unit/engine/PhpunitTestEngine.php', 'PhpunitTestEngineTestCase' => 'unit/engine/__tests__/PhpunitTestEngineTestCase.php', + 'PhutilTestCase' => 'unit/engine/phutil/PhutilTestCase.php', + 'PhutilTestCaseTestCase' => 'unit/engine/phutil/testcase/PhutilTestCaseTestCase.php', + 'PhutilTestSkippedException' => 'unit/engine/phutil/testcase/PhutilTestSkippedException.php', + 'PhutilTestTerminatedException' => 'unit/engine/phutil/testcase/PhutilTestTerminatedException.php', 'PhutilUnitTestEngine' => 'unit/engine/PhutilUnitTestEngine.php', 'PhutilUnitTestEngineTestCase' => 'unit/engine/__tests__/PhutilUnitTestEngineTestCase.php', 'PytestTestEngine' => 'unit/engine/PytestTestEngine.php', 'XUnitTestEngine' => 'unit/engine/XUnitTestEngine.php', 'XUnitTestResultParserTestCase' => 'unit/parser/__tests__/XUnitTestResultParserTestCase.php', ), 'function' => array(), 'xmap' => array( 'ArcanistAliasWorkflow' => 'ArcanistWorkflow', 'ArcanistAmendWorkflow' => 'ArcanistWorkflow', 'ArcanistAnoidWorkflow' => 'ArcanistWorkflow', 'ArcanistBackoutWorkflow' => 'ArcanistWorkflow', - 'ArcanistBaseCommitParserTestCase' => 'ArcanistTestCase', + 'ArcanistBaseCommitParserTestCase' => 'PhutilTestCase', 'ArcanistBaseXHPASTLinter' => 'ArcanistFutureLinter', 'ArcanistBookmarkWorkflow' => 'ArcanistFeatureWorkflow', 'ArcanistBranchWorkflow' => 'ArcanistFeatureWorkflow', - 'ArcanistBritishTestCase' => 'ArcanistTestCase', + 'ArcanistBritishTestCase' => 'PhutilTestCase', 'ArcanistBrowseWorkflow' => 'ArcanistWorkflow', - 'ArcanistBundleTestCase' => 'ArcanistTestCase', + 'ArcanistBundleTestCase' => 'PhutilTestCase', 'ArcanistCSSLintLinter' => 'ArcanistExternalLinter', 'ArcanistCSSLintLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistCSharpLinter' => 'ArcanistLinter', 'ArcanistCallConduitWorkflow' => 'ArcanistWorkflow', 'ArcanistCapabilityNotSupportedException' => 'Exception', 'ArcanistCheckstyleXMLLintRenderer' => 'ArcanistLintRenderer', 'ArcanistChmodLinter' => 'ArcanistLinter', 'ArcanistChmodLinterTestCase' => 'ArcanistLinterTestCase', 'ArcanistCloseRevisionWorkflow' => 'ArcanistWorkflow', 'ArcanistCloseWorkflow' => 'ArcanistWorkflow', 'ArcanistClosureLinter' => 'ArcanistExternalLinter', 'ArcanistClosureLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistCoffeeLintLinter' => 'ArcanistExternalLinter', 'ArcanistCoffeeLintLinterTestCase' => 'ArcanistExternalLinterTestCase', - 'ArcanistCommentRemoverTestCase' => 'ArcanistTestCase', + 'ArcanistCommentRemoverTestCase' => 'PhutilTestCase', 'ArcanistCommitWorkflow' => 'ArcanistWorkflow', 'ArcanistCompilerLintRenderer' => 'ArcanistLintRenderer', 'ArcanistComprehensiveLintEngine' => 'ArcanistLintEngine', 'ArcanistConfigurationDrivenLintEngine' => 'ArcanistLintEngine', 'ArcanistConsoleLintRenderer' => 'ArcanistLintRenderer', 'ArcanistCoverWorkflow' => 'ArcanistWorkflow', 'ArcanistCppcheckLinter' => 'ArcanistExternalLinter', 'ArcanistCppcheckLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistCpplintLinter' => 'ArcanistExternalLinter', 'ArcanistCpplintLinterTestCase' => 'ArcanistExternalLinterTestCase', - 'ArcanistDiffParserTestCase' => 'ArcanistTestCase', - 'ArcanistDiffUtilsTestCase' => 'ArcanistTestCase', + 'ArcanistDiffParserTestCase' => 'PhutilTestCase', + 'ArcanistDiffUtilsTestCase' => 'PhutilTestCase', 'ArcanistDiffWorkflow' => 'ArcanistWorkflow', 'ArcanistDifferentialCommitMessageParserException' => 'Exception', 'ArcanistDifferentialDependencyGraph' => 'AbstractDirectedGraph', 'ArcanistDownloadWorkflow' => 'ArcanistWorkflow', 'ArcanistEventType' => 'PhutilEventType', 'ArcanistExportWorkflow' => 'ArcanistWorkflow', 'ArcanistExternalLinter' => 'ArcanistFutureLinter', 'ArcanistExternalLinterTestCase' => 'ArcanistLinterTestCase', 'ArcanistFeatureWorkflow' => 'ArcanistWorkflow', 'ArcanistFilenameLinter' => 'ArcanistLinter', 'ArcanistFilenameLinterTestCase' => 'ArcanistLinterTestCase', 'ArcanistFlagWorkflow' => 'ArcanistWorkflow', 'ArcanistFlake8Linter' => 'ArcanistExternalLinter', 'ArcanistFlake8LinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistFutureLinter' => 'ArcanistLinter', 'ArcanistGeneratedLinter' => 'ArcanistLinter', 'ArcanistGeneratedLinterTestCase' => 'ArcanistLinterTestCase', 'ArcanistGetConfigWorkflow' => 'ArcanistWorkflow', 'ArcanistGitAPI' => 'ArcanistRepositoryAPI', 'ArcanistGoLintLinter' => 'ArcanistExternalLinter', 'ArcanistGoLintLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistGoTestResultParser' => 'ArcanistTestResultParser', - 'ArcanistGoTestResultParserTestCase' => 'ArcanistTestCase', + 'ArcanistGoTestResultParserTestCase' => 'PhutilTestCase', 'ArcanistHLintLinter' => 'ArcanistExternalLinter', 'ArcanistHLintLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistHelpWorkflow' => 'ArcanistWorkflow', 'ArcanistHgClientChannel' => 'PhutilProtocolChannel', 'ArcanistHgServerChannel' => 'PhutilProtocolChannel', 'ArcanistInstallCertificateWorkflow' => 'ArcanistWorkflow', 'ArcanistJSHintLinter' => 'ArcanistExternalLinter', 'ArcanistJSHintLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistJSONLintLinter' => 'ArcanistExternalLinter', 'ArcanistJSONLintLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistJSONLintRenderer' => 'ArcanistLintRenderer', 'ArcanistJSONLinter' => 'ArcanistLinter', 'ArcanistJSONLinterTestCase' => 'ArcanistLinterTestCase', 'ArcanistJscsLinter' => 'ArcanistExternalLinter', 'ArcanistJscsLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistLandWorkflow' => 'ArcanistWorkflow', 'ArcanistLesscLinter' => 'ArcanistExternalLinter', 'ArcanistLesscLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistLiberateWorkflow' => 'ArcanistWorkflow', 'ArcanistLibraryTestCase' => 'PhutilLibraryTestCase', 'ArcanistLintWorkflow' => 'ArcanistWorkflow', - 'ArcanistLinterTestCase' => 'ArcanistPhutilTestCase', + 'ArcanistLinterTestCase' => 'PhutilTestCase', 'ArcanistLintersWorkflow' => 'ArcanistWorkflow', 'ArcanistListWorkflow' => 'ArcanistWorkflow', 'ArcanistMercurialAPI' => 'ArcanistRepositoryAPI', - 'ArcanistMercurialParserTestCase' => 'ArcanistTestCase', + 'ArcanistMercurialParserTestCase' => 'PhutilTestCase', 'ArcanistMergeConflictLinter' => 'ArcanistLinter', 'ArcanistMergeConflictLinterTestCase' => 'ArcanistLinterTestCase', 'ArcanistMissingLinterException' => 'Exception', 'ArcanistNoEffectException' => 'ArcanistUsageException', 'ArcanistNoEngineException' => 'ArcanistUsageException', 'ArcanistNoLintLinter' => 'ArcanistLinter', 'ArcanistNoLintLinterTestCase' => 'ArcanistLinterTestCase', 'ArcanistNoneLintRenderer' => 'ArcanistLintRenderer', 'ArcanistPEP8Linter' => 'ArcanistExternalLinter', 'ArcanistPEP8LinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistPasteWorkflow' => 'ArcanistWorkflow', 'ArcanistPatchWorkflow' => 'ArcanistWorkflow', 'ArcanistPhpLinter' => 'ArcanistExternalLinter', 'ArcanistPhpLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistPhpcsLinter' => 'ArcanistExternalLinter', 'ArcanistPhpcsLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistPhpunitTestResultParser' => 'ArcanistTestResultParser', 'ArcanistPhrequentWorkflow' => 'ArcanistWorkflow', 'ArcanistPhutilLibraryLinter' => 'ArcanistLinter', - 'ArcanistPhutilTestCaseTestCase' => 'ArcanistPhutilTestCase', - 'ArcanistPhutilTestSkippedException' => 'Exception', - 'ArcanistPhutilTestTerminatedException' => 'Exception', 'ArcanistPhutilXHPASTLinter' => 'ArcanistBaseXHPASTLinter', 'ArcanistPhutilXHPASTLinterTestCase' => 'ArcanistLinterTestCase', 'ArcanistPuppetLintLinter' => 'ArcanistExternalLinter', 'ArcanistPuppetLintLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistPyFlakesLinter' => 'ArcanistExternalLinter', 'ArcanistPyFlakesLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistPyLintLinter' => 'ArcanistExternalLinter', 'ArcanistPyLintLinterTestCase' => 'ArcanistExternalLinterTestCase', - 'ArcanistRepositoryAPIMiscTestCase' => 'ArcanistTestCase', - 'ArcanistRepositoryAPIStateTestCase' => 'ArcanistTestCase', + 'ArcanistRepositoryAPIMiscTestCase' => 'PhutilTestCase', + 'ArcanistRepositoryAPIStateTestCase' => 'PhutilTestCase', 'ArcanistRevertWorkflow' => 'ArcanistWorkflow', 'ArcanistRuboCopLinter' => 'ArcanistExternalLinter', 'ArcanistRuboCopLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistRubyLinter' => 'ArcanistExternalLinter', 'ArcanistRubyLinterTestCase' => 'ArcanistExternalLinterTestCase', 'ArcanistScriptAndRegexLinter' => 'ArcanistLinter', 'ArcanistSetConfigWorkflow' => 'ArcanistWorkflow', 'ArcanistShellCompleteWorkflow' => 'ArcanistWorkflow', 'ArcanistSingleLintEngine' => 'ArcanistLintEngine', 'ArcanistSpellingLinter' => 'ArcanistLinter', 'ArcanistSpellingLinterTestCase' => 'ArcanistLinterTestCase', 'ArcanistStartWorkflow' => 'ArcanistPhrequentWorkflow', 'ArcanistStopWorkflow' => 'ArcanistPhrequentWorkflow', 'ArcanistSubversionAPI' => 'ArcanistRepositoryAPI', 'ArcanistSummaryLintRenderer' => 'ArcanistLintRenderer', 'ArcanistTasksWorkflow' => 'ArcanistWorkflow', - 'ArcanistTestCase' => 'ArcanistPhutilTestCase', 'ArcanistTestXHPASTLintSwitchHook' => 'ArcanistXHPASTLintSwitchHook', 'ArcanistTextLinter' => 'ArcanistLinter', 'ArcanistTextLinterTestCase' => 'ArcanistLinterTestCase', 'ArcanistTimeWorkflow' => 'ArcanistPhrequentWorkflow', 'ArcanistTodoWorkflow' => 'ArcanistWorkflow', 'ArcanistUSEnglishTranslation' => 'PhutilTranslation', 'ArcanistUnitConsoleRenderer' => 'ArcanistUnitRenderer', 'ArcanistUnitTestableLintEngine' => 'ArcanistLintEngine', 'ArcanistUnitWorkflow' => 'ArcanistWorkflow', 'ArcanistUpgradeWorkflow' => 'ArcanistWorkflow', 'ArcanistUploadWorkflow' => 'ArcanistWorkflow', 'ArcanistUsageException' => 'Exception', 'ArcanistUserAbortException' => 'ArcanistUsageException', 'ArcanistVersionWorkflow' => 'ArcanistWorkflow', 'ArcanistWhichWorkflow' => 'ArcanistWorkflow', 'ArcanistWorkflow' => 'Phobject', - 'ArcanistXHPASTLintNamingHookTestCase' => 'ArcanistTestCase', + 'ArcanistXHPASTLintNamingHookTestCase' => 'PhutilTestCase', 'ArcanistXHPASTLinter' => 'ArcanistBaseXHPASTLinter', 'ArcanistXHPASTLinterTestCase' => 'ArcanistLinterTestCase', 'ArcanistXMLLinter' => 'ArcanistLinter', 'ArcanistXMLLinterTestCase' => 'ArcanistLinterTestCase', 'CSharpToolsTestEngine' => 'XUnitTestEngine', 'NoseTestEngine' => 'ArcanistUnitTestEngine', 'PhpunitTestEngine' => 'ArcanistUnitTestEngine', - 'PhpunitTestEngineTestCase' => 'ArcanistTestCase', + 'PhpunitTestEngineTestCase' => 'PhutilTestCase', + 'PhutilTestCaseTestCase' => 'PhutilTestCase', + 'PhutilTestSkippedException' => 'Exception', + 'PhutilTestTerminatedException' => 'Exception', 'PhutilUnitTestEngine' => 'ArcanistUnitTestEngine', - 'PhutilUnitTestEngineTestCase' => 'ArcanistTestCase', + 'PhutilUnitTestEngineTestCase' => 'PhutilTestCase', 'PytestTestEngine' => 'ArcanistUnitTestEngine', 'XUnitTestEngine' => 'ArcanistUnitTestEngine', - 'XUnitTestResultParserTestCase' => 'ArcanistTestCase', + 'XUnitTestResultParserTestCase' => 'PhutilTestCase', ), )); diff --git a/src/configuration/__tests__/ArcanistBritishTestCase.php b/src/configuration/__tests__/ArcanistBritishTestCase.php index 53517e46..7cba7407 100644 --- a/src/configuration/__tests__/ArcanistBritishTestCase.php +++ b/src/configuration/__tests__/ArcanistBritishTestCase.php @@ -1,97 +1,97 @@ assertCommandCompletion( array('land'), 'alnd', array('land', 'amend')); $this->assertCommandCompletion( array('branch'), 'brnach', array('branch', 'browse')); $this->assertCommandCompletion( array(), 'test', array('list', 'unit')); $this->assertCommandCompletion( array('list'), 'lists', array('list')); $this->assertCommandCompletion( array('diff'), 'dfif', array('diff')); $this->assertCommandCompletion( array('unit'), 'uint', array('unit', 'lint', 'list')); $this->assertCommandCompletion( array('list', 'lint'), 'nilt', array('unit', 'lint', 'list')); } private function assertCommandCompletion($expect, $input, $commands) { $result = ArcanistConfiguration::correctCommandSpelling( $input, $commands, 2); sort($result); sort($expect); $commands = implode(', ', $commands); $this->assertEqual( $expect, $result, pht('Correction of %s against: %s', $input, $commands)); } public function testArgumentCompletion() { $this->assertArgumentCompletion( array('nolint'), 'no-lint', array('nolint', 'nounit')); $this->assertArgumentCompletion( array('reviewers'), 'reviewer', array('reviewers', 'cc')); $this->assertArgumentCompletion( array(), 'onlint', array('nolint')); $this->assertArgumentCompletion( array(), 'nolind', array('nolint')); } private function assertArgumentCompletion($expect, $input, $arguments) { $result = ArcanistConfiguration::correctArgumentSpelling( $input, $arguments); sort($result); sort($expect); $arguments = implode(', ', $arguments); $this->assertEqual( $expect, $result, pht('Correction of %s against: %s', $input, $arguments)); } } diff --git a/src/difference/__tests__/ArcanistDiffUtilsTestCase.php b/src/difference/__tests__/ArcanistDiffUtilsTestCase.php index d928e539..81c326d2 100644 --- a/src/difference/__tests__/ArcanistDiffUtilsTestCase.php +++ b/src/difference/__tests__/ArcanistDiffUtilsTestCase.php @@ -1,242 +1,242 @@ assertEqual( $test[2], ArcanistDiffUtils::generateEditString( str_split($test[0]), str_split($test[1])), "'{$test[0]}' vs '{$test[1]}'"); } $utf8_tests = array( array( 'GrumpyCat', "Grumpy\xE2\x98\x83at", 'ssssssxss', ), ); foreach ($tests as $test) { $this->assertEqual( $test[2], ArcanistDiffUtils::generateEditString( phutil_utf8v_combined($test[0]), phutil_utf8v_combined($test[1])), "'{$test[0]}' vs '{$test[1]}' (utf8)"); } } public function testGenerateUTF8IntralineDiff() { // Both Strings Empty. $left = ''; $right = ''; $result = array( array(array(0, 0)), array(array(0, 0)), ); $this->assertEqual( $result, ArcanistDiffUtils::generateIntralineDiff($left, $right)); // Left String Empty. $left = ''; $right = "Grumpy\xE2\x98\x83at"; $result = array( array(array(0, 0)), array(array(0, 11)), ); $this->assertEqual( $result, ArcanistDiffUtils::generateIntralineDiff($left, $right)); // Right String Empty. $left = "Grumpy\xE2\x98\x83at"; $right = ''; $result = array( array(array(0, 11)), array(array(0, 0)), ); $this->assertEqual( $result, ArcanistDiffUtils::generateIntralineDiff($left, $right)); // Both Strings Same $left = "Grumpy\xE2\x98\x83at"; $right = "Grumpy\xE2\x98\x83at"; $result = array( array(array(0, 11)), array(array(0, 11)), ); $this->assertEqual( $result, ArcanistDiffUtils::generateIntralineDiff($left, $right)); // Both Strings are different. $left = "Grumpy\xE2\x98\x83at"; $right = 'Smiling Dog'; $result = array( array(array(1, 11)), array(array(1, 11)), ); $this->assertEqual( $result, ArcanistDiffUtils::generateIntralineDiff($left, $right)); // String with one difference in the middle. $left = 'GrumpyCat'; $right = "Grumpy\xE2\x98\x83at"; $result = array( array(array(0, 6), array(1, 1), array(0, 2)), array(array(0, 6), array(1, 3), array(0, 2)), ); $this->assertEqual( $result, ArcanistDiffUtils::generateIntralineDiff($left, $right)); // Differences in middle, not connected to each other. $left = 'GrumpyCat'; $right = "Grumpy\xE2\x98\x83a\xE2\x98\x83t"; $result = array( array(array(0, 6), array(1, 2), array(0, 1)), array(array(0, 6), array(1, 7), array(0, 1)), ); $this->assertEqual( $result, ArcanistDiffUtils::generateIntralineDiff($left, $right)); // String with difference at the beginning. $left = "GrumpyC\xE2\x98\x83t"; $right = "DrumpyC\xE2\x98\x83t"; $result = array( array(array(1, 1), array(0, 10)), array(array(1, 1), array(0, 10)), ); $this->assertEqual( $result, ArcanistDiffUtils::generateIntralineDiff($left, $right)); // String with difference at the end. $left = "GrumpyC\xE2\x98\x83t"; $right = "GrumpyC\xE2\x98\x83P"; $result = array( array(array(0, 10), array(1, 1)), array(array(0, 10), array(1, 1)), ); $this->assertEqual( $result, ArcanistDiffUtils::generateIntralineDiff($left, $right)); // String with differences at the beginning and end. $left = "GrumpyC\xE2\x98\x83t"; $right = "DrumpyC\xE2\x98\x83P"; $result = array( array(array(1, 1), array(0, 9), array(1, 1)), array(array(1, 1), array(0, 9), array(1, 1)), ); $this->assertEqual( $result, ArcanistDiffUtils::generateIntralineDiff($left, $right)); // This is a unicode combining character, "COMBINING DOUBLE TILDE". $cc = "\xCD\xA0"; $left = 'Senor'; $right = "Sen{$cc}or"; $result = array( array(array(0, 2), array(1, 1), array(0, 2)), array(array(0, 2), array(1, 3), array(0, 2)), ); $this->assertEqual( $result, ArcanistDiffUtils::generateIntralineDiff($left, $right)); } } diff --git a/src/infrastructure/testing/ArcanistTestCase.php b/src/infrastructure/testing/ArcanistTestCase.php deleted file mode 100644 index e56707a6..00000000 --- a/src/infrastructure/testing/ArcanistTestCase.php +++ /dev/null @@ -1,3 +0,0 @@ -getLinter(); } $files = id(new FileFinder($root)) ->withType('f') ->withSuffix('lint-test') ->find(); $test_count = 0; foreach ($files as $file) { $this->lintFile($root.$file, $linter); $test_count++; } $this->assertTrue( ($test_count > 0), pht( 'Expected to find some %s tests in directory %s!', '.lint-test', $root)); } private function lintFile($file, ArcanistLinter $linter) { $linter = clone $linter; $contents = Filesystem::readFile($file); $contents = preg_split('/^~{4,}\n/m', $contents); if (count($contents) < 2) { throw new Exception( pht( "Expected '%s' separating test case and results.", '~~~~~~~~~~')); } list ($data, $expect, $xform, $config) = array_merge( $contents, array(null, null)); $basename = basename($file); if ($config) { $config = phutil_json_decode($config); } else { $config = array(); } PhutilTypeSpec::checkMap( $config, array( 'config' => 'optional map', 'path' => 'optional string', 'mode' => 'optional string', 'stopped' => 'optional bool', )); $exception = null; $after_lint = null; $messages = null; $exception_message = false; $caught_exception = false; try { $tmp = new TempFile($basename); Filesystem::writeFile($tmp, $data); $full_path = (string)$tmp; $mode = idx($config, 'mode'); if ($mode) { Filesystem::changePermissions($tmp, octdec($mode)); } $dir = dirname($full_path); $path = basename($full_path); $working_copy = ArcanistWorkingCopyIdentity::newFromRootAndConfigFile( $dir, null, 'Unit Test'); $configuration_manager = new ArcanistConfigurationManager(); $configuration_manager->setWorkingCopyIdentity($working_copy); $engine = new ArcanistUnitTestableLintEngine(); $engine->setWorkingCopy($working_copy); $engine->setConfigurationManager($configuration_manager); $path_name = idx($config, 'path', $path); $engine->setPaths(array($path_name)); $linter->addPath($path_name); $linter->addData($path_name, $data); foreach (idx($config, 'config', array()) as $key => $value) { $linter->setLinterConfigurationValue($key, $value); } $engine->addLinter($linter); $engine->addFileData($path_name, $data); $results = $engine->run(); $this->assertEqual( 1, count($results), pht('Expect one result returned by linter.')); $assert_stopped = idx($config, 'stopped'); if ($assert_stopped !== null) { $this->assertEqual( $assert_stopped, $linter->didStopAllLinters(), $assert_stopped ? pht('Expect linter to be stopped.') : pht('Expect linter to not be stopped.')); } $result = reset($results); $patcher = ArcanistLintPatcher::newFromArcanistLintResult($result); $after_lint = $patcher->getModifiedFileContent(); - } catch (ArcanistPhutilTestTerminatedException $ex) { + } catch (PhutilTestTerminatedException $ex) { throw $ex; } catch (Exception $exception) { $caught_exception = true; if ($exception instanceof PhutilAggregateException) { $caught_exception = false; foreach ($exception->getExceptions() as $ex) { if ($ex instanceof ArcanistUsageException || $ex instanceof ArcanistMissingLinterException) { $this->assertSkipped($ex->getMessage()); } else { $caught_exception = true; } } } else if ($exception instanceof ArcanistUsageException || $exception instanceof ArcanistMissingLinterException) { $this->assertSkipped($exception->getMessage()); } $exception_message = $exception->getMessage()."\n\n". $exception->getTraceAsString(); } $this->assertEqual(false, $caught_exception, $exception_message); $this->compareLint($basename, $expect, $result); $this->compareTransform($xform, $after_lint); } private function compareLint($file, $expect, ArcanistLintResult $result) { $seen = array(); $raised = array(); $message_map = array(); foreach ($result->getMessages() as $message) { $sev = $message->getSeverity(); $line = $message->getLine(); $char = $message->getChar(); $code = $message->getCode(); $name = $message->getName(); $message_key = $sev.':'.$line.':'.$char; $message_map[$message_key] = $message; $seen[] = $message_key; $raised[] = sprintf( ' %s: %s %s', pht('%s at line %d, char %d', $sev, $line, $char), $code, $name); } $expect = trim($expect); if ($expect) { $expect = explode("\n", $expect); } else { $expect = array(); } foreach ($expect as $key => $expected) { $expect[$key] = head(explode(' ', $expected)); } $expect = array_fill_keys($expect, true); $seen = array_fill_keys($seen, true); if (!$raised) { $raised = array(pht('No messages.')); } $raised = sprintf( "%s:\n%s", pht('Actually raised'), implode("\n", $raised)); foreach (array_diff_key($expect, $seen) as $missing => $ignored) { $missing = explode(':', $missing); $sev = array_shift($missing); $pos = $missing; $this->assertFailure( pht( "In '%s', expected lint to raise %s on line %d at char %d, ". "but no %s was raised. %s", $file, $sev, idx($pos, 0), idx($pos, 1), $sev, $raised)); } foreach (array_diff_key($seen, $expect) as $surprising => $ignored) { $message = $message_map[$surprising]; $message_info = $message->getDescription(); list($sev, $line, $char) = explode(':', $surprising); $this->assertFailure( sprintf( "%s:\n\n%s\n\n%s", pht( "In '%s', lint raised %s on line %d at char %d, ". "but nothing was expected", $file, $sev, $line, $char), $message_info, $raised)); } } private function compareTransform($expected, $actual) { if (!strlen($expected)) { return; } $this->assertEqual( $expected, $actual, pht('File as patched by lint did not match the expected patched file.')); } } diff --git a/src/lint/linter/xhpast/__tests__/ArcanistXHPASTLintNamingHookTestCase.php b/src/lint/linter/xhpast/__tests__/ArcanistXHPASTLintNamingHookTestCase.php index 6a1d67eb..baec7dbc 100644 --- a/src/lint/linter/xhpast/__tests__/ArcanistXHPASTLintNamingHookTestCase.php +++ b/src/lint/linter/xhpast/__tests__/ArcanistXHPASTLintNamingHookTestCase.php @@ -1,65 +1,66 @@ array(1, 0, 0, 0), 'UpperCamelCaseROFL' => array(1, 0, 0, 0), 'lowerCamelCase' => array(0, 1, 0, 0), 'lowerCamelCaseROFL' => array(0, 1, 0, 0), 'UPPERCASE_WITH_UNDERSCORES' => array(0, 0, 1, 0), '_UPPERCASE_WITH_UNDERSCORES_' => array(0, 0, 1, 0), '__UPPERCASE__WITH__UNDERSCORES__' => array(0, 0, 1, 0), 'lowercase_with_underscores' => array(0, 0, 0, 1), '_lowercase_with_underscores_' => array(0, 0, 0, 1), '__lowercase__with__underscores__' => array(0, 0, 0, 1), 'mixedCASE_NoNsEnSe' => array(0, 0, 0, 0), ); foreach ($tests as $test => $expect) { $this->assertEqual( $expect[0], ArcanistXHPASTLintNamingHook::isUpperCamelCase($test), "UpperCamelCase: '{$test}'"); $this->assertEqual( $expect[1], ArcanistXHPASTLintNamingHook::isLowerCamelCase($test), "lowerCamelCase: '{$test}'"); $this->assertEqual( $expect[2], ArcanistXHPASTLintNamingHook::isUppercaseWithUnderscores($test), "UPPERCASE_WITH_UNDERSCORES: '{$test}'"); $this->assertEqual( $expect[3], ArcanistXHPASTLintNamingHook::isLowercaseWithUnderscores($test), "lowercase_with_underscores: '{$test}'"); } } public function testStripUtilities() { // Variable stripping. $this->assertEqual( 'stuff', ArcanistXHPASTLintNamingHook::stripPHPVariable('stuff')); $this->assertEqual( 'stuff', ArcanistXHPASTLintNamingHook::stripPHPVariable('$stuff')); // Function/method stripping. $this->assertEqual( 'construct', ArcanistXHPASTLintNamingHook::stripPHPFunction('construct')); $this->assertEqual( 'construct', ArcanistXHPASTLintNamingHook::stripPHPFunction('__construct')); } } diff --git a/src/parser/__tests__/ArcanistBaseCommitParserTestCase.php b/src/parser/__tests__/ArcanistBaseCommitParserTestCase.php index a2d5875a..5580676b 100644 --- a/src/parser/__tests__/ArcanistBaseCommitParserTestCase.php +++ b/src/parser/__tests__/ArcanistBaseCommitParserTestCase.php @@ -1,162 +1,162 @@ assertCommit( 'Empty Rules', null, array( )); $this->assertCommit( 'Literal', 'xyz', array( 'runtime' => 'literal:xyz', )); } public function testResolutionOrder() { // Rules should be resolved in order: args, local, project, global. These // test cases intentionally scramble argument order to test that resolution // order is independent of argument order. $this->assertCommit( 'Order: Args', 'y', array( 'local' => 'literal:n', 'project' => 'literal:n', 'runtime' => 'literal:y', 'user' => 'literal:n', )); $this->assertCommit( 'Order: Local', 'y', array( 'project' => 'literal:n', 'local' => 'literal:y', 'user' => 'literal:n', )); $this->assertCommit( 'Order: Project', 'y', array( 'project' => 'literal:y', 'user' => 'literal:n', )); $this->assertCommit( 'Order: Global', 'y', array( 'user' => 'literal:y', )); } public function testLegacyRule() { // 'global' should translate to 'user' $this->assertCommit( '"global" name', 'y', array( 'runtime' => 'arc:global, arc:halt', 'local' => 'arc:halt', 'project' => 'arc:halt', 'user' => 'literal:y', )); // 'args' should translate to 'runtime' $this->assertCommit( '"args" name', 'y', array( 'runtime' => 'arc:project, literal:y', 'local' => 'arc:halt', 'project' => 'arc:args', 'user' => 'arc:halt', )); } public function testHalt() { // 'arc:halt' should halt all processing. $this->assertCommit( 'Halt', null, array( 'runtime' => 'arc:halt', 'local' => 'literal:xyz', )); } public function testYield() { // 'arc:yield' should yield to other rulesets. $this->assertCommit( 'Yield', 'xyz', array( 'runtime' => 'arc:yield, literal:abc', 'local' => 'literal:xyz', )); // This one should return to 'runtime' after exhausting 'local'. $this->assertCommit( 'Yield + Return', 'abc', array( 'runtime' => 'arc:yield, literal:abc', 'local' => 'arc:skip', )); } public function testJump() { // This should resolve to 'abc' without hitting any of the halts. $this->assertCommit( 'Jump', 'abc', array( 'runtime' => 'arc:project, arc:halt', 'local' => 'literal:abc', 'project' => 'arc:user, arc:halt', 'user' => 'arc:local, arc:halt', )); } public function testJumpReturn() { // After jumping to project, we should return to 'runtime'. $this->assertCommit( 'Jump Return', 'xyz', array( 'runtime' => 'arc:project, literal:xyz', 'local' => 'arc:halt', 'project' => '', 'user' => 'arc:halt', )); } private function assertCommit($desc, $commit, $rules) { $parser = $this->buildParser(); $result = $parser->resolveBaseCommit($rules); $this->assertEqual($commit, $result, $desc); } private function buildParser() { // TODO: This is a little hacky because we're using the Arcanist repository // itself to execute tests with, but it should be OK until we get proper // isolation for repository-oriented test cases. $root = dirname(phutil_get_library_root('arcanist')); $working_copy = ArcanistWorkingCopyIdentity::newFromPath($root); $configuration_manager = new ArcanistConfigurationManager(); $configuration_manager->setWorkingCopyIdentity($working_copy); $repo = ArcanistRepositoryAPI::newAPIFromConfigurationManager( $configuration_manager); return new ArcanistBaseCommitParser($repo); } } diff --git a/src/parser/__tests__/ArcanistBundleTestCase.php b/src/parser/__tests__/ArcanistBundleTestCase.php index cbd6b41e..ea29726f 100644 --- a/src/parser/__tests__/ArcanistBundleTestCase.php +++ b/src/parser/__tests__/ArcanistBundleTestCase.php @@ -1,919 +1,919 @@ getResourcePath($name)); } private function getResourcePath($name) { return dirname(__FILE__).'/bundle/'.$name; } private function loadDiff($old, $new) { list($err, $stdout) = exec_manual( 'diff --unified=65535 --label %s --label %s -- %s %s', 'file 9999-99-99', 'file 9999-99-99', $this->getResourcePath($old), $this->getResourcePath($new)); $this->assertEqual( 1, $err, pht( "Expect `%s` to find changes between '%s' and '%s'.", 'diff', $old, $new)); return $stdout; } private function loadOneChangeBundle($old, $new) { $diff = $this->loadDiff($old, $new); return ArcanistBundle::newFromDiff($diff); } /** * Unarchive a saved git repository and apply each commit as though via * "arc patch", verifying that the resulting tree hash is identical to the * tree hash produced by the real commit. */ public function testGitRepository() { if (phutil_is_windows()) { $this->assertSkipped(pht('This test is not supported under Windows.')); } $archive = dirname(__FILE__).'/bundle.git.tgz'; $fixture = PhutilDirectoryFixture::newFromArchive($archive); $old_dir = getcwd(); chdir($fixture->getPath()); $caught = null; try { $this->runGitRepositoryTests($fixture); } catch (Exception $ex) { $caught = $ex; } chdir($old_dir); if ($caught) { throw $ex; } } private function runGitRepositoryTests(PhutilDirectoryFixture $fixture) { $patches = dirname(__FILE__).'/patches/'; list($commits) = execx( 'git log --format=%s', '%H %T %s'); $commits = explode("\n", trim($commits)); // The very first commit doesn't have a meaningful parent, so don't examine // it. array_pop($commits); foreach ($commits as $commit) { list($commit_hash, $tree_hash, $subject) = explode(' ', $commit, 3); execx('git reset --hard %s --', $commit_hash); $fixture_path = $fixture->getPath(); $working_copy = ArcanistWorkingCopyIdentity::newFromPath($fixture_path); $configuration_manager = new ArcanistConfigurationManager(); $configuration_manager->setWorkingCopyIdentity($working_copy); $repository_api = ArcanistRepositoryAPI::newAPIFromConfigurationManager( $configuration_manager); $repository_api->setBaseCommitArgumentRules('arc:this'); $diff = $repository_api->getFullGitDiff( $repository_api->getBaseCommit(), $repository_api->getHeadCommit()); $parser = new ArcanistDiffParser(); $parser->setRepositoryAPI($repository_api); $changes = $parser->parseDiff($diff); $this->makeChangeAssertions($commit_hash, $changes); $bundle = ArcanistBundle::newFromChanges($changes); execx('git reset --hard %s^ --', $commit_hash); $patch = $bundle->toGitPatch(); $expect_path = $patches.'/'.$commit_hash.'.gitpatch'; $expect = null; if (Filesystem::pathExists($expect_path)) { $expect = Filesystem::readFile($expect_path); } if ($patch === $expect) { $this->assertEqual($expect, $patch); } else { Filesystem::writeFile($expect_path.'.real', $patch); throw new Exception( pht( "Expected patch and actual patch for %s differ. ". "Wrote actual patch to '%s.real'.", $commit_hash, $expect_path)); } try { id(new ExecFuture('git apply --index --reject')) ->write($patch) ->resolvex(); } catch (CommandException $ex) { $temp = new TempFile(substr($commit_hash, 0, 8).'.patch'); $temp->setPreserveFile(true); Filesystem::writeFile($temp, $patch); PhutilConsole::getConsole()->writeErr( "%s\n", pht("Wrote failing patch to '%s'.", $temp)); throw $ex; } execx('git commit -m %s', $subject); list($result_hash) = execx('git log -n1 --format=%s', '%T'); $result_hash = trim($result_hash); $this->assertEqual( $tree_hash, $result_hash, pht('Commit %s: %s', $commit_hash, $subject)); } } private function makeChangeAssertions($commit, array $raw_changes) { $changes = array(); // Verify that there are no duplicate changes, and rekey the changes on // affected path because we don't care about the order in which the // changes appear. foreach ($raw_changes as $change) { $this->assertTrue( empty($changes[$change->getCurrentPath()]), 'Unique Path: '.$change->getCurrentPath()); $changes[$change->getCurrentPath()] = $change; } switch ($commit) { case '1830a13adf764b55743f7edc6066451898d8ffa4': // "Mark koan2 as +x and edit it." $this->assertEqual(1, count($changes)); $c = $changes['koan2']; $this->assertEqual( ArcanistDiffChangeType::TYPE_CHANGE, $c->getType()); $this->assertEqual( '100644', idx($c->getOldProperties(), 'unix:filemode')); $this->assertEqual( '100755', idx($c->getNewProperties(), 'unix:filemode')); break; case '8ecc728bcc9b482a9a91527ea471b04fc1a025cf': // "Move 'text' to 'executable' and mark it +x." $this->assertEqual(2, count($changes)); $c = $changes['executable']; $this->assertEqual( ArcanistDiffChangeType::TYPE_MOVE_HERE, $c->getType()); $this->assertEqual( '100644', idx($c->getOldProperties(), 'unix:filemode')); $this->assertEqual( '100755', idx($c->getNewProperties(), 'unix:filemode')); break; case '39c8e7dd3914edff087a6214f0cd996ad08e5b3d': // "Mark koan as +x." // Primarily a test against a recusive synthetic hunk construction bug. $this->assertEqual(1, count($changes)); $c = $changes['koan']; $this->assertEqual( ArcanistDiffChangeType::TYPE_CHANGE, $c->getType()); $this->assertEqual( '100644', idx($c->getOldProperties(), 'unix:filemode')); $this->assertEqual( '100755', idx($c->getNewProperties(), 'unix:filemode')); break; case 'c573c25d1a767d270fed504cd993e78aba936338': // "Copy a koan over text, editing the original koan." // Git doesn't really do anything meaningful with this. $this->assertEqual(2, count($changes)); $c = $changes['koan']; $this->assertEqual( ArcanistDiffChangeType::TYPE_CHANGE, $c->getType()); $c = $changes['text']; $this->assertEqual( ArcanistDiffChangeType::TYPE_CHANGE, $c->getType()); break; case 'd26628e588cf7d16368845b121c6ac6c781e81d0': // "Copy a koan, modifying both the source and destination." $this->assertEqual(2, count($changes)); $c = $changes['koan']; $this->assertEqual( ArcanistDiffChangeType::TYPE_COPY_AWAY, $c->getType()); $c = $changes['koan2']; $this->assertEqual( ArcanistDiffChangeType::TYPE_COPY_HERE, $c->getType()); break; case 'b0c9663ecda5f666f62dad245a3a7549aac5e636': // "Remove a koan copy." $this->assertEqual(1, count($changes)); $c = $changes['koan2']; $this->assertEqual( ArcanistDiffChangeType::TYPE_DELETE, $c->getType()); break; case 'b6ecdb3b4801f3028d88ba49940a558360847dbf': // "Copy a koan and edit the destination." // Git does not detect this as a copy without --find-copies-harder. $this->assertEqual(1, count($changes)); $c = $changes['koan2']; $this->assertEqual( ArcanistDiffChangeType::TYPE_ADD, $c->getType()); break; case '30d23787e1ecd254c884afbe37afa612f61e3904': // "Move and edit a koan." $this->assertEqual(2, count($changes)); $c = $changes['koan2']; $this->assertEqual( ArcanistDiffChangeType::TYPE_MOVE_AWAY, $c->getType()); $c = $changes['koan']; $this->assertEqual( ArcanistDiffChangeType::TYPE_MOVE_HERE, $c->getType()); break; case 'c0ba9bfe3695f95c3f558bc5797eeba421d32483': // "Remove two koans." $this->assertEqual(2, count($changes)); $c = $changes['koan3']; $this->assertEqual( ArcanistDiffChangeType::TYPE_DELETE, $c->getType()); $c = $changes['koan4']; $this->assertEqual( ArcanistDiffChangeType::TYPE_DELETE, $c->getType()); break; case '2658fd01d5355abe5d4c7ead3a0e7b4b3449fe77': // "Multicopy a koan." $this->assertEqual(3, count($changes)); $c = $changes['koan']; $this->assertEqual( ArcanistDiffChangeType::TYPE_MULTICOPY, $c->getType()); $c = $changes['koan3']; $this->assertEqual( ArcanistDiffChangeType::TYPE_COPY_HERE, $c->getType()); $c = $changes['koan4']; $this->assertEqual( ArcanistDiffChangeType::TYPE_MOVE_HERE, $c->getType()); break; case '1c5fe4e2243bb19d6b3bf15896177b13768e6eb6': // "Copy a koan." // Git does not detect this as a copy without --find-copies-harder. $this->assertEqual(1, count($changes)); $c = $changes['koan']; $this->assertEqual( ArcanistDiffChangeType::TYPE_ADD, $c->getType()); break; case '6d9eb65a2c2b56dee64d72f59554c1cca748dd34': // "Move a koan." $this->assertEqual(2, count($changes)); $c = $changes['koan']; $this->assertEqual( ArcanistDiffChangeType::TYPE_MOVE_AWAY, $c->getType()); $c = $changes['koan2']; $this->assertEqual( ArcanistDiffChangeType::TYPE_MOVE_HERE, $c->getType()); break; case '141452e2a775ee86409e8779dd2eda767b4fe8ab': // "Add a koan." $this->assertEqual(1, count($changes)); $c = $changes['koan']; $this->assertEqual( ArcanistDiffChangeType::TYPE_ADD, $c->getType()); break; case '5dec8bf28557f078d1987c4e8cfb53d08310f522': // "Copy an image, and replace the original." // `image_2.png` is copied to `image.png` and then replaced. $this->assertEqual(2, count($changes)); $c = $changes['image.png']; $this->assertEqual( ArcanistDiffChangeType::TYPE_COPY_HERE, $c->getType()); $this->assertEqual( ArcanistDiffChangeType::FILE_BINARY, $c->getFileType()); $this->assertEqual( null, $c->getOriginalFileData()); $this->assertEqual( '8645053452b2cc2f955ef3944ac0831a', md5($c->getCurrentFileData())); $c = $changes['image_2.png']; $this->assertEqual( ArcanistDiffChangeType::TYPE_COPY_AWAY, $c->getType()); $this->assertEqual( ArcanistDiffChangeType::FILE_BINARY, $c->getFileType()); $this->assertEqual( '8645053452b2cc2f955ef3944ac0831a', md5($c->getOriginalFileData())); $this->assertEqual( 'c9ec1b952480da09b393ba672d9b13da', md5($c->getCurrentFileData())); break; case 'fb28468d25a5fdd063aca4ca559454c998a0af51': // "Multicopy image." // `image.png` is copied to `image_2.png` and `image_3.png` and then // deleted. Git detects this as a move and an add. $this->assertEqual(3, count($changes)); $c = $changes['image.png']; $this->assertEqual( ArcanistDiffChangeType::TYPE_MULTICOPY, $c->getType()); $this->assertEqual( ArcanistDiffChangeType::FILE_BINARY, $c->getFileType()); $this->assertEqual( '8645053452b2cc2f955ef3944ac0831a', md5($c->getOriginalFileData())); $this->assertEqual( null, $c->getCurrentFileData()); $c = $changes['image_2.png']; $this->assertEqual( ArcanistDiffChangeType::TYPE_COPY_HERE, $c->getType()); $this->assertEqual( ArcanistDiffChangeType::FILE_BINARY, $c->getFileType()); $this->assertEqual( null, $c->getOriginalFileData()); $this->assertEqual( '8645053452b2cc2f955ef3944ac0831a', md5($c->getCurrentFileData())); $c = $changes['image_3.png']; $this->assertEqual( ArcanistDiffChangeType::TYPE_MOVE_HERE, $c->getType()); $this->assertEqual( ArcanistDiffChangeType::FILE_BINARY, $c->getFileType()); $this->assertEqual( null, $c->getOriginalFileData()); $this->assertEqual( '8645053452b2cc2f955ef3944ac0831a', md5($c->getCurrentFileData())); break; case 'df340e88d8aba12e8f2b8827f01f0cd9f35eb758': // "Remove binary image." // `image_2.png` is deleted. $this->assertEqual(1, count($changes)); $c = $changes['image_2.png']; $this->assertEqual( ArcanistDiffChangeType::TYPE_DELETE, $c->getType()); $this->assertEqual( ArcanistDiffChangeType::FILE_BINARY, $c->getFileType()); $this->assertEqual( '8645053452b2cc2f955ef3944ac0831a', md5($c->getOriginalFileData())); $this->assertEqual( null, $c->getCurrentFileData()); break; case '3f5c6d735e64c25a04f83be48ef184b25b5282f0': // "Copy binary image." // `image_2.png` is copied to `image.png`. Git does not detect this as // a copy without --find-copies-harder. $this->assertEqual(1, count($changes)); $c = $changes['image.png']; $this->assertEqual( ArcanistDiffChangeType::TYPE_ADD, $c->getType()); $this->assertEqual( ArcanistDiffChangeType::FILE_BINARY, $c->getFileType()); $this->assertEqual( null, $c->getOriginalFileData()); $this->assertEqual( '8645053452b2cc2f955ef3944ac0831a', md5($c->getCurrentFileData())); break; case 'b454edb3bb29890ee5b3af5ef66ce6a24d15d882': // "Move binary image." // `image.png` is moved to `image_2.png`. $this->assertEqual(2, count($changes)); $c = $changes['image.png']; $this->assertEqual( ArcanistDiffChangeType::TYPE_MOVE_AWAY, $c->getType()); $this->assertEqual( ArcanistDiffChangeType::FILE_BINARY, $c->getFileType()); $this->assertEqual( '8645053452b2cc2f955ef3944ac0831a', md5($c->getOriginalFileData())); $this->assertEqual( null, $c->getCurrentFileData()); $c = $changes['image_2.png']; $this->assertEqual( ArcanistDiffChangeType::TYPE_MOVE_HERE, $c->getType()); $this->assertEqual( ArcanistDiffChangeType::FILE_BINARY, $c->getFileType()); $this->assertEqual( null, $c->getOriginalFileData()); $this->assertEqual( '8645053452b2cc2f955ef3944ac0831a', md5($c->getCurrentFileData())); break; case '5de5f3dfda1b7db2eb054e57699f05aaf1f4483e': // "Add a binary image." // `image.png` is added. $c = $changes['image.png']; $this->assertEqual( ArcanistDiffChangeType::TYPE_ADD, $c->getType()); $this->assertEqual( ArcanistDiffChangeType::FILE_BINARY, $c->getFileType()); $this->assertEqual( null, $c->getOriginalFileData()); $this->assertEqual( '8645053452b2cc2f955ef3944ac0831a', md5($c->getCurrentFileData())); break; case '176a4c2c3fd88b2d598ce41a55d9c3958be9fd2d': // "Convert \r\n newlines to \n newlines." case 'a73b28e139296d23ade768f2346038318b331f94': // "Add text with \r\n newlines." case '337ccec314075a2bdb4a912ef467d35d04a713e4': // "Convert \n newlines to \r\n newlines."; case '6d5e64a4a7a6a036c53b1d087184cb2c70099f2c': // "Remove tabs." case '49395994a1a8a06287e40a3b318be4349e8e0288': // "Add tabs." case 'a5a53c424f3c2a7e85f6aee35e834c8ec5b3dbe3': // "Add trailing newline." case 'd53dc614090c6c7d6d023e170877d7f611f18f5a': // "Remove trailing newline." case 'f19fb9fa1385c01b53bdb6d8842dd154e47151ec': // "Edit a text file." $this->assertEqual(1, count($changes)); $c = $changes['text']; $this->assertEqual( ArcanistDiffChangeType::TYPE_CHANGE, $c->getType()); $this->assertEqual( ArcanistDiffChangeType::FILE_TEXT, $c->getFileType()); break; case '228d7be4840313ed805c25c15bba0f7b188af3e6': // "Add a text file." // This commit is never reached because we skip the 0th commit junk. $this->assertTrue(true, pht('This is never reached.')); break; default: throw new Exception( pht('Commit %s has no change assertions!', $commit)); } } public function testTrailingContext() { // Diffs need to generate without extra trailing context, or 'patch' will // choke on them. $this->assertEqual( $this->loadResource('trailing-context.diff'), $this->loadOneChangeBundle( 'trailing-context.old', 'trailing-context.new')->toUnifiedDiff()); } public function testDisjointHunks() { // Diffs need to generate without overlapping hunks. $this->assertEqual( $this->loadResource('disjoint-hunks.diff'), $this->loadOneChangeBundle( 'disjoint-hunks.old', 'disjoint-hunks.new')->toUnifiedDiff()); } public function testNonlocalTrailingNewline() { // Diffs without changes near the end of the file should not generate a // bogus, change-free hunk if the file has no trailing newline. $this->assertEqual( $this->loadResource('trailing-newline.diff'), $this->loadOneChangeBundle( 'trailing-newline.old', 'trailing-newline.new')->toUnifiedDiff()); } public function testEncodeBase85() { $data = ''; for ($ii = 0; $ii <= 255; $ii++) { $data .= chr($ii); } for ($ii = 255; $ii >= 0; $ii--) { $data .= chr($ii); } $expect = Filesystem::readFile(dirname(__FILE__).'/base85/expect1.txt'); $expect = trim($expect); $this->assertEqual( $expect, ArcanistBundle::encodeBase85($data)); // This is just a large block of random binary data, it has no special // significance. $data = "\x56\x4c\xb3\x63\xe5\x4a\x9f\x03\xa3\x4c\xdd\x5d\x85\x86\x10". "\x30\x3f\xc1\x28\x51\xd8\xb2\x1a\xc3\x79\x15\x85\x31\x66\xf9". "\x8e\xe1\x20\x8f\x12\xa1\x94\x0e\xbf\xb6\x9c\xb5\xc0\x15\x43". "\x3d\xad\xed\x00\x3c\x16\xfa\x76\x2f\xed\x99\x3a\x78\x3e\xd1". "\x91\xf8\xb0\xca\xb9\x29\xfe\xd4\x0f\x16\x70\x19\xad\xd9\x42". "\x15\xb4\x8f\xd6\x8f\x80\x62\xe9\x48\x77\x9f\x38\x6d\x3f\xd6". "\x0e\x40\x68\x68\x93\xae\x75\x6d\x7f\x75\x9c\x80\x69\x94\x22". "\x87\xb6\xc0\x62\x6b\xab\x49\xb8\x91\xe9\x96\xbf\x04\xc2\x50". "\x30\xae\xea\xc1\x70\x8e\x91\xd0\xb6\xec\x56\x14\x78\xd5\x8a". "\x8c\x52\xd1\x3c\xde\x65\x21\xec\x93\xab\xcf\x7e\xf5\xfd\x6d". "\x2d\x69\xb9\x2e\xa3\x42\x7b\x4d\xa5\xfb\x28\x6d\x74\xa3\x7b". "\x3a\xc5\x34\x7c\x63\xa9\xf9\x8e\x34\x14\x42\xb0\xf1\x0e\xe2". "\xd0\xd2\x04\x81\xff\x62\xd5\xd9\x46\x3b\x36\x88\x8a\x93\x55". "\x02\x2c\xff\x9f\x48\xd6\x7a\xcb\xbf\x6a\x33\xaa\x6b\x08\x4c". "\x96\x98\x89\x53\x56\xb4\xb3\x9b\x06\xb1\xa0\x13\x69\xfa\x6a". "\xa8\x0d\x6a\xda\xb2\x6f\x62\x0b\xa8\xf6\x59\x29\x46\x7d\x04". "\x44\xeb\x90\x6f\xd7\xc7\xb6\xca\xc5\xeb\xde\x10\x9b\xbd\xf2". "\x66\x8e\xd0\x0b\xda\x8c\xeb\x90\x73\x73\x33\xe7\x6f\x26\x57". "\x4e\xfc\x95\xe0\xfc\x62\x93\xa7\x28\xe6\x0c\x46\x73\xdd\x01". "\xce\x43\x9b\x4e\x16\x74\x5b\x36\x92\x5a\x66\x4c\xe3\x9e\x90". "\x2d\x9a\x1a\x3d\x69\x39\x67\x04\xd6\xf8\x5f\x45\xee\xbb\xd4". "\x63\xcf\x8c\x9b\x31\x69\x98\x1a\x98\x57\x4b\xa9\x49\xf6\x1b". "\x76\x28\xd7\xe3\x8f\x63\x95\x5b\x06\xe2\xa8\x66\x60\xf9\x49". "\x4e\x40\x53\x32\x9b\x74\x36\xc0\x56\xf4\x33\xec\x83\xd2\x2c". "\x69\x60\x55\x11\x3b\x4f\xd6\x0a\xf6\x04\x38\x75\xb6\xc2\x82". "\x4d\xfa\x83\x56\xba\x35\x42\xc3\xcb\xdc\x28\xf4\x69\x48\xa9". "\xe0\x51\x41\x79\x66\xfe\x61\xd1\xf2\x9f\x7b\xde\xc4\x3e\x8f". "\x8f\xb6\x9c\x0a\x74\xf8\x71\x03\x37\x37\x30\x8d\x2a\x6a\xc9". "\x51\xa1\xe2\x34\xe5\x42\xdb\x4f\x61\x4e\x16\xfc\x23\x72\x12". "\x46\x53\x12\x82\x3e\x44\x63\x23\x82\xaa\xab\x7e\x8d\x70\x66". "\xf1\x94\x86\x02\xc5\x3e\x9c\x79\x17\x1e\x9f\x13\x89\x3d\x25". "\x45\xc9\x3b\x1e\xa0\x1a\x03\x20\x1c\x81\x6b\xfc\xb5\xc9\xe2". "\xda\xb1\x87\x34\xa0\xb2\x72\x36\x68\x12\x05\x53\x7c\x68\x6b". "\x1e\x2a\x56\x2a\x7e\x7f\xd0\x9c\x13\xa9\xb2\x4c\xe6\x8a\x65". "\xd7\x67\xad\xf3\xf3\x2b\x9c\xe8\x10\x07\x8a\xe2\x20\x67\xe4". "\x51\x47\xc1\x22\x91\x05\x22\x39\x1a\xef\x54\xd2\x8a\x88\x55". "\x3f\x83\xba\x73\xd4\x95\xc7\xb8\xa2\xfd\x4d\x4e\x5d\xff\xdd". "\xaf\x1a\xc2\x7e\xb5\xfa\x86\x5f\x93\x38\x5d\xca\x9a\x5a\x7e". "\xb7\x47\xd5\x5c\x6b\xf3\x32\x03\x11\x44\xe9\x49\x12\x40\x82". "\x67\x7d\x2a\x5a\x61\x81\xbd\x24\xaa\xd7\x7c\xc9\xcf\xaf\xb0". "\x3e\xb0\x43\xcd\xce\x21\xe4\x1b\x5a\xd6\x40\xf5\x0e\x23\xef". "\x70\xf4\xc6\xd2\xd7\x36\xd7\x20\xda\x8d\x39\x46\xea\xfc\x78". "\x55\xa2\x02\xd6\x77\x21\xc8\x97\x1e\xdf\x45\xde\x93\xa7\x74". "\xd8\x59\x10\x24\x8a\xe8\xcd\xe9\x00\xb5\x4e\xe6\x49\xb0\xde". "\x14\x1a\x5d\xdd\x38\x47\xb0\xc7\x1e\xec\x7c\x76\xc9\x21\x3c". "\x3a\x85\x4f\x71\x97\xed\x4a\x94\x2c\x51\x48\x9c\x43\x90\x70". "\xe9\x0e\x84\x55\xd2\xa4\x48\xfa\xfd\x54\x12\x11\xb9\x32\xfc". "\x1d\x66\xe7\x42\xe3\x5e\x65\xf4\x3d\xea\x1a\x53\xe3\x7b\x4b". "\xee\xdb\x74\xce\x30\xd3\x04\xcb\xda\xa4\xdd\xad\x98\x3a\x76". "\xe8\xba\x1b\x03\x53\xed\x46\x5d\xef\xd4\x34\xc2\x8d\xef\xae". "\x51\x35\x0f\x4d\x40\xaa\x3a\xdb\x50\x1a\xbe\x5f\x8b\xb8\x24". "\x40\x19\x8f\x8a\x6b\x44\x4f\x9b\xe0\xf4\x9c\x4b\xc4\x23\x37". "\xf0\xb3\xe1\x58\x9d\x0e\xd9\xa9\xf7\x3e\x86\x43\x9b\x5b\x90". "\x3c\xc0\x20\xa0\xc5\x86\x4f\xc6\xcb\xb5\xcb\xd4\x88\xc6\x72". "\x57\xa7\x57\x2c\x34\x26\x91\x44\x15\xa8\xf4\x88\xca\x74\x56". "\x9e\x12\x6c\xdf\x52\xef\xc0\xb4\x5c\x16\xe8\xaa\xf7\xb6\xf3". "\x7c\xda\xcd\x42\xf9\x1c\x40\x88\x44\x68\x4f\x1b\x5a\x7b\x8f". "\xc3\x47\x48\xd3\xf3\xe5\xf5\x66\x35\x48\xbe\x64\xdf\xfe\x35". "\xf1\xc3\xe4\xa8\xfc\x86\xfb\x69\x20\xc9\xf4\x16\x96\xc1\x7a". "\x51\x14\x77\xa4\x6e\x13\xe8\x59\x35\x24\xf1\xe5\xfe\xe9\x98". "\x0d\xd1\xe8\xce\x9c\x7f\xf8\x3b\x79\x39\x3a\x1d\xa3\x77\xef". "\x4f\x4b\x59\x73\x03\xb3\xfe\xae\x70\x2a\x3a\xf0\x79\x9d\x7e". "\x9b\xaa\xb1\x18\xf9\x43\x69\xf3\x55\x46\xad\x38\xa2\xf1\xcb". "\xce\x37\xa9\x88\x20\x38\xea\x19\x29\x95\x8c\x75\x06\x9d\x1d". "\x9e\xf2\xb7\x64\x98\x21\x36\x90\x92\xf8\xb8\x89\x1e\x5c\x5d". "\x09\x3b\x52\xc5\x6a\x87\x7e\x46\xca\x8c\xdf\xe7\xca\xa9\x7b". "\x11\x63\x0f\x9e\x42\x9a\x3e\xe0\x8b\x80\x9e\x91\x76\x88\x9a". "\xa1\xe2\x96\xae\xfb\x18\x39\xdc\x92\x99\x34\xfd\x98\x20\xa8". "\x89\x61\x2c\x26\xe0\xb8\x83\xa7\xe7\x50\x42\x8f\xfc\x36\x66". "\x6b\x25\xc5\x6d\xb4\x31\xe1\x4d\x0f\x2e\xf8\x44\xe2\xb6\x6a". "\x6d\xfe\x83\x9e\x2c\x07\x2f\x15\x41\xf3\xe7\xa6\x18\x2b\x84". "\x7e\xeb\x43\xcc\xbb\xdb\xa9\x54\x5c\xbc\x59\x6a\xdc\x26\x2a". "\xf4\x59\xa7\x75\xa4\xac\xed\x73\x8f\x16\x43\x0d\x97\x10\x2c". "\x70\xef\x9e\xb2\xc9\xdf\xe6\xa7\x9b\x08\x79\xa3\xf7\x99\xf5". "\x59\xe4\xd5\x89\x10\xe5\xc9\xf7\xe7\x29\x72\x06\xc6\x54\xc3". "\xcd\xd0\xff\x69\xf8\xdf\x19\xf2\x66\x1c\x69\x40\xbc\x97\xf1". "\x49\x5e\x78\x62\x52\x46\x7f\xcf\x44\x50\x8b\x5f\xe7\xa8\xeb". "\xd5\x84\x24\x81\xc0\x2c\x65\xf7\x95\xbd\xf2\x8e\x43\xfb\x6a". "\x49\x3c\x6a\xe5\x2a\x39\xf0\xfa\x89\x59\x5f\x39\x75\xb4\x6f". "\x04\xf1\xe0\x2c\xcd\x77\x34\xec\x6b\x45\x16\xe3\x18\x24\x05". "\xb9\x68\xc1\x4e\x71\x4b\xff\x88\x18\xea\x0d\x56\x49\x55\xdf". "\xe5\xb0\x59\xdb\x74\x9e\x0b\x38\x03\x9f\x10\x6f\xd9\x34\x07". "\x44\x29\x08\xb1\xd4\x77\xc6\x84\x0d\xbb\xb5\xd5\x09\x05\x19". "\x01\x62\x29\x45\x52\x1d\xc6\x4f\x25\x78\x7e\xbc\xae\x07\xb3". "\xd4\xe0\x19\x91\x03\xd6\x8d\x2f\x00\xc9\xb2\x66\x3b\x4e\x3d". "\x75\xf7\x23\x9a\x3e\xa4\xd5\x7f\x75\x47\xd0\xbc\xc3\xc8\x2a". "\xdc\x85\x09\x6c\x0c\x90\x38\xd8\xef\xcf\xf4\x7a\x1b\xc7\x76". "\xe0\xdb\x81\xa8\x1b\x2b\x8d\xd4\x36\x90\x76\xde\x8a\x90\xc8". "\x5b\x05\x00\xeb\xb3\x20\xce\x6e\x5c\xb9\x35\x3d\x95\x3a\x79". "\x4a\x60\xeb\x23\x11\xfb\x90\x2d\xf6\xb7\x05\x4a\x43\x41\x79". "\x51\xaa\xe6\x90\x0a\x71\x87\x80\xbe\xb0\x89\x0f\xd3\x84\x19". "\xce\x6c\xf9\xbb\x1b\x15\x4d\x0f\x33\x65\xf7\x9e\x3a\xd9\x8c". "\x02\x43\xcf\xdf\xb2\x60\xc1\x4c\xe9\xa5\x3c\xaf\xfa\x41\x2d". "\xb9\x1f\x45\x32\xcb\x39\x2f\x94\xae\x44\x6d\x69\xc1\xc9\x57". "\x8c\xe5\xf4\xa4\x3a\xb6\x70\x61\xf9\xbb\x41\xdc\x78\xf0\xf7". "\xbf\xa8\x8e\xe3\x77\x51\xce\x25\x2f\xdf\x27\x6b\x07\x30\x9f". "\xce\xdb\x59\x58\xaa\xb2\x2e\xdc\x90\x92\x82\x55\xfe\x25\x36". "\x49\x7f\x6d\x2d\x39\x51\xef\x3d\xc8\xa3\x87\x0b\xe7\xf2\xac". "\x90\xa0\x1d\xd8\xc7\xea\x93\x53\x3b\x21\x84\x2e\x52\x6c\xfb". "\x4f\x31\xda\xd1\xea\x45\x3e\xdc\xeb\x52\x81\x8c\x2b\xf4\x2a". "\xbc\x01\xc4\xe7\x68\x36\x9c\xd5\x2d\xc1\x61\xcb\x9a\x5f\x18". "\x00\x6a\xc8\x9a\x4e\xfd\x31\x5b\xce\x90\x4e\x45\xff\x7f\xea". "\xb2\x26\xad\xc1\x3a\x21\xa9\xe8\x7c\x14\xae\x81\x1e\xbe\xa3". "\x6d\xda\x92\x1b\xeb\xf2\x69\x76\x3e\xf1\x2b\xf7\x1a\x45\xd5". "\xb3\x81\xb1\xbe\x80\x7f\x24\xba\x0e\xd5\x68\x34\x3f\x1a\x29". "\x15\x0e\xc2\x26\x62\x0c\xaa\xa9\x20\x4c\x61\x65\x49\x07\xbe". "\x69\xf4\xc9\xec\x2f\x1c\xfa\x59\x2e\x72\xc0\x17\xc5\x4c\xfa". "\xba\x2f\x64\xab\xa9\xb4\xcb\xdc\xcb\x25\x5f\xcf\x0c\x87\xcc". "\xf0\x36\x2b\xce\x81\x5a\x22\x85\xa0\x50\x50\x97\x8e\xda\x36". "\x80\x74\xb5\x1e\x02\x3f\xd7\xc8\x29\x11\xeb\x1d\x3d\x74\x9f". "\x26\x1a\xa4\x3d\xf9\x0e\xf0\x2d\x5c\xa9\x43\xbf\x51\x6c\x8d". "\xe6\x78\xe0\x67\x57\xf0\xc8\x0e\x97\x9c\x57\x23\x30\xac\x63". "\xdf\x46\x98\xa4\xaf\x4e\xa7\xe5\xac\x31\xbd\xeb\x6a\xa0\xb0". "\xe4\x94\x7e\x51\xf6\x89\x81\x3e\xab\x4f\x64\xb7\xc5\x51\x71". "\xcd\x74\x02\xa9\x02\x99\x5c\xab\x0e\x14\x47\x3b\x04\xc1\x9b". "\x59\x1a\x93\x92\x4c\x71\x20\x5f\x6e\xd3\xf3\xa7\x47\x1b\x39". "\x3e\x73\x69\xe2\xec\xcb\x52\xb3\x5c\x7a\x95\x25\x3f\x16\x98". "\x60\xa8\xa2\x5d\xc4\x5a\x67\xe4\x11\x06\x06\xf9\x7a\xb4\x14". "\xe0\xbc\x7b\x13\x1d\x0f\xf2\xca\x0b\xd4\xaa\x71\x35\x3e\xd6". "\x2e\x2e\x5d\x7b\x15\xc9\x23\x1a\xa9\x24\x31\x48\xd4\xcf\x4a". "\xf4\x32\x17\x9b\x1d\x4b\xfe\x49\x69\xd6\xc0\x8f\xb9\xdb\x72". "\x52\x2c\xe8\xf3\xc4\xfc\x46\xf5\xb8\x1b\x05\x06\xcf\xcc\x23". "\x34\xbf\x25\x6a\xea\x3c\xc7\x64\xd4\xd5\xb3\x67\xed\x24\x27". "\xd3\x67\xc1\xbd\x9f\x7b\x7d\x19\x04\x5c\xd1\x96\x7e\xa5\xc7". "\xbb\xb2\x84\x68\x98\x38\x11\x90\xfb\x62\x15\xfd\xe6\xb7\x24". "\x77\xb2\x78\xc7\x73\x91\xc9\x60\x1d\x91\x6d\x04\x2b\x41\xe9". "\xc9\xfa\xe4\x98\x54\x83\x9a\x6e\x76\x8c\x21\xf9\x91\x38\x1f". "\xdc\xfe\x13\x09\x30\xd7\x53\x63\x62\xba\xe3\x2c\x70\xd5\xfc". "\x78\x35\x36\x79\x5d\xb6\x0e\x35\x3d\x46\x87\xfb\xf5\x64\x1f". "\x3e\xfd\x2f\x1c\xbb\xed\x95\x2d\xd6\x63\xdc\xa7\x6a\x39\x8f". "\xbd\xcb\x79\x95\xe9\x45\xbf\xe4\x3e\x05\x55\x00\xdb\x33\x28". "\x3a\x6c\xe2\x35\xbb\xac\x70\x52\x2b\xac\x4e\x11\x44\x58\x16". "\x21\xb4\xae\x0d\x6a\xb9\xdc\x85\x5d\x90\x11\x26\x85\xdb\xc3". "\xf0\x38\x6f\x8a\xff\x12\xf0\xc9\x9e\xf0\xfc\xae\x94\x11\x4d". "\xce\x96\x29\x09\x6c\xf4\x2a\x6c\xda\x1e\x4c\x4a\xa2\x96\x5a". "\xef\xc6\x38\x5c\x60\xa2\x28\x13\x58\x73\x96\xde\x59\x2a\x57". "\x64\x6c\x14\x94\x8a\x2e\x8e\x21\x3f\xa2\x43\xde\xf6\x2d\x23". "\x74\x5c\xbd\x7a\x10\xdb\x17\xa8\x93\xd0\x74\x86\x9d\x33\x07". "\x48\xee\xac\x18\x6d\x64\x61\x7b\x61\x2b\xa4\xa2\xab\x99\x59". "\xbe\x19\xd7\x19\x41\x1e\x61\x87\xad\x40\x5b\x69\x8c\x32\xf5". "\xb6\x49\xbe\x1f\xad\xd8\x0f\x3e\xd9\x62\xac\x3a\x76\xde\x32". "\xa3\xb2\x41\x95\xad\x17\x23\xab\xa1\x37\x9c\xab\x73\x79\x70". "\xd6\x66\x0d\x6e\x4d\x8b\xa0\xac\xe3\x44\x1e\x0a\xee\xf0\x74". "\x64\xd8\x44\xd1\x6c\xa6\xd5\x36\x2e\xd9\x55\x6e\x90\x63\xb7". "\xf7\x8e\xc6\x28\xa3\x40\x00\x60\x9a\x3c\xfe\xff\x03\x30\x11". "\x18\x92\x2f\x5b\x23\xe1\x4e\x99\xe4\x82\xc9\x51\xe2\x15\x6a". "\x76\x5c\x67\xae\xa3\xa2\x9c\x85\x51\xe0\x44\x89\x63\xa5\x71". "\x99\xbc\x2d\x9c\xab\x9a\xfb\x20\x37\x58\xd6\x2d\x8b\x7d\x42". "\x13\x35\x44\x4c\x11\x97\x66\x27\x17\xac\x44\xe8\x6a\x03\x78". "\xa2\x88\xc6\x36\x71\x5a\x5a\x5a\x72\xa3\xe9\x72\x0c\x91\x31". "\xfc\xae\x7b\xa0\x75\x21\x0a\xc1\x4b\x95\xcb\xe3\xc2\xee\x03". "\x0f\xb8\xb2\x51\xc7\xc8\x9c\x8d\x6d\x3a\xe7\x4e\x2c\xaa\xeb". "\x5e\x49\x93\xe0\x8f\xa1\x54\x93\xe7\x7c\x5d\x31\xc7\x05\x00". "\x28\x14\x57\x47\xb3\x05\x2d\x17\x92\x28\x45\xee\x85\x3a\x59". "\xb6\xa6\x04\xc0\x5c\x07\x1f\xe6\x5b\x36\x53\x62\x82\x64\xd5". "\xb6\xf2\xf5\x67\x19\x11\xee\xd2\x70\xc5\x14\x63\xc1\x75\xe1". "\x24\xe5\x01\x59\x52\x7c\x88\x17\xb4\xe0\x15\xe9\x12\x05\xcd". "\x88\x7a\xd5\xea\x45\xc3\xbb\x65\xd4\xdd\x0d\xde\x36\x94\x98". "\x0d\x2c\xfb\x3c\x2f\x69\xd0\x28\xe2\x85\xd9\x27\xf3\x7a\xad". "\x50\x68\x96\x54\x5e\xeb\xbc\x2a\x74\xde\xf3\x4e\x8b\x27\x0a". "\xcf\x4c\x60\x40\xe8\xc5\x72\xab\x8c\xfd\xe9\xab\xff\x51\xe5". "\xd6\xea\x9e\x34\x73\xe1\xe6\xf8\x5b\xb1\x10\xf0\xf9\x2d\x23". "\x0e\xfe\xe5\xf4\x8d\xb6\x6d\x37\x14\xed\x54\x97\x92\x5c\x68". "\x40\x88\xf1\x43\x29\xef\x5e\x96\x77\xa2\xe8\x3c\xae\x7f\xb1". "\x99\x17\xa7\x0c\x6f\xe2\x43\x32\x9b\x14\x43\xf2\x15\x6b\x13". "\x10\x68\x56\x0b\xaa\x06\x2e\xc0\xf8\xde\x9e\x54\x9d\xba\xff". "\x76\x26\x6d\x5e\x9e\x88\x3a\x2b\x9b\x20\x43\xb9\x1a\x0e\x58". "\x65\xec\xdb\x9e\x97\xb8\xfb\x03\x6c\xb0\x7f\xa2\xf1\xf4\x27". "\x24\x21\x47\x51\x21\x40\x45\x28\x71\xf7\xa1\x6b\xbe\x0e\xc8". "\x3f\x9b\xda\x62\x9d\x73\xf7\x5f\x70\x6c\xba\x1e\xeb\x16\x5c". "\x2e\x44\x0a\x22\x02\x6c\xbe\xb9\x69\x93\xfd\xa5\x33\x26\x64". "\x24\x6c\xc2\x3d\x2f\xf3\xd1\x97\xde\x60\x43\x1c\x0d\x1b\x94". "\xb3\x48\x45\x7c\xd5\xd0\x71\x4d\xad\xbf\xa4\x0a\x22\x27\x04". "\x38\x84\x19\x66\x63\xf0\xf3\xfc\xb0\xf3\x1d\xea\xba\xb9\xe4". "\xe5\x80\xed\xe3\xf1\x78\x24\xc3\x25\x27\x71\x81\xc2\xec\x54". "\xed\xcc\x63\xf7\x39\xcd\x83\xdf\x32\x88\xc0\x3b\xd4\x62\xb8". "\xea\x34\xd8\xcf\xbc\x3a\x89\x38\x64\x60\x44\xde\xb6\x76\x59". "\xb1\x95\x6a\x26\x08\xf0\xf4\x71\x25\x8b\xf8\x81\xdd\x0d\x2f". "\x8c\xe2\x70\xc2\x96\xc2\xd8\x9b\xe4\x3f\xec\x8b\xfd\xbd\xc9". "\x36\x33\xb7\xbc\x59\x37\x19\x09\x30\x5e\xef\x67\xae\x67\x48". "\x72\x0b\xf4\x2a\x82\xff\xcb\xd7\xd9\x9d\x6d\x7c\xa6\x20\x42". "\x50\x2b\x0a\x2f\x45\x99\x5b\x76\x6d\x99\x39\xa9\xb6\x32\x06". "\x11\xf8\x19\xd1\x3f\xc0\xd6\x1f\x67\xfa\xd5\xae\x7a\x71\x8c". "\xbc\x3d\xb4\x5f\x5c\x81\x7c\xa1\x39\x70\x0a\x17\x24\xb7\x22". "\x86\x50\xd8\x1f\xc8\x6c\x59\x9a\xdc\xf0\x71\x01\xda\xd8\x53". "\x98\x1c\x73\x36\xf1\x09\x86\xc9\xa7\x26\x25\xc0\x03\x3e\x13". "\x4e\x29\xeb\xf0\x8d\xe3\x38\x03\x54\xee\x37\xfb\x51\x2e\xb4". "\xf6\x12\x1f\xb2\x8c\x66\x75\x00\x30\x5b\xef\x59\xf9\x63\xa9". "\x74\x07\x91\xe4\x9c\xb7\xc9\x89\xd9\xa9\x51\x93\xcb\xb1\xa7". "\x64\x08\x79\x8f\xb4\x6d\x09\xd7\xc5\xbf\x0a\xdb\x50\xe0\x1c". "\x83\xca\xf8\xcf\xa7\x81\xbb\x0b\xe6\xcf\x1b\x0e\x0a\xe0\xcd". "\x68\xe2\xde\xc4\x2d\xba\x55\xc7\xc7\x1e\x6c\x5e\xca\x9b\x20". "\x75\x96\x94\x92\x84\xec\xf5\x22\x25\x78\x67\xcd\xbe\x01\xfe". "\x53\xa5\xcc\x6a\x40\x33\x83\xa4\x7a\x44\x93\x0b\xf9\x4c\xb2". "\x95\xb6\x7e\x4b\xa4\xc8\x86\xfe\x8a\xf1\x77\x40\x56\x13\xc1". "\x31\x2c\x8c\x4a\xa8\x89\x61\x0c\x39\x33\x78\x8c\xd5\x50\x3b". "\x89\xc3\xd3\x80\x1c\xa7\xb6\x36\xc2\x00\x8d\x0a\x7f\xcc\xd3". "\x20\x74\x60\x70\x36\x7d\xda\xdc\xc4\x49\x04\xf0\xe6\x6c\xd1". "\xbe\xcb\xfb\xf1\xa2\xd6\xd4\xe4\x97\x3f\x35\x09\x5b\xda\x06". "\x6b\x6d\x86\x53\x23\x0c\x26\x51\x2a\x15\xaa\xe2\x73\xfb\xc7". "\x41\x54\xdc\x5d\x99\x0b\x0a\x1e\xd4\xdb\x70\xa3\x8e\xfd\x5b". "\xf0\xa8\x3e\x9b\xff\x57\x98\xbc\xd9\x2a\x56\xd3\x19\xf9\x0b". "\xd9\x67\x0f\x10\x9c\x23\xe5\x6b\x12\xc6\xb6\x4b\xd1\x0c\xe9". "\x45\x36\xdf\x54\x6f\xcc\xfe\xb5\xcc\xb9\xfe\xde\xc8\xb5\xc9". "\x04\x59\x61\x75\x1e\x72\x37\x54\xfd\xc6\xc3\x7e\x74\xae\x55". "\x31\x6a\xbc\x8a\xd8\x45\x91\xe2\x8d\x20\x97\x71\xe7\x55\xd6". "\x8a\xb8\x82\x2a\x27\x4f\xdc\x53\x89\x28\xf7\x3a\xfe\x07\xef". "\x60\xb2\x32\x7c\xbc\x13\xc4\x3d\xda\xd7\xfb\xb8\x61\x7d\x69". "\xae\x0e\x9a\x71\xd6\x00\x26\x97\xff\xdb\xe6\xbe\x45\x7a\xb5". "\x00\x31\xfd\x70\xcc\xd7\x34\x88\xe4\x05\x61\xf5\x72\x1d\x14". "\xf0\x7e\x90\xdb\x0e\xc7\xda\xd4\xf3\x99\xd4\x60\xd9\xa7\xc8". "\x5b\x33\x34\xb5\x23\x74\x2c\x5f\x6b\x56\x95\x9c\x1b\x2a\xac". "\xf9\xfe\x46\xc3\xf1\x9b\x24\x7e\x4b\xca\x25\x58\x41\x10\x63". "\xe8\xe7\x68\xda\xcc\xb6\x4d\x5b\x8f\xc9\xa9\x31\xeb\x5c\x2a". "\xcf\x9d\x89\xd5\x51\x93\x80\x30\xf4\xc9\x2c\x8c\xb8\x8c\x62". "\xd6\x33\xbd\x95\x9f\xfa\x19\xf2\x48\x28\x09\x73\xc9\x53\x61". "\x94\x3a\x62\x68\x6c\xc6\xd6\x0a\xb4\xae\x27\x96\xfb\x29\xd7". "\x46\x67\x11\x7a\xe8\x3a\x9a\x3f\xf4\x9a\x75\xed\x24\x67\x45". "\x79\xdc\x8b\x19\xf2\xef\x57\xaa\xc7\x84\xff\x9d\x2d\xc3\xa8". "\x85\x54\xb7\x9d\xe1\xd6\x2b\xe9\x31\x9d\x6c\xb8\x4e\x76\x50". "\x80\x44\x46\x8f\x5e\x7e\x20\xaa\xa0\x8a\x36\x6b\xef\xd1\x75". "\xf8\x3f\x20\xdd\x09\x73\xbf\xa5\xf7\xb4\x87\xb2\x44\xc0\x0f". "\x10\xc0\x95\x2e\x8a\x42\xfa\xc3\x49\x17\xb9\xb5\x1a\xc3\x80". "\x93\x0c\xd8\xe3\xcd\xa4\x38\x61\x7a\x22\x73\x8e\x32\x8f\x55". "\x9c\x91\x08\xd9\x65\xa9\x02\x28\xc6\x59\xc8\x51\x32\x20\x48". "\xea\x2c\xae\x0e\xa6\x35\x5b\xe2\x63\xf9\xf2\x9d\x5f\xe3\x45". "\xdc\x41\xba\xfb\x40\xcc\x8d\xde\x6c\x3d\x50\x97\x9d\x83\xa0". "\xda\x41\x61\xba\xaf\xf8\x74\xd2\x21\x7b\x09\xcc\x83\xe1\x08". "\x01\x04\x42\xce\xcb\xec\x1d\x6b\xb7\x6f\x0f\x4b\xd4\x53\x90". "\x55\x3b\xcf\x9f\x93\xb8\xad\xce\x5f\x13\x83\xb3\x89\x6f\x5a". "\x1b\xa4\xf5\x95\x4b\xb4\x22\x22\x1d\x35\xaa\xfa\xc7\x14\x8c". "\xcd\x50\x66\x14\x47\xff\x67\xb2\xf8\x12\x09\xb3\x8a\xe5\x7d". "\xb8\xc9\xe4\x89\xf7\xa4\xb5\x70\xfa\x2d\xeb\x95\x89\xec\xbb". "\x49\x59\xd2\xc1\x6d\x0e\x06\xe4\x5e\xd5\x13\x13\x0d\x72\x6e". "\xf0\x6d\xa9\xd5\xe7\x54\x68\x35\xcd\xd0\xd5\xa6\xe5\xb2\xe4". "\xb1\x19\xe4\xf1\xe3\x8a\x56\x4c\x3b\x3d\xb8\x03\xfe\x22\x2f". "\xc6\xdc\x88\x7b\xca\x5c\xc6\xdd\x17\x34\x08\x22\xf0\x17\x61". "\x0e\x60\x9c\xb4\x27\x57\x30\x6e\xb8\x4f\xdd\x25\x7b\xef\x9e". "\x8e\x88\x6b\xd8\x10\x23\xc2\x44\x53\x73\x64\x8f\x40\x22\xe1". "\xe8\xa2\xb0\x3f\x8a\x07\x66\xcd\x64\x4f\x9c\x1e\x89\x76\x04". "\x6d\xab\xc2\xbb\x16\x85\x80\x01\xa5\xb1\xe2\x12\x04\x2e\x39". "\x87\x8c\xee\xbc\xfb\x07\x6d\x03\x4c\x3a\xa5\x7b\x95\xd9\xd7". "\xd6\xee\x2b\xe9\xcb\xe6\xec\xa8\x84\x6a\x42\xf9\xb2\x25\xc8". "\xf3\x6a\xaa\x34\x3b\xd9\x72\xd9\x70\x81\x3b\xd4\x5e\x66\x97". "\x1b\xe6\x2b\x88\x71\x82\xa3\x8a\x98\xb0\x16\xd9\xbb\x97\x8b". "\x57\x79\x41\x56\x6e\xc2\x8f\xdf\xfa\x5b\xc7\x68\x5b\xb8\x09". "\x41\x31\x7c\x19\xe1\x95\x2e\x05\x4c\xac\x38\x81\xda\xb3\x8b". "\x3e\x1c\x79\x9a\x31\xac\x3e\x3d\x6d\xab\xf3\x5a\x5e\xc7\x6e". "\x8e\x39\xcd\x7b\x6f\x62\xee\xb9\x73\xdd\x82\x42\x6f\x09\xe4". "\xc3\xae\x92\xe8\x18\x99\xa0\x5e\xa2\x12\xf4\xe2\xe0\xe6\x95". "\x58\x3a\x45\xad\xfe\x23\x79\x5f\x82\xce\x95\x88\x73\xeb\x46". "\xc8\x00\xac\xc3\x2a\xdc\x7e\xab\x9b\xf8\xbb\x46\x5c\xa8\x46". "\xbc\xfd\x99\xae\x4c\xa7\x77\xeb\x7c\x58\xbf\xbb\x52\x68\x62". "\x3d\x0b\x79\x64\x38\x65\xa7\xcb\x7b\xe9\xb2\x33\xb5\x59\x52". "\x7b\x17\xb4\x02\x2b\x07\x0d\x3a\x11\x57\x92\xa5\x22\x2b\xbc". "\xe6\x97\x05\x12\x05\xe7\x91\xe3\xfa\xae\x15\xbe\x20\xe5\x5c". "\x71\x24\x80\x85\xc9\x66\xc1\x53\x5c\x8f\x08\xd4\x52\xe1\x10". "\xb6\xd6\x20\x08\x01\x79\x33\x9f\x1b\xbd\xa0\xab\x7c\xb1\xd9". "\xdc\xca\x44\x22\x49\xb7\xb7\x3d\x84\xac\x92\xf4\xfa\x0a\xc9". "\xc5\xb2\x42\x2b\x9a\x63\xbb\x8a\x82\x04\x2f\xf7\xe9\x30\x05". "\x67\x32\xd1\x41\x1a\x69\x6e\xb9\xf8\x5f\x6d\xb7\xe5\x4e\x85". "\x21\xfa\x16\x8a\x44\xfd\xf6\xd9\xa2\x5f\x68\x2b\xf3\xe2\x3c". "\x8a\x69\xd2\xc1\x38\xed\x83\xef\x0d\x53\x86\x93\x32\x23\xc6". "\x14\x0c\xb0\xb6\x6e\x77\xa4\x20\x0f\xb1\x6e\xe2\xce\xca\x6f". "\x93\x1c\x3a\x8f\xd0\xd2\x5a\x6e\x30\xd6\x8e\x5f\x4b\xa5\xef". "\xa9\x62\xeb\x28\xa0\x5e\x3f\xc1\xbc\x0a\x68\xab\xd7\xfa\xa2". "\xb7\x8f\x12\xb0\x99\xbc\x93\x20\xb8\x95\x8d\xca\xc7\xa7\xd9". "\x2e\x19\xac\x06\xb9\x4e\x56\x8e\x74\xef\x2a\x04\xd8\x75\x04". "\x38\x2a\xc7\xa0\xa4\x89\xf3\xa4\x8a\xd4\x2c\x2c\x58\x6f\x00". "\x03\x23\xb8\xaf\x02\x48\x7d\x50\x46\x6f\x5a\x08\x41\xe3\x56". "\x6d\xcb\xe2\x4f\xea\x8e\xab\x74\xcd\xf9\xef\xcf\xf9\x1e\xf1". "\xf8\xb9\x6c\xaa\x3b\x37\xd1\x21\x42\x67\xec\xd6\x44\x55\x33". "\xe8\x1d\xa4\x18\xf3\x73\x82\xb4\x50\x59\xc2\x34\x36\x05\xeb"; $expect = Filesystem::readFile(dirname(__FILE__).'/base85/expect2.txt'); $expect = trim($expect); $this->assertEqual( $expect, ArcanistBundle::encodeBase85($data)); } } diff --git a/src/parser/__tests__/ArcanistCommentRemoverTestCase.php b/src/parser/__tests__/ArcanistCommentRemoverTestCase.php index 26603e2b..d89a3d08 100644 --- a/src/parser/__tests__/ArcanistCommentRemoverTestCase.php +++ b/src/parser/__tests__/ArcanistCommentRemoverTestCase.php @@ -1,32 +1,32 @@ assertEqual($expect, ArcanistCommentRemover::removeComments($test)); } } diff --git a/src/parser/__tests__/ArcanistDiffParserTestCase.php b/src/parser/__tests__/ArcanistDiffParserTestCase.php index 9322063c..0976fb9b 100644 --- a/src/parser/__tests__/ArcanistDiffParserTestCase.php +++ b/src/parser/__tests__/ArcanistDiffParserTestCase.php @@ -1,687 +1,687 @@ parseDiff($root.$file); } } private function parseDiff($diff_file) { $contents = Filesystem::readFile($diff_file); $file = basename($diff_file); $parser = new ArcanistDiffParser(); $changes = $parser->parseDiff($contents); switch ($file) { case 'colorized.hggitdiff': $this->assertEqual(1, count($changes)); break; case 'basic-missing-both-newlines-plus.udiff': case 'basic-missing-both-newlines.udiff': case 'basic-missing-new-newline-plus.udiff': case 'basic-missing-new-newline.udiff': case 'basic-missing-old-newline-plus.udiff': case 'basic-missing-old-newline.udiff': $expect_old = strpos($file, '-old-') || strpos($file, '-both-'); $expect_new = strpos($file, '-new-') || strpos($file, '-both-'); $expect_two = strpos($file, '-plus'); $this->assertEqual(count($changes), $expect_two ? 2 : 1); $change = reset($changes); $this->assertTrue($change !== null); $hunks = $change->getHunks(); $this->assertEqual(1, count($hunks)); $hunk = reset($hunks); $this->assertEqual((bool)$expect_old, $hunk->getIsMissingOldNewline()); $this->assertEqual((bool)$expect_new, $hunk->getIsMissingNewNewline()); break; case 'basic-binary.udiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual( ArcanistDiffChangeType::FILE_BINARY, $change->getFileType()); break; case 'basic-multi-hunk.udiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $hunks = $change->getHunks(); $this->assertEqual(4, count($hunks)); $this->assertEqual('right', $change->getCurrentPath()); $this->assertEqual('left', $change->getOldPath()); break; case 'basic-multi-hunk-content.svndiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $hunks = $change->getHunks(); $this->assertEqual(2, count($hunks)); $there_is_a_literal_trailing_space_here = ' '; $corpus_0 = <<assertEqual( $corpus_0, $hunks[0]->getCorpus()); $this->assertEqual( $corpus_1, $hunks[1]->getCorpus()); break; case 'svn-ignore-whitespace-only.svndiff': $this->assertEqual(2, count($changes)); $hunks = reset($changes)->getHunks(); $this->assertEqual(0, count($hunks)); break; case 'svn-property-add.svndiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $hunks = reset($changes)->getHunks(); $this->assertEqual(1, count($hunks)); $this->assertEqual( array( 'duck' => 'quack', ), $change->getNewProperties()); break; case 'svn-property-modify.svndiff': $this->assertEqual(2, count($changes)); $change = array_shift($changes); $this->assertEqual(0, count($change->getHunks())); $this->assertEqual( array( 'svn:ignore' => '*.phpz', ), $change->getOldProperties()); $this->assertEqual( array( 'svn:ignore' => '*.php', ), $change->getNewProperties()); $change = array_shift($changes); $this->assertEqual(0, count($change->getHunks())); $this->assertEqual( array( 'svn:special' => '*', ), $change->getOldProperties()); $this->assertEqual( array( 'svn:special' => 'moo', ), $change->getNewProperties()); break; case 'svn-property-delete.svndiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(0, count($change->getHunks())); $this->assertEqual( $change->getOldProperties(), array( 'svn:special' => '*', )); $this->assertEqual( array( ), $change->getNewProperties()); break; case 'svn-property-merged.svndiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(count($change->getHunks()), 0); $this->assertEqual( $change->getOldProperties(), array()); $this->assertEqual( $change->getNewProperties(), array()); break; case 'svn-property-merge.svndiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(count($change->getHunks()), 0); $this->assertEqual( $change->getOldProperties(), array( )); $this->assertEqual( $change->getNewProperties(), array( 'svn:mergeinfo' => <<assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(count($change->getHunks()), 0); $this->assertEqual( $change->getOldProperties(), array( )); $this->assertEqual( $change->getNewProperties(), array( 'svn:executable' => '*', )); break; case 'svn-binary-add.svndiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual( ArcanistDiffChangeType::FILE_BINARY, $change->getFileType()); $this->assertEqual(0, count($change->getHunks())); $this->assertEqual( array( 'svn:mime-type' => 'application/octet-stream', ), $change->getNewProperties()); break; case 'svn-binary-diff.svndiff': case 'svn-binary-diff-freebsd.svndiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual( ArcanistDiffChangeType::FILE_BINARY, $change->getFileType()); $this->assertEqual(count($change->getHunks()), 0); break; case 'git-delete-file.gitdiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual( ArcanistDiffChangeType::TYPE_DELETE, $change->getType()); $this->assertEqual( 'scripts/intern/test/testfile2', $change->getCurrentPath()); $this->assertEqual(1, count($change->getHunks())); break; case 'git-binary-change.gitdiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual( ArcanistDiffChangeType::FILE_BINARY, $change->getFileType()); $this->assertEqual(0, count($change->getHunks())); break; case 'git-filemode-change.gitdiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual(1, count($change->getHunks())); $this->assertEqual( array( 'unix:filemode' => '100644', ), $change->getOldProperties()); $this->assertEqual( array( 'unix:filemode' => '100755', ), $change->getNewProperties()); break; case 'git-filemode-change-only.gitdiff': $this->assertEqual(count($changes), 2); $change = reset($changes); $this->assertEqual(count($change->getHunks()), 0); $this->assertEqual( array( 'unix:filemode' => '100644', ), $change->getOldProperties()); $this->assertEqual( array( 'unix:filemode' => '100755', ), $change->getNewProperties()); break; case 'svn-empty-file.svndiff': $this->assertEqual(2, count($changes)); $change = array_shift($changes); $this->assertEqual(0, count($change->getHunks())); break; case 'git-ignore-whitespace-only.gitdiff': $this->assertEqual(count($changes), 2); $change = array_shift($changes); $this->assertEqual(count($change->getHunks()), 0); $this->assertEqual( $change->getOldPath(), 'scripts/intern/test/testfile2'); $this->assertEqual( $change->getCurrentPath(), 'scripts/intern/test/testfile2'); $change = array_shift($changes); $this->assertEqual(count($change->getHunks()), 1); $this->assertEqual( $change->getOldPath(), 'scripts/intern/test/testfile3'); $this->assertEqual( $change->getCurrentPath(), 'scripts/intern/test/testfile3'); break; case 'git-move.gitdiff': case 'git-move-edit.gitdiff': case 'git-move-plus.gitdiff': $extra_changeset = (bool)strpos($file, '-plus'); $has_hunk = (bool)strpos($file, '-edit'); $this->assertEqual($extra_changeset ? 3 : 2, count($changes)); $change = array_shift($changes); $this->assertEqual($has_hunk ? 1 : 0, count($change->getHunks())); $this->assertEqual( $change->getType(), ArcanistDiffChangeType::TYPE_MOVE_HERE); $target = $change; $change = array_shift($changes); $this->assertEqual(0, count($change->getHunks())); $this->assertEqual( ArcanistDiffChangeType::TYPE_MOVE_AWAY, $change->getType()); $this->assertEqual( $change->getCurrentPath(), $target->getOldPath()); $this->assertTrue( in_array($target->getCurrentPath(), $change->getAwayPaths())); break; case 'git-merge-header.gitdiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual( ArcanistDiffChangeType::TYPE_MESSAGE, $change->getType()); $this->assertEqual( '501f6d519703458471dbea6284ec5f49d1408598', $change->getCommitHash()); break; case 'git-new-file.gitdiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual( ArcanistDiffChangeType::TYPE_ADD, $change->getType()); break; case 'git-copy.gitdiff': $this->assertEqual(2, count($changes)); $change = array_shift($changes); $this->assertEqual(0, count($change->getHunks())); $this->assertEqual( ArcanistDiffChangeType::TYPE_COPY_HERE, $change->getType()); $this->assertEqual( 'flib/intern/widgets/ui/UIWidgetRSSBox.php', $change->getCurrentPath()); $change = array_shift($changes); $this->assertEqual(0, count($change->getHunks())); $this->assertEqual( ArcanistDiffChangeType::TYPE_COPY_AWAY, $change->getType()); $this->assertEqual( 'lib/display/intern/ui/widget/UIWidgetRSSBox.php', $change->getCurrentPath()); break; case 'git-copy-plus.gitdiff': $this->assertEqual(2, count($changes)); $change = array_shift($changes); $this->assertEqual(3, count($change->getHunks())); $this->assertEqual( ArcanistDiffChangeType::TYPE_COPY_HERE, $change->getType()); $this->assertEqual( 'flib/intern/widgets/ui/UIWidgetGraphConnect.php', $change->getCurrentPath()); $change = array_shift($changes); $this->assertEqual(0, count($change->getHunks())); $this->assertEqual( ArcanistDiffChangeType::TYPE_COPY_AWAY, $change->getType()); $this->assertEqual( 'lib/display/intern/ui/widget/UIWidgetLunchtime.php', $change->getCurrentPath()); break; case 'svn-property-multiline.svndiff': $this->assertEqual(1, count($changes)); $change = array_shift($changes); $this->assertEqual(0, count($change->getHunks())); $this->assertEqual( array( 'svn:ignore' => 'tags', ), $change->getOldProperties()); $this->assertEqual( array( 'svn:ignore' => "tags\nasdf\nlol\nwhat", ), $change->getNewProperties()); break; case 'git-empty-files.gitdiff': $this->assertEqual(2, count($changes)); while ($change = array_shift($changes)) { $this->assertEqual(0, count($change->getHunks())); } break; case 'git-mnemonicprefix.gitdiff': // Check parsing of diffs created with `diff.mnemonicprefix` // configuration option set to `true`. $this->assertEqual(1, count($changes)); $this->assertEqual(1, count(reset($changes)->getHunks())); break; case 'git-commit.gitdiff': case 'git-commit-logdecorate.gitdiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual( ArcanistDiffChangeType::TYPE_MESSAGE, $change->getType()); $this->assertEqual( '76e2f1339c298c748aa0b52030799ed202a6537b', $change->getCommitHash()); $this->assertEqual( <<. I tested most of these calls, but there were some that I didn't know how to reach, so if you are one of the owners of this code, please test your feature in my sandbox: www.ngao.devrs013.facebook.com @brosenthal, I removed some logic that was setting a disabled state on a UIActionButton, which is actually a no-op. Reviewed By: brosenthal Other Commenters: sparker, egiovanola Test Plan: www.ngao.devrs013.facebook.com Explicitly tested: * ads creation flow (add keyword) * ads manager (conversion tracking) * help center (create a discussion) * new user wizard (next step button) Revert: OK DiffCamp Revision: 94064 git-svn-id: svn+ssh://tubbs/svnroot/tfb/trunk/www@223593 2c7ba8d8 EOTEXT , $change->getMetadata('message')); break; case 'git-binary.gitdiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual( ArcanistDiffChangeType::TYPE_CHANGE, $change->getType()); $this->assertEqual( ArcanistDiffChangeType::FILE_BINARY, $change->getFileType()); break; case 'git-odd-filename.gitdiff': $this->assertEqual(2, count($changes)); $change = reset($changes); $this->assertEqual( 'old/'."\342\210\206".'.jpg', $change->getOldPath()); $this->assertEqual( 'new/'."\342\210\206".'.jpg', $change->getCurrentPath()); break; case 'hg-binary-change.hgdiff': case 'hg-solo-binary-change.hgdiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual( ArcanistDiffChangeType::TYPE_ADD, $change->getType()); $this->assertEqual( ArcanistDiffChangeType::FILE_BINARY, $change->getFileType()); break; case 'hg-binary-delete.hgdiff': $this->assertEqual(1, count($changes)); $change = reset($changes); $this->assertEqual( ArcanistDiffChangeType::TYPE_DELETE, $change->getType()); $this->assertEqual( ArcanistDiffChangeType::FILE_BINARY, $change->getFileType()); break; case 'git-replace-symlink.gitdiff': $this->assertEqual(1, count($changes)); $change = array_shift($changes); $this->assertEqual( ArcanistDiffChangeType::TYPE_CHANGE, $change->getType()); break; case 'svn-1.7-property-added.svndiff': $this->assertEqual(1, count($changes)); $change = head($changes); $new_properties = $change->getNewProperties(); $this->assertEqual(2, count($new_properties)); $this->assertEqual('*', idx($new_properties, 'svn:executable')); $this->assertEqual('text/html', idx($new_properties, 'svn:mime-type')); break; case 'hg-diff-range.hgdiff': $this->assertEqual(1, count($changes)); $change = array_shift($changes); $this->assertEqual( 'Test.java', $change->getOldPath()); $this->assertEqual( 'Test.java', $change->getCurrentPath()); break; case 'hg-patch.hgdiff': $this->assertEqual(1, count($changes)); break; case 'hg-patch-git.hgdiff': $this->assertEqual(1, count($changes)); break; case 'custom-prefixes.gitdiff': $this->assertEqual(1, count($changes)); $change = head($changes); $this->assertEqual( 'dst/file', $change->getCurrentPath()); break; case 'more-newlines.svndiff': $this->assertEqual(1, count($changes)); break; case 'suppress-blank-empty.gitdiff': $this->assertEqual(1, count($changes)); break; case 'svn-property-windows.svndiff': $this->assertEqual(1, count($changes)); break; case 'rcs-addline.rcsdiff': $this->assertEqual(1, count($changes)); $change = array_shift($changes); $this->assertEqual( ArcanistDiffChangeType::TYPE_CHANGE, $change->getType()); break; case 'rcs-deleteline.rcsdiff': $this->assertEqual(1, count($changes)); $change = array_shift($changes); $this->assertEqual( ArcanistDiffChangeType::TYPE_CHANGE, $change->getType()); break; case 'comment.svndiff': $this->assertEqual(1, count($changes)); $change = array_shift($changes); $this->assertEqual( ArcanistDiffChangeType::TYPE_CHANGE, $change->getType()); break; case 'svnlook-basics.svndiff': case 'svnlook-add.svndiff': case 'svnlook-delete.svndiff': case 'svnlook-copied.svndiff': $this->assertEqual(1, count($changes)); break; case 'git-format-patch.gitdiff': $this->assertEqual(2, count($changes)); $change = array_shift($changes); $this->assertEqual( ArcanistDiffChangeType::TYPE_MESSAGE, $change->getType()); $this->assertEqual('WIP', $change->getMetadata('message')); $change = array_shift($changes); $this->assertEqual( ArcanistDiffChangeType::TYPE_CHANGE, $change->getType()); break; case 'svn-double-diff.svndiff': $this->assertEqual(1, count($changes)); $change = array_shift($changes); $hunks = $change->getHunks(); $this->assertEqual(1, count($hunks)); break; case 'git-remove-spaces.gitdiff': $this->assertEqual(1, count($changes)); $change = array_shift($changes); $this->assertEqual('file with spaces.txt', $change->getOldPath()); break; default: throw new Exception(pht('No test block for diff file %s.', $diff_file)); break; } } public function testGitPrefixStripping() { static $tests = array( 'a/file.c' => 'file.c', 'b/file.c' => 'file.c', 'i/file.c' => 'file.c', 'c/file.c' => 'file.c', 'w/file.c' => 'file.c', 'o/file.c' => 'file.c', '1/file.c' => 'file.c', '2/file.c' => 'file.c', 'src/file.c' => 'src/file.c', 'file.c' => 'file.c', ); foreach ($tests as $input => $expect) { $this->assertEqual( $expect, ArcanistDiffParser::stripGitPathPrefix($input), pht("Strip git prefix from '%s'.", $input)); } } public function testGitPathSplitting() { static $tests = array( 'a/old.c b/new.c' => array('old.c', 'new.c'), "a/old.c b/new.c\n" => array('old.c', 'new.c'), "a/old.c b/new.c\r\n" => array('old.c', 'new.c'), 'old.c new.c' => array('old.c', 'new.c'), '1/old.c 2/new.c' => array('old.c', 'new.c'), '"a/\\"quotes1\\"" "b/\\"quotes2\\""' => array( '"quotes1"', '"quotes2"', ), '"a/\\"quotes and spaces1\\"" "b/\\"quotes and spaces2\\""' => array( '"quotes and spaces1"', '"quotes and spaces2"', ), '"a/\\342\\230\\2031" "b/\\342\\230\\2032"' => array( "\xE2\x98\x831", "\xE2\x98\x832", ), 'a/Core Data/old.c b/Core Data/new.c' => array( 'Core Data/old.c', 'Core Data/new.c', ), 'some file with spaces.c some file with spaces.c' => array( 'some file with spaces.c', 'some file with spaces.c', ), ); foreach ($tests as $input => $expect) { $result = ArcanistDiffParser::splitGitDiffPaths($input); $this->assertEqual( $expect, $result, pht('Split: %s', $input)); } static $ambiguous = array( 'old file with spaces.c new file with spaces.c', ); foreach ($ambiguous as $input) { $caught = null; try { ArcanistDiffParser::splitGitDiffPaths($input); } catch (Exception $ex) { $caught = $ex; } $this->assertTrue( ($caught instanceof Exception), pht('Ambiguous: %s', $input)); } } } diff --git a/src/repository/api/__tests__/ArcanistRepositoryAPIMiscTestCase.php b/src/repository/api/__tests__/ArcanistRepositoryAPIMiscTestCase.php index 957a42b2..666d32ff 100644 --- a/src/repository/api/__tests__/ArcanistRepositoryAPIMiscTestCase.php +++ b/src/repository/api/__tests__/ArcanistRepositoryAPIMiscTestCase.php @@ -1,23 +1,23 @@ assertEqual( $expect, ArcanistSubversionAPI::escapeFileNamesForSVN($input)); } } diff --git a/src/repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php b/src/repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php index 236f46c7..689588d7 100644 --- a/src/repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php +++ b/src/repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php @@ -1,123 +1,123 @@ parseState('git_basic.git.tgz'); } else { $this->assertSkipped(pht('Git is not installed')); } } public function testHgStateParsing() { if (Filesystem::binaryExists('hg')) { $this->parseState('hg_basic.hg.tgz'); } else { $this->assertSkipped(pht('Mercurial is not installed')); } } public function testSvnStateParsing() { if (Filesystem::binaryExists('svn')) { $this->parseState('svn_basic.svn.tgz'); } else { $this->assertSkipped(pht('Subversion is not installed')); } } private function parseState($test) { $dir = dirname(__FILE__).'/state/'; $fixture = PhutilDirectoryFixture::newFromArchive($dir.'/'.$test); $fixture_path = $fixture->getPath(); $working_copy = ArcanistWorkingCopyIdentity::newFromPath($fixture_path); $configuration_manager = new ArcanistConfigurationManager(); $configuration_manager->setWorkingCopyIdentity($working_copy); $api = ArcanistRepositoryAPI::newAPIFromConfigurationManager( $configuration_manager); $api->setBaseCommitArgumentRules('arc:this'); if ($api instanceof ArcanistSubversionAPI) { // Upgrade the repository so that the test will still pass if the local // `svn` is newer than the `svn` which created the repository. // NOTE: Some versions of Subversion (1.7.x?) exit with an error code on // a no-op upgrade, although newer versions do not. We just ignore the // error here; if it's because of an actual problem we'll hit an error // shortly anyway. $api->execManualLocal('upgrade'); } $this->assertCorrectState($test, $api); } private function assertCorrectState($test, ArcanistRepositoryAPI $api) { $f_mod = ArcanistRepositoryAPI::FLAG_MODIFIED; $f_add = ArcanistRepositoryAPI::FLAG_ADDED; $f_del = ArcanistRepositoryAPI::FLAG_DELETED; $f_unt = ArcanistRepositoryAPI::FLAG_UNTRACKED; $f_con = ArcanistRepositoryAPI::FLAG_CONFLICT; $f_mis = ArcanistRepositoryAPI::FLAG_MISSING; $f_uns = ArcanistRepositoryAPI::FLAG_UNSTAGED; $f_unc = ArcanistRepositoryAPI::FLAG_UNCOMMITTED; $f_ext = ArcanistRepositoryAPI::FLAG_EXTERNALS; $f_obs = ArcanistRepositoryAPI::FLAG_OBSTRUCTED; $f_inc = ArcanistRepositoryAPI::FLAG_INCOMPLETE; switch ($test) { case 'svn_basic.svn.tgz': $expect = array( 'ADDED' => $f_add, 'COPIED_TO' => $f_add, 'DELETED' => $f_del, 'MODIFIED' => $f_mod, 'MOVED' => $f_del, 'MOVED_TO' => $f_add, 'PROPCHANGE' => $f_mod, 'UNTRACKED' => $f_unt, ); $this->assertEqual($expect, $api->getUncommittedStatus()); $this->assertEqual($expect, $api->getCommitRangeStatus()); break; case 'git_basic.git.tgz': $expect_uncommitted = array( 'UNCOMMITTED' => $f_add | $f_unc, 'UNSTAGED' => $f_mod | $f_uns | $f_unc, 'UNTRACKED' => $f_unt, ); $this->assertEqual($expect_uncommitted, $api->getUncommittedStatus()); $expect_range = array( 'ADDED' => $f_add, 'DELETED' => $f_del, 'MODIFIED' => $f_mod, 'UNCOMMITTED' => $f_add, 'UNSTAGED' => $f_add, ); $this->assertEqual($expect_range, $api->getCommitRangeStatus()); break; case 'hg_basic.hg.tgz': $expect_uncommitted = array( 'UNCOMMITTED' => $f_mod | $f_unc, 'UNTRACKED' => $f_unt, ); $this->assertEqual($expect_uncommitted, $api->getUncommittedStatus()); $expect_range = array( 'ADDED' => $f_add, 'DELETED' => $f_del, 'MODIFIED' => $f_mod, 'UNCOMMITTED' => $f_add, ); $this->assertEqual($expect_range, $api->getCommitRangeStatus()); break; default: throw new Exception( pht("No test cases for working copy '%s'!", $test)); } } } diff --git a/src/repository/parser/__tests__/ArcanistMercurialParserTestCase.php b/src/repository/parser/__tests__/ArcanistMercurialParserTestCase.php index 6ff5f426..49f92710 100644 --- a/src/repository/parser/__tests__/ArcanistMercurialParserTestCase.php +++ b/src/repository/parser/__tests__/ArcanistMercurialParserTestCase.php @@ -1,90 +1,90 @@ parseData( basename($file), Filesystem::readFile($root.'/'.$file)); } } private function parseData($name, $data) { switch ($name) { case 'branches-basic.txt': $output = ArcanistMercurialParser::parseMercurialBranches($data); $this->assertEqual( array('default', 'stable'), array_keys($output)); $this->assertEqual( array('a21ccf4412d5', 'ec222a29bdf0'), array_values(ipull($output, 'rev'))); break; case 'branches-with-spaces.txt': $output = ArcanistMercurialParser::parseMercurialBranches($data); $this->assertEqual( array( 'm m m m m 2:ffffffffffff (inactive)', 'xxx yyy zzz', 'default', "'", ), array_keys($output)); $this->assertEqual( array('0b9d8290c4e0', '78963faacfc7', '5db03c5500c6', 'ffffffffffff'), array_values(ipull($output, 'rev'))); break; case 'branches-empty.txt': $output = ArcanistMercurialParser::parseMercurialBranches($data); $this->assertEqual(array(), $output); break; case 'log-basic.txt': $output = ArcanistMercurialParser::parseMercurialLog($data); $this->assertEqual( 3, count($output)); $this->assertEqual( array('a21ccf4412d5', 'a051f8a6a7cc', 'b1f49efeab65'), array_values(ipull($output, 'rev'))); break; case 'log-empty.txt': // Empty logs (e.g., "hg parents" for a root revision) should parse // correctly. $output = ArcanistMercurialParser::parseMercurialLog($data); $this->assertEqual( array(), $output); break; case 'status-basic.txt': $output = ArcanistMercurialParser::parseMercurialStatus($data); $this->assertEqual( 4, count($output)); $this->assertEqual( array('changed', 'added', 'removed', 'untracked'), array_keys($output)); break; case 'status-moves.txt': $output = ArcanistMercurialParser::parseMercurialStatusDetails($data); $this->assertEqual( 'move_source', $output['moved_file']['from']); $this->assertEqual( null, $output['changed_file']['from']); $this->assertEqual( 'copy_source', $output['copied_file']['from']); $this->assertEqual( null, idx($output, 'copy_source')); break; default: throw new Exception( pht("No test information for test data '%s'!", $name)); } } } diff --git a/src/unit/engine/PhutilUnitTestEngine.php b/src/unit/engine/PhutilUnitTestEngine.php index 375890ba..3879396c 100644 --- a/src/unit/engine/PhutilUnitTestEngine.php +++ b/src/unit/engine/PhutilUnitTestEngine.php @@ -1,201 +1,201 @@ getRunAllTests()) { $run_tests = $this->getAllTests(); } else { $run_tests = $this->getTestsForPaths(); } if (!$run_tests) { throw new ArcanistNoEffectException('No tests to run.'); } $enable_coverage = $this->getEnableCoverage(); if ($enable_coverage !== false) { if (!function_exists('xdebug_start_code_coverage')) { if ($enable_coverage === true) { throw new ArcanistUsageException( pht( 'You specified %s but xdebug is not available, so '. 'coverage can not be enabled for %s.', '--coverage', __CLASS__)); } } else { $enable_coverage = true; } } $project_root = $this->getWorkingCopy()->getProjectRoot(); $test_cases = array(); foreach ($run_tests as $test_class) { $test_case = newv($test_class, array()); $test_case->setEnableCoverage($enable_coverage); $test_case->setWorkingCopy($this->getWorkingCopy()); if ($this->getPaths()) { $test_case->setPaths($this->getPaths()); } if ($this->renderer) { $test_case->setRenderer($this->renderer); } $test_cases[] = $test_case; } foreach ($test_cases as $test_case) { $test_case->willRunTestCases($test_cases); } $results = array(); foreach ($test_cases as $test_case) { $results[] = $test_case->run(); } $results = array_mergev($results); foreach ($test_cases as $test_case) { $test_case->didRunTestCases($test_cases); } return $results; } private function getAllTests() { $project_root = $this->getWorkingCopy()->getProjectRoot(); $symbols = id(new PhutilSymbolLoader()) ->setType('class') - ->setAncestorClass('ArcanistPhutilTestCase') + ->setAncestorClass('PhutilTestCase') ->setConcreteOnly(true) ->selectSymbolsWithoutLoading(); $in_working_copy = array(); $run_tests = array(); foreach ($symbols as $symbol) { if (!preg_match('@(?:^|/)__tests__/@', $symbol['where'])) { continue; } $library = $symbol['library']; if (!isset($in_working_copy[$library])) { $library_root = phutil_get_library_root($library); $in_working_copy[$library] = Filesystem::isDescendant( $library_root, $project_root); } if ($in_working_copy[$library]) { $run_tests[] = $symbol['name']; } } return $run_tests; } private function getTestsForPaths() { $project_root = $this->getWorkingCopy()->getProjectRoot(); $look_here = array(); foreach ($this->getPaths() as $path) { $library_root = phutil_get_library_root_for_path($path); if (!$library_root) { continue; } $library_name = phutil_get_library_name_for_root($library_root); if (!$library_name) { throw new Exception( sprintf( "%s\n\n %s\n\n%s\n\n - %s\n - %s\n", pht( 'Attempting to run unit tests on a libphutil library '. 'which has not been loaded, at:'), $library_root, pht('This probably means one of two things:'), pht( 'You may need to add this library to %s.', '.arcconfig.'), pht( 'You may be running tests on a copy of libphutil or '. 'arcanist using a different copy of libphutil or arcanist. '. 'This operation is not supported.'))); } $path = Filesystem::resolvePath($path, $project_root); if (!is_dir($path)) { $path = dirname($path); } if ($path == $library_root) { $look_here[$library_name.':.'] = array( 'library' => $library_name, 'path' => '', ); } else if (!Filesystem::isDescendant($path, $library_root)) { // We have encountered some kind of symlink maze -- for instance, $path // is some symlink living outside the library that links into some file // inside the library. Just ignore these cases, since the affected file // does not actually lie within the library. continue; } else { $library_path = Filesystem::readablePath($path, $library_root); do { $look_here[$library_name.':'.$library_path] = array( 'library' => $library_name, 'path' => $library_path, ); $library_path = dirname($library_path); } while ($library_path != '.'); } } - // Look for any class that extends ArcanistPhutilTestCase inside a - // __tests__ directory in any parent directory of every affected file. + // Look for any class that extends PhutilTestCase inside a `__tests__` + // directory in any parent directory of every affected file. // // The idea is that "infrastructure/__tests__/" tests defines general tests // for all of "infrastructure/", and those tests run for any change in // "infrastructure/". However, "infrastructure/concrete/rebar/__tests__/" // defines more specific tests that run only when rebar/ (or some // subdirectory) changes. $run_tests = array(); foreach ($look_here as $path_info) { $library = $path_info['library']; $path = $path_info['path']; $symbols = id(new PhutilSymbolLoader()) ->setType('class') ->setLibrary($library) ->setPathPrefix(($path ? $path.'/' : '').'__tests__/') - ->setAncestorClass('ArcanistPhutilTestCase') + ->setAncestorClass('PhutilTestCase') ->setConcreteOnly(true) ->selectAndLoadSymbols(); foreach ($symbols as $symbol) { $run_tests[$symbol['name']] = true; } } $run_tests = array_keys($run_tests); return $run_tests; } public function shouldEchoTestResults() { return !$this->renderer; } } diff --git a/src/unit/engine/__tests__/PhpunitTestEngineTestCase.php b/src/unit/engine/__tests__/PhpunitTestEngineTestCase.php index dc8f8de5..5f7f50eb 100644 --- a/src/unit/engine/__tests__/PhpunitTestEngineTestCase.php +++ b/src/unit/engine/__tests__/PhpunitTestEngineTestCase.php @@ -1,42 +1,42 @@ assertEqual( array( '/path/to/some/file/', '/path/to/some/file/tests/', '/path/to/some/file/Tests/', '/path/to/some/tests/', '/path/to/some/Tests/', '/path/to/tests/', '/path/to/Tests/', '/path/tests/', '/path/Tests/', '/tests/', '/Tests/', '/path/to/tests/file/', '/path/to/Tests/file/', '/path/tests/some/file/', '/path/Tests/some/file/', '/tests/to/some/file/', '/Tests/to/some/file/', '/path/to/some/tests/file/', '/path/to/some/Tests/file/', '/path/to/tests/some/file/', '/path/to/Tests/some/file/', '/path/tests/to/some/file/', '/path/Tests/to/some/file/', '/tests/path/to/some/file/', '/Tests/path/to/some/file/', ), PhpunitTestEngine::getSearchLocationsForTests($path)); } } diff --git a/src/unit/engine/__tests__/PhutilUnitTestEngineTestCase.php b/src/unit/engine/__tests__/PhutilUnitTestEngineTestCase.php index 62b3af07..0df0512d 100644 --- a/src/unit/engine/__tests__/PhutilUnitTestEngineTestCase.php +++ b/src/unit/engine/__tests__/PhutilUnitTestEngineTestCase.php @@ -1,123 +1,123 @@ assertEqual( 1, self::$allTestsCounter, pht( 'Expect %s has been called once.', 'willRunTests()')); self::$allTestsCounter--; $actual_test_count = 4; $this->assertEqual( $actual_test_count, count(self::$distinctWillRunTests), pht( 'Expect %s was called once for each test.', 'willRunOneTest()')); $this->assertEqual( $actual_test_count, count(self::$distinctDidRunTests), pht( 'Expect %s was called once for each test.', 'didRunOneTest()')); $this->assertEqual( self::$distinctWillRunTests, self::$distinctDidRunTests, pht('Expect same tests had pre-run and post-run callbacks invoked.')); } public function __destruct() { if (self::$allTestsCounter !== 0) { throw new Exception( pht( '%s was not called correctly after tests completed!', 'didRunTests()')); } } protected function willRunOneTest($test) { self::$distinctWillRunTests[$test] = true; self::$oneTestCounter++; } protected function didRunOneTest($test) { $this->assertEqual( 1, self::$oneTestCounter, pht('Expect %s depth to be one.', 'willRunOneTest()')); self::$distinctDidRunTests[$test] = true; self::$oneTestCounter--; } public function testPass() { $this->assertEqual(1, 1, pht('This test is expected to pass.')); } public function testFailSkip() { $failed = 0; $skipped = 0; - $test_case = id(new ArcanistPhutilTestCaseTestCase()) + $test_case = id(new PhutilTestCaseTestCase()) ->setWorkingCopy($this->getWorkingCopy()); foreach ($test_case->run() as $result) { if ($result->getResult() == ArcanistUnitTestResult::RESULT_FAIL) { $failed++; } else if ($result->getResult() == ArcanistUnitTestResult::RESULT_SKIP) { $skipped++; } else { $this->assertFailure(pht('These tests should either fail or skip.')); } } $this->assertEqual(1, $failed, pht('One test was expected to fail.')); $this->assertEqual(1, $skipped, pht('One test was expected to skip.')); } public function testTryTestCases() { $this->tryTestCases( array( true, false, ), array( true, false, ), array($this, 'throwIfFalsey')); } public function testTryTestMap() { $this->tryTestCaseMap( array( 1 => true, 0 => false, ), array($this, 'throwIfFalsey')); } protected function throwIfFalsey($input) { if (!$input) { throw new Exception(pht('This is a negative test case!')); } } } diff --git a/src/unit/engine/phutil/ArcanistPhutilTestCase.php b/src/unit/engine/phutil/PhutilTestCase.php similarity index 97% rename from src/unit/engine/phutil/ArcanistPhutilTestCase.php rename to src/unit/engine/phutil/PhutilTestCase.php index 54aa7306..a3f98510 100644 --- a/src/unit/engine/phutil/ArcanistPhutilTestCase.php +++ b/src/unit/engine/phutil/PhutilTestCase.php @@ -1,742 +1,742 @@ assertions++; return; } $this->failAssertionWithExpectedValue('false', $result, $message); } /** * Assert that a value is `true`, strictly. The test fails if it is not. * * @param wild The empirically derived value, generated by executing the * test. * @param string A human-readable description of what these values represent, * and particularly of what a discrepancy means. * * @return void * @task assert */ final protected function assertTrue($result, $message = null) { if ($result === true) { $this->assertions++; return; } $this->failAssertionWithExpectedValue('true', $result, $message); } /** * Assert that two values are equal, strictly. The test fails if they are not. * * NOTE: This method uses PHP's strict equality test operator (`===`) to * compare values. This means values and types must be equal, key order must * be identical in arrays, and objects must be referentially identical. * * @param wild The theoretically expected value, generated by careful * reasoning about the properties of the system. * @param wild The empirically derived value, generated by executing the * test. * @param string A human-readable description of what these values represent, * and particularly of what a discrepancy means. * * @return void * @task assert */ final protected function assertEqual($expect, $result, $message = null) { if ($expect === $result) { $this->assertions++; return; } $expect = PhutilReadableSerializer::printableValue($expect); $result = PhutilReadableSerializer::printableValue($result); $caller = self::getCallerInfo(); $file = $caller['file']; $line = $caller['line']; if ($message !== null) { $output = pht( 'Assertion failed, expected values to be equal (at %s:%d): %s', $file, $line, $message); } else { $output = pht( 'Assertion failed, expected values to be equal (at %s:%d).', $file, $line); } $output .= "\n"; if (strpos($expect, "\n") === false && strpos($result, "\n") === false) { $output .= pht("Expected: %s\n Actual: %s", $expect, $result); } else { $output .= pht( "Expected vs Actual Output Diff\n%s", ArcanistDiffUtils::renderDifferences( $expect, $result, $lines = 0xFFFF)); } $this->failTest($output); - throw new ArcanistPhutilTestTerminatedException($output); + throw new PhutilTestTerminatedException($output); } /** * Assert an unconditional failure. This is just a convenience method that * better indicates intent than using dummy values with assertEqual(). This * causes test failure. * * @param string Human-readable description of the reason for test failure. * @return void * @task assert */ final protected function assertFailure($message) { $this->failTest($message); - throw new ArcanistPhutilTestTerminatedException($message); + throw new PhutilTestTerminatedException($message); } /** * End this test by asserting that the test should be skipped for some * reason. * * @param string Reason for skipping this test. * @return void * @task assert */ final protected function assertSkipped($message) { $this->skipTest($message); - throw new ArcanistPhutilTestSkippedException($message); + throw new PhutilTestSkippedException($message); } /* -( Exception Handling )------------------------------------------------- */ /** * This simplest way to assert exceptions are thrown. * * @param exception The expected exception. * @param callable The thing which throws the exception. * * @return void * @task exceptions */ final protected function assertException($expected_exception_class, $callable) { $this->tryTestCases( array('assertException' => array()), array(false), $callable, $expected_exception_class); } /** * Straightforward method for writing unit tests which check if some block of * code throws an exception. For example, this allows you to test the * exception behavior of ##is_a_fruit()## on various inputs: * * public function testFruit() { * $this->tryTestCases( * array( * 'apple is a fruit' => new Apple(), * 'rock is not a fruit' => new Rock(), * ), * array( * true, * false, * ), * array($this, 'tryIsAFruit'), * 'NotAFruitException'); * } * * protected function tryIsAFruit($input) { * is_a_fruit($input); * } * * @param map Map of test case labels to test case inputs. * @param list List of expected results, true to indicate that the case * is expected to succeed and false to indicate that the case * is expected to throw. * @param callable Callback to invoke for each test case. * @param string Optional exception class to catch, defaults to * 'Exception'. * @return void * @task exceptions */ final protected function tryTestCases( array $inputs, array $expect, $callable, $exception_class = 'Exception') { if (count($inputs) !== count($expect)) { $this->assertFailure( pht('Input and expectations must have the same number of values.')); } $labels = array_keys($inputs); $inputs = array_values($inputs); $expecting = array_values($expect); foreach ($inputs as $idx => $input) { $expect = $expecting[$idx]; $label = $labels[$idx]; $caught = null; try { call_user_func($callable, $input); } catch (Exception $ex) { - if ($ex instanceof ArcanistPhutilTestTerminatedException) { + if ($ex instanceof PhutilTestTerminatedException) { throw $ex; } if (!($ex instanceof $exception_class)) { throw $ex; } $caught = $ex; } $actual = !($caught instanceof Exception); if ($expect === $actual) { if ($expect) { $message = pht("Test case '%s' did not throw, as expected.", $label); } else { $message = pht("Test case '%s' threw, as expected.", $label); } } else { if ($expect) { $message = pht( "Test case '%s' was expected to succeed, but it ". "raised an exception of class %s with message: %s", $label, get_class($ex), $ex->getMessage()); } else { $message = pht( "Test case '%s' was expected to raise an ". "exception, but it did not throw anything.", $label); } } $this->assertEqual($expect, $actual, $message); } } /** * Convenience wrapper around @{method:tryTestCases} for cases where your * inputs are scalar. For example: * * public function testFruit() { * $this->tryTestCaseMap( * array( * 'apple' => true, * 'rock' => false, * ), * array($this, 'tryIsAFruit'), * 'NotAFruitException'); * } * * protected function tryIsAFruit($input) { * is_a_fruit($input); * } * * For cases where your inputs are not scalar, use @{method:tryTestCases}. * * @param map Map of scalar test inputs to expected success (true * expects success, false expects an exception). * @param callable Callback to invoke for each test case. * @param string Optional exception class to catch, defaults to * 'Exception'. * @return void * @task exceptions */ final protected function tryTestCaseMap( array $map, $callable, $exception_class = 'Exception') { return $this->tryTestCases( array_fuse(array_keys($map)), array_values($map), $callable, $exception_class); } /* -( Hooks for Setup and Teardown )--------------------------------------- */ /** * This hook is invoked once, before any tests in this class are run. It * gives you an opportunity to perform setup steps for the entire class. * * @return void * @task hook */ protected function willRunTests() { return; } /** * This hook is invoked once, after any tests in this class are run. It gives * you an opportunity to perform teardown steps for the entire class. * * @return void * @task hook */ protected function didRunTests() { return; } /** * This hook is invoked once per test, before the test method is invoked. * * @param string Method name of the test which will be invoked. * @return void * @task hook */ protected function willRunOneTest($test_method_name) { return; } /** * This hook is invoked once per test, after the test method is invoked. * * @param string Method name of the test which was invoked. * @return void * @task hook */ protected function didRunOneTest($test_method_name) { return; } /** * This hook is invoked once, before any test cases execute. It gives you * an opportunity to perform setup steps for the entire suite of test cases. * - * @param list List of test cases to be run. + * @param list List of test cases to be run. * @return void * @task hook */ public function willRunTestCases(array $test_cases) { return; } /** * This hook is invoked once, after all test cases execute. * - * @param list List of test cases that ran. + * @param list List of test cases that ran. * @return void * @task hook */ public function didRunTestCases(array $test_cases) { return; } /* -( Internals )---------------------------------------------------------- */ /** * Construct a new test case. This method is ##final##, use willRunTests() to * provide test-wide setup logic. * * @task internal */ final public function __construct() {} /** * Mark the currently-running test as a failure. * * @param string Human-readable description of problems. * @return void * * @task internal */ final private function failTest($reason) { $this->resultTest(ArcanistUnitTestResult::RESULT_FAIL, $reason); } /** * This was a triumph. I'm making a note here: HUGE SUCCESS. * * @param string Human-readable overstatement of satisfaction. * @return void * * @task internal */ final private function passTest($reason) { $this->resultTest(ArcanistUnitTestResult::RESULT_PASS, $reason); } /** * Mark the current running test as skipped. * * @param string Description for why this test was skipped. * @return void * @task internal */ final private function skipTest($reason) { $this->resultTest(ArcanistUnitTestResult::RESULT_SKIP, $reason); } final private function resultTest($test_result, $reason) { $coverage = $this->endCoverage(); $result = new ArcanistUnitTestResult(); $result->setCoverage($coverage); $result->setNamespace(get_class($this)); $result->setName($this->runningTest); $result->setLink($this->getLink($this->runningTest)); $result->setResult($test_result); $result->setDuration(microtime(true) - $this->testStartTime); $result->setUserData($reason); $this->results[] = $result; if ($this->renderer) { echo $this->renderer->renderUnitResult($result); } } /** * Execute the tests in this test case. You should not call this directly; * use @{class:PhutilUnitTestEngine} to orchestrate test execution. * * @return void * @task internal */ final public function run() { $this->results = array(); $reflection = new ReflectionClass($this); $methods = $reflection->getMethods(); // Try to ensure that poorly-written tests which depend on execution order // (and are thus not properly isolated) will fail. shuffle($methods); $this->willRunTests(); foreach ($methods as $method) { $name = $method->getName(); if (preg_match('/^test/', $name)) { $this->runningTest = $name; $this->assertions = 0; $this->testStartTime = microtime(true); try { $this->willRunOneTest($name); $this->beginCoverage(); $exceptions = array(); try { call_user_func_array( array($this, $name), array()); $this->passTest(pht('%d assertion(s) passed.', $this->assertions)); } catch (Exception $ex) { $exceptions['Execution'] = $ex; } try { $this->didRunOneTest($name); } catch (Exception $ex) { $exceptions['Shutdown'] = $ex; } if ($exceptions) { if (count($exceptions) == 1) { throw head($exceptions); } else { throw new PhutilAggregateException( pht('Multiple exceptions were raised during test execution.'), $exceptions); } } if (!$this->assertions) { $this->failTest( pht( 'This test case made no assertions. Test cases must make at '. 'least one assertion.')); } - } catch (ArcanistPhutilTestTerminatedException $ex) { + } catch (PhutilTestTerminatedException $ex) { // Continue with the next test. - } catch (ArcanistPhutilTestSkippedException $ex) { + } catch (PhutilTestSkippedException $ex) { // Continue with the next test. } catch (Exception $ex) { $ex_class = get_class($ex); $ex_message = $ex->getMessage(); $ex_trace = $ex->getTraceAsString(); $message = sprintf( "%s (%s): %s\n%s", pht('EXCEPTION'), $ex_class, $ex_message, $ex_trace); $this->failTest($message); } } } $this->didRunTests(); return $this->results; } final public function setEnableCoverage($enable_coverage) { $this->enableCoverage = $enable_coverage; return $this; } /** * @phutil-external-symbol function xdebug_start_code_coverage */ final private function beginCoverage() { if (!$this->enableCoverage) { return; } $this->assertCoverageAvailable(); xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE); } /** * @phutil-external-symbol function xdebug_get_code_coverage * @phutil-external-symbol function xdebug_stop_code_coverage */ final private function endCoverage() { if (!$this->enableCoverage) { return; } $result = xdebug_get_code_coverage(); xdebug_stop_code_coverage($cleanup = false); $coverage = array(); foreach ($result as $file => $report) { $project_root = $this->getProjectRoot(); if (strncmp($file, $project_root, strlen($project_root))) { continue; } $max = max(array_keys($report)); $str = ''; for ($ii = 1; $ii <= $max; $ii++) { $c = idx($report, $ii); if ($c === -1) { $str .= 'U'; // Un-covered. } else if ($c === -2) { // TODO: This indicates "unreachable", but it flags the closing braces // of functions which end in "return", which is super ridiculous. Just // ignore it for now. // // See http://bugs.xdebug.org/view.php?id=1041 $str .= 'N'; // Not executable. } else if ($c === 1) { $str .= 'C'; // Covered. } else { $str .= 'N'; // Not executable. } } $coverage[substr($file, strlen($project_root) + 1)] = $str; } // Only keep coverage information for files modified by the change. In // the case of --everything, we won't have paths, so just return all the // coverage data. if ($this->paths) { $coverage = array_select_keys($coverage, $this->paths); } return $coverage; } final private function assertCoverageAvailable() { if (!function_exists('xdebug_start_code_coverage')) { throw new Exception( pht("You've enabled code coverage but XDebug is not installed.")); } } final public function getWorkingCopy() { return $this->workingCopy; } final public function setWorkingCopy( ArcanistWorkingCopyIdentity $working_copy) { $this->workingCopy = $working_copy; return $this; } final public function getProjectRoot() { $working_copy = $this->getWorkingCopy(); if (!$working_copy) { throw new PhutilInvalidStateException('setWorkingCopy'); } return $working_copy->getProjectRoot(); } final public function setPaths(array $paths) { $this->paths = $paths; return $this; } final protected function getLink($method) { $base_uri = $this ->getWorkingCopy() ->getProjectConfig('phabricator.uri'); $uri = id(new PhutilURI($base_uri)) ->setPath("/diffusion/symbol/{$method}/") ->setQueryParam('context', get_class($this)) ->setQueryParam('jump', 'true') ->setQueryParam('lang', 'php'); return (string)$uri; } public function setRenderer(ArcanistUnitRenderer $renderer) { $this->renderer = $renderer; return $this; } /** * Returns info about the caller function. * * @return map */ private static final function getCallerInfo() { $callee = array(); $caller = array(); $seen = false; foreach (array_slice(debug_backtrace(), 1) as $location) { $function = idx($location, 'function'); if (!$seen && preg_match('/^assert[A-Z]/', $function)) { $seen = true; $caller = $location; } else if ($seen && !preg_match('/^assert[A-Z]/', $function)) { $callee = $location; break; } } return array( 'file' => basename(idx($caller, 'file')), 'line' => idx($caller, 'line'), 'function' => idx($callee, 'function'), 'class' => idx($callee, 'class'), 'object' => idx($caller, 'object'), 'type' => idx($callee, 'type'), 'args' => idx($caller, 'args'), ); } /** * Fail an assertion which checks that some result is equal to a specific * value, like 'true' or 'false'. This prints a readable error message and * fails the current test. * * This method throws and does not return. * * @param string Human readable description of the expected value. * @param string The actual value. * @param string|null Optional assertion message. * @return void * @task internal */ private function failAssertionWithExpectedValue( $expect_description, $actual_result, $message) { $caller = self::getCallerInfo(); $file = $caller['file']; $line = $caller['line']; if ($message !== null) { $description = pht( "Assertion failed, expected '%s' (at %s:%d): %s", $expect_description, $file, $line, $message); } else { $description = pht( "Assertion failed, expected '%s' (at %s:%d).", $expect_description, $file, $line); } $actual_result = PhutilReadableSerializer::printableValue($actual_result); $header = pht('ACTUAL VALUE'); $output = $description."\n\n".$header."\n".$actual_result; $this->failTest($output); - throw new ArcanistPhutilTestTerminatedException($output); + throw new PhutilTestTerminatedException($output); } } diff --git a/src/unit/engine/phutil/testcase/ArcanistPhutilTestSkippedException.php b/src/unit/engine/phutil/testcase/ArcanistPhutilTestSkippedException.php deleted file mode 100644 index eaefe826..00000000 --- a/src/unit/engine/phutil/testcase/ArcanistPhutilTestSkippedException.php +++ /dev/null @@ -1,6 +0,0 @@ -assertFailure(pht('This test is expected to fail.')); } public function testSkip() { $this->assertSkipped(pht('This test is expected to skip.')); } } diff --git a/src/unit/engine/phutil/testcase/PhutilTestSkippedException.php b/src/unit/engine/phutil/testcase/PhutilTestSkippedException.php new file mode 100644 index 00000000..23816129 --- /dev/null +++ b/src/unit/engine/phutil/testcase/PhutilTestSkippedException.php @@ -0,0 +1,6 @@ +parseTestResults('subpackage_test.go', $stubbed_results); $this->assertEqual(2, count($parsed_results)); $this->assertEqual( 'Go::Test::package::subpackage::TestFoo', $parsed_results[0]->getName()); foreach ($parsed_results as $result) { $this->assertEqual( ArcanistUnitTestResult::RESULT_PASS, $result->getResult()); } } public function testSingleTestCaseFailure() { $stubbed_results = Filesystem::readFile( dirname(__FILE__).'/testresults/go.single-test-case-failure'); $parsed_results = id(new ArcanistGoTestResultParser()) ->parseTestResults('subpackage_test.go', $stubbed_results); $this->assertEqual(2, count($parsed_results)); $this->assertEqual( ArcanistUnitTestResult::RESULT_FAIL, $parsed_results[0]->getResult()); $this->assertEqual( ArcanistUnitTestResult::RESULT_PASS, $parsed_results[1]->getResult()); } public function testMultipleTestCasesSuccessful() { $stubbed_results = Filesystem::readFile( dirname(__FILE__).'/testresults/go.multiple-test-cases-successful'); $parsed_results = id(new ArcanistGoTestResultParser()) ->parseTestResults('package', $stubbed_results); $this->assertEqual(3, count($parsed_results)); $this->assertEqual( 'Go::Test::package::subpackage1::TestFoo1', $parsed_results[0]->getName()); $this->assertEqual( 'Go::Test::package::subpackage2::TestFoo2', $parsed_results[2]->getName()); foreach ($parsed_results as $result) { $this->assertEqual( ArcanistUnitTestResult::RESULT_PASS, $result->getResult()); } } public function testMultipleTestCasesFailure() { $stubbed_results = Filesystem::readFile( dirname(__FILE__).'/testresults/go.multiple-test-cases-failure'); $parsed_results = id(new ArcanistGoTestResultParser()) ->parseTestResults('package', $stubbed_results); $this->assertEqual(3, count($parsed_results)); $this->assertEqual( 'Go::Test::package::subpackage1::TestFoo1', $parsed_results[0]->getName()); $this->assertEqual( 'Go::Test::package::subpackage2::TestFoo2', $parsed_results[2]->getName()); $this->assertEqual( ArcanistUnitTestResult::RESULT_PASS, $parsed_results[0]->getResult()); $this->assertEqual( ArcanistUnitTestResult::RESULT_FAIL, $parsed_results[2]->getResult()); } public function testNonVerboseOutput() { $stubbed_results = Filesystem::readFile( dirname(__FILE__).'/testresults/go.nonverbose'); $parsed_results = id(new ArcanistGoTestResultParser()) ->parseTestResults('package', $stubbed_results); $this->assertEqual(2, count($parsed_results)); $this->assertEqual( 'Go::TestCase::package::subpackage1', $parsed_results[0]->getName()); $this->assertEqual( 'Go::TestCase::package::subpackage2', $parsed_results[1]->getName()); foreach ($parsed_results as $result) { $this->assertEqual( ArcanistUnitTestResult::RESULT_PASS, $result->getResult()); } } public function testSingleTestCaseSuccessfulGo14() { $stubbed_results = Filesystem::readFile( dirname(__FILE__).'/testresults/go.single-test-case-successful-go1.4'); $parsed_results = id(new ArcanistGoTestResultParser()) ->parseTestResults('subpackage_test.go', $stubbed_results); $this->assertEqual(2, count($parsed_results)); $this->assertEqual( 'Go::Test::package::subpackage::TestFoo', $parsed_results[0]->getName()); foreach ($parsed_results as $result) { $this->assertEqual( ArcanistUnitTestResult::RESULT_PASS, $result->getResult()); } } public function testSingleTestCaseFailureGo14() { $stubbed_results = Filesystem::readFile( dirname(__FILE__).'/testresults/go.single-test-case-failure-go1.4'); $parsed_results = id(new ArcanistGoTestResultParser()) ->parseTestResults('subpackage_test.go', $stubbed_results); $this->assertEqual(2, count($parsed_results)); $this->assertEqual( ArcanistUnitTestResult::RESULT_FAIL, $parsed_results[0]->getResult()); $this->assertEqual( ArcanistUnitTestResult::RESULT_PASS, $parsed_results[1]->getResult()); } public function testNonVerboseOutputGo14() { $stubbed_results = Filesystem::readFile( dirname(__FILE__).'/testresults/go.nonverbose-go1.4'); $parsed_results = id(new ArcanistGoTestResultParser()) ->parseTestResults('package', $stubbed_results); $this->assertEqual(2, count($parsed_results)); $this->assertEqual( 'Go::TestCase::package::subpackage1', $parsed_results[0]->getName()); $this->assertEqual( 'Go::TestCase::package::subpackage2', $parsed_results[1]->getName()); foreach ($parsed_results as $result) { $this->assertEqual( ArcanistUnitTestResult::RESULT_PASS, $result->getResult()); } } public function testMultipleTestCasesSuccessfulGo14() { $stubbed_results = Filesystem::readFile( dirname(__FILE__).'/testresults/go.multiple-test-cases-successful-go1.4'); $parsed_results = id(new ArcanistGoTestResultParser()) ->parseTestResults('package', $stubbed_results); $this->assertEqual(3, count($parsed_results)); $this->assertEqual( 'Go::Test::package::subpackage1::TestFoo1', $parsed_results[0]->getName()); $this->assertEqual( 'Go::Test::package::subpackage2::TestFoo2', $parsed_results[2]->getName()); foreach ($parsed_results as $result) { $this->assertEqual( ArcanistUnitTestResult::RESULT_PASS, $result->getResult()); } } public function testMultipleTestCasesFailureGo14() { $stubbed_results = Filesystem::readFile( dirname(__FILE__).'/testresults/go.multiple-test-cases-failure-go1.4'); $parsed_results = id(new ArcanistGoTestResultParser()) ->parseTestResults('package', $stubbed_results); $this->assertEqual(3, count($parsed_results)); $this->assertEqual( 'Go::Test::package::subpackage1::TestFoo1', $parsed_results[0]->getName()); $this->assertEqual( 'Go::Test::package::subpackage2::TestFoo2', $parsed_results[2]->getName()); $this->assertEqual( ArcanistUnitTestResult::RESULT_PASS, $parsed_results[0]->getResult()); $this->assertEqual( ArcanistUnitTestResult::RESULT_FAIL, $parsed_results[2]->getResult()); } } diff --git a/src/unit/parser/__tests__/XUnitTestResultParserTestCase.php b/src/unit/parser/__tests__/XUnitTestResultParserTestCase.php index 84c93dca..c26356d0 100644 --- a/src/unit/parser/__tests__/XUnitTestResultParserTestCase.php +++ b/src/unit/parser/__tests__/XUnitTestResultParserTestCase.php @@ -1,54 +1,54 @@ parseTestResults($stubbed_results); $this->assertEqual(0, count($parsed_results)); } public function testAcceptsSimpleInput() { $stubbed_results = Filesystem::readFile( dirname(__FILE__).'/testresults/xunit.simple'); $parsed_results = id(new ArcanistXUnitTestResultParser()) ->parseTestResults($stubbed_results); $this->assertEqual(3, count($parsed_results)); } public function testEmptyInputFailure() { try { $parsed_results = id(new ArcanistXUnitTestResultParser()) ->parseTestResults(''); $this->failTest(pht('Should throw on empty input')); } catch (Exception $e) { // OK } $this->assertTrue(true); } public function testInvalidXmlInputFailure() { $stubbed_results = Filesystem::readFile( dirname(__FILE__).'/testresults/xunit.invalid-xml'); try { $parsed_results = id(new ArcanistXUnitTestResultParser()) ->parseTestResults($stubbed_results); $this->failTest(pht('Should throw on non-xml input')); } catch (Exception $e) { // OK } $this->assertTrue(true); } }