diff options
author | Victor Rubezhny | 2020-10-22 11:44:36 +0000 |
---|---|---|
committer | Mickael Istria | 2020-11-09 16:04:59 +0000 |
commit | 9a809a15352d02bb22dbbdcf5a088ee445b5819a (patch) | |
tree | a57109489c611488e802fa77ad98f8148e57249f | |
parent | 5bb7e72122e0bb02d75efe535220e8ee71e01372 (diff) | |
download | eclipse.platform.debug-9a809a15352d02bb22dbbdcf5a088ee445b5819a.tar.gz eclipse.platform.debug-9a809a15352d02bb22dbbdcf5a088ee445b5819a.tar.xz eclipse.platform.debug-9a809a15352d02bb22dbbdcf5a088ee445b5819a.zip |
Bug 507626: Debug framework should provide a generic "test report" viewY20201109-1200
A Unit Test View implementation based on refactored JUnit Test View
Also-By: Mickael Istria <mistria@redhat.com>
Also-By: Alexander Kurtakov <akurtako@redhat.com>
Signed-off-by: Victor Rubezhny <vrubezhny@redhat.com>
Change-Id: Id971613f3f33030343950c539d09a02192cc7ecf
214 files changed, 12381 insertions, 0 deletions
diff --git a/org.eclipse.unittest.ui/.classpath b/org.eclipse.unittest.ui/.classpath new file mode 100644 index 000000000..4a00becd8 --- /dev/null +++ b/org.eclipse.unittest.ui/.classpath @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"> + <attributes> + <attribute name="module" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="src" path="src"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/org.eclipse.unittest.ui/.project b/org.eclipse.unittest.ui/.project new file mode 100644 index 000000000..3b2e76cfb --- /dev/null +++ b/org.eclipse.unittest.ui/.project @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.unittest.ui</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature> + </natures> +</projectDescription> diff --git a/org.eclipse.unittest.ui/.settings/org.eclipse.core.resources.prefs b/org.eclipse.unittest.ui/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..99f26c020 --- /dev/null +++ b/org.eclipse.unittest.ui/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/<project>=UTF-8 diff --git a/org.eclipse.unittest.ui/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.unittest.ui/.settings/org.eclipse.core.runtime.prefs new file mode 100644 index 000000000..5a0ad22d2 --- /dev/null +++ b/org.eclipse.unittest.ui/.settings/org.eclipse.core.runtime.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +line.separator=\n diff --git a/org.eclipse.unittest.ui/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.unittest.ui/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..5ab320324 --- /dev/null +++ b/org.eclipse.unittest.ui/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,538 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.builder.cleanOutputFolder=clean +org.eclipse.jdt.core.builder.duplicateResourceTask=warning +org.eclipse.jdt.core.builder.invalidClasspath=abort +org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore +org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch +org.eclipse.jdt.core.circularClasspath=error +org.eclipse.jdt.core.classpath.exclusionPatterns=enabled +org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled +org.eclipse.jdt.core.classpath.outputOverlappingAnotherSource=error +org.eclipse.jdt.core.codeComplete.argumentPrefixes= +org.eclipse.jdt.core.codeComplete.argumentSuffixes= +org.eclipse.jdt.core.codeComplete.fieldPrefixes=f +org.eclipse.jdt.core.codeComplete.fieldSuffixes= +org.eclipse.jdt.core.codeComplete.localPrefixes= +org.eclipse.jdt.core.codeComplete.localSuffixes= +org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=fg +org.eclipse.jdt.core.codeComplete.staticFieldSuffixes= +org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes= +org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes= +org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled +org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore +org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= +org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullable.secondary= +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=11 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.maxProblemPerUnit=100 +org.eclipse.jdt.core.compiler.problem.APILeak=warning +org.eclipse.jdt.core.compiler.problem.annotatedTypeArgumentToUnannotated=info +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=info +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=error +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=warning +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +org.eclipse.jdt.core.compiler.problem.finalParameterBound=error +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=error +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=error +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=info +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=enabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=enabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=error +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning +org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning +org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning +org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error +org.eclipse.jdt.core.compiler.problem.nullReference=error +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=error +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error +org.eclipse.jdt.core.compiler.problem.potentialNullReference=info +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=info +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=info +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.suppressWarningsNotFullyAnalysed=info +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=info +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled +org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedImport=error +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=info +org.eclipse.jdt.core.compiler.problem.unusedParameter=warning +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=11 +org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false +org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false +org.eclipse.jdt.core.formatter.align_with_spaces=false +org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assertion_message=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain=0 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 +org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_record_components=16 +org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_shift_operator=0 +org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 +org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch=0 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_record_constructor=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_record_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=true +org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=true +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false +org.eclipse.jdt.core.formatter.comment.indent_root_tags=false +org.eclipse.jdt.core.formatter.comment.indent_tag_description=false +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags=do not insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert +org.eclipse.jdt.core.formatter.comment.line_length=80 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_not_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case=insert +org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=false +org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.lineSplit=120 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block=0 +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block=0 +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block=0 +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=tab +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.text_block_indentation=0 +org.eclipse.jdt.core.formatter.use_on_off_tags=false +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true +org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator=true +org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false +org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true +org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true +org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true +org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_before_relational_operator=true +org.eclipse.jdt.core.formatter.wrap_before_shift_operator=true +org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.incompatibleJDKLevel=ignore +org.eclipse.jdt.core.incompleteClasspath=error +org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/org.eclipse.unittest.ui/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.unittest.ui/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..e1de5eec4 --- /dev/null +++ b/org.eclipse.unittest.ui/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,84 @@ +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +formatter_profile=org.eclipse.jdt.ui.default.eclipse_profile +formatter_settings_version=20 +org.eclipse.jdt.ui.exception.name=e +org.eclipse.jdt.ui.gettersetter.use.is=true +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=java;javax;sun;com;org;org.apache;org.w3c;org.eclipse;org.eclipse.swt;org.eclipse.core;org.eclipse.core.runtime;org.eclipse.core.resources;org.eclipse.core.filebuffers;org.eclipse.text;org.eclipse.jface;org.eclipse.jface.text;org.eclipse.ui;org.eclipse.ui.workbench.texteditor;org.eclipse.ui.texteditor;org.eclipse.ui.editors;org.eclipse.compare;org.eclipse.debug;org.eclipse.debug.ui;org.eclipse.search;org.eclipse.search2;org.eclipse.ltk;org.eclipse.jdt.core;org.eclipse.jdt.internal;org.eclipse.jdt.launching;org.eclipse.jdt.ui;org.eclipse.jdt.internal.ui; +org.eclipse.jdt.ui.keywordthis=false +org.eclipse.jdt.ui.ondemandthreshold=99 +org.eclipse.jdt.ui.overrideannotation=true +org.eclipse.jdt.ui.staticondemandthreshold=99 +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=true +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=true +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=false +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=true +sp_cleanup.convert_to_enhanced_for_loop=true +sp_cleanup.convert_to_enhanced_for_loop_if_loop_var_used=false +sp_cleanup.correct_indentation=false +sp_cleanup.format_source_code=true +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.insert_inferred_type_arguments=false +sp_cleanup.lazy_logical_operator=false +sp_cleanup.make_local_variable_final=true +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=false +sp_cleanup.merge_conditional_blocks=false +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.number_suffix=false +sp_cleanup.objects_equals=false +sp_cleanup.on_save_use_additional_actions=true +sp_cleanup.organize_imports=true +sp_cleanup.precompile_regex=true +sp_cleanup.push_down_negation=false +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=false +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_modifiers=true +sp_cleanup.remove_redundant_semicolons=true +sp_cleanup.remove_redundant_type_arguments=true +sp_cleanup.remove_trailing_whitespaces=true +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_array_creation=true +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.simplify_lambda_expression_and_method_ref=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_autoboxing=false +sp_cleanup.use_blocks=false +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_directly_map_method=true +sp_cleanup.use_lambda=true +sp_cleanup.use_parentheses_in_expressions=false +sp_cleanup.use_this_for_non_static_field_access=false +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=false +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true +sp_cleanup.use_unboxing=false +sp_cleanup.use_var=false diff --git a/org.eclipse.unittest.ui/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.unittest.ui/.settings/org.eclipse.pde.api.tools.prefs new file mode 100644 index 000000000..9d6e2dc79 --- /dev/null +++ b/org.eclipse.unittest.ui/.settings/org.eclipse.pde.api.tools.prefs @@ -0,0 +1,102 @@ +ANNOTATION_ELEMENT_TYPE_ADDED_FIELD=Error +ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error +ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error +ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error +ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error +ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error +API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error +API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error +API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error +API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error +API_USE_SCAN_FIELD_SEVERITY=Error +API_USE_SCAN_METHOD_SEVERITY=Error +API_USE_SCAN_TYPE_SEVERITY=Error +CLASS_ELEMENT_TYPE_ADDED_FIELD=Error +CLASS_ELEMENT_TYPE_ADDED_METHOD=Error +CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error +CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error +CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error +CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error +CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error +CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error +CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error +CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error +CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error +CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error +CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error +CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error +CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error +CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error +CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error +CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error +CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error +ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error +ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error +ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error +ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error +ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error +ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error +FIELD_ELEMENT_TYPE_ADDED_VALUE=Error +FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error +FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error +FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error +FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error +FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error +FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error +FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error +FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error +FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error +ILLEGAL_EXTEND=Warning +ILLEGAL_IMPLEMENT=Warning +ILLEGAL_INSTANTIATE=Warning +ILLEGAL_OVERRIDE=Warning +ILLEGAL_REFERENCE=Warning +INTERFACE_ELEMENT_TYPE_ADDED_DEFAULT_METHOD=Error +INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error +INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error +INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error +INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error +INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error +INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error +INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error +INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error +INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error +INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error +INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error +INVALID_ANNOTATION=Ignore +INVALID_JAVADOC_TAG=Ignore +INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Ignore +LEAK_EXTEND=Warning +LEAK_FIELD_DECL=Warning +LEAK_IMPLEMENT=Warning +LEAK_METHOD_PARAM=Warning +LEAK_METHOD_RETURN_TYPE=Warning +METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error +METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error +METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error +METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error +METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error +METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error +METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error +METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error +METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error +METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error +MISSING_EE_DESCRIPTIONS=Warning +TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error +TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error +TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error +TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error +TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error +TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error +UNUSED_PROBLEM_FILTERS=Warning +automatically_removed_unused_problem_filters=false +changed_execution_env=Error +eclipse.preferences.version=1 +incompatible_api_component_version=Error +incompatible_api_component_version_report_major_without_breaking_change=Error +incompatible_api_component_version_report_minor_without_api_change=Error +invalid_since_tag_version=Error +malformed_since_tag=Error +missing_since_tag=Error +report_api_breakage_when_major_version_incremented=Disabled +report_resolution_errors_api_component=Warning diff --git a/org.eclipse.unittest.ui/.settings/org.eclipse.pde.prefs b/org.eclipse.unittest.ui/.settings/org.eclipse.pde.prefs new file mode 100644 index 000000000..9dbfcc5af --- /dev/null +++ b/org.eclipse.unittest.ui/.settings/org.eclipse.pde.prefs @@ -0,0 +1,36 @@ +compilers.f.unresolved-features=1 +compilers.f.unresolved-plugins=1 +compilers.incompatible-environment=0 +compilers.p.build=1 +compilers.p.build.bin.includes=1 +compilers.p.build.encodings=2 +compilers.p.build.java.compiler=2 +compilers.p.build.java.compliance=1 +compilers.p.build.missing.output=2 +compilers.p.build.output.library=1 +compilers.p.build.source.library=1 +compilers.p.build.src.includes=1 +compilers.p.deprecated=1 +compilers.p.discouraged-class=1 +compilers.p.exec-env-too-low=1 +compilers.p.internal=1 +compilers.p.missing-packages=2 +compilers.p.missing-version-export-package=2 +compilers.p.missing-version-import-package=2 +compilers.p.missing-version-require-bundle=1 +compilers.p.no-required-att=0 +compilers.p.no.automatic.module=1 +compilers.p.not-externalized-att=1 +compilers.p.service.component.without.lazyactivation=1 +compilers.p.unknown-attribute=0 +compilers.p.unknown-class=0 +compilers.p.unknown-element=0 +compilers.p.unknown-identifier=0 +compilers.p.unknown-resource=0 +compilers.p.unresolved-ex-points=0 +compilers.p.unresolved-import=0 +compilers.s.create-docs=false +compilers.s.doc-folder=doc +compilers.s.open-tags=1 +compilers.use-project=true +eclipse.preferences.version=1 diff --git a/org.eclipse.unittest.ui/META-INF/MANIFEST.MF b/org.eclipse.unittest.ui/META-INF/MANIFEST.MF new file mode 100644 index 000000000..9f68832bb --- /dev/null +++ b/org.eclipse.unittest.ui/META-INF/MANIFEST.MF @@ -0,0 +1,21 @@ +Manifest-Version: 1.0 +Automatic-Module-Name: org.eclipse.unittest.ui +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: org.eclipse.unittest.ui;singleton:=true +Bundle-Version: 0.1.0.qualifier +Bundle-Activator: org.eclipse.unittest.internal.UnitTestPlugin +Bundle-ActivationPolicy: lazy +Bundle-Vendor: %providerName +Bundle-Localization: plugin +Export-Package: org.eclipse.unittest.launcher, + org.eclipse.unittest.model, + org.eclipse.unittest.ui +Require-Bundle: + org.eclipse.ui.ide;bundle-version="[3.5.0,4.0.0)", + org.eclipse.jface.text;bundle-version="[3.5.0,4.0.0)", + org.eclipse.ui;bundle-version="[3.5.0,4.0.0)", + org.eclipse.debug.ui;bundle-version="[3.5.0,4.0.0)", + org.eclipse.core.runtime;bundle-version="[3.11.0,4.0.0)", + org.eclipse.compare;bundle-version="[3.5.0,4.0.0)" +Bundle-RequiredExecutionEnvironment: JavaSE-11 diff --git a/org.eclipse.unittest.ui/about.html b/org.eclipse.unittest.ui/about.html new file mode 100644 index 000000000..164f781a8 --- /dev/null +++ b/org.eclipse.unittest.ui/about.html @@ -0,0 +1,36 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> +<title>About</title> +</head> +<body lang="EN-US"> + <h2>About This Content</h2> + + <p>November 30, 2017</p> + <h3>License</h3> + + <p> + The Eclipse Foundation makes available all content in this plug-in + ("Content"). Unless otherwise indicated below, the Content + is provided to you under the terms and conditions of the Eclipse + Public License Version 2.0 ("EPL"). A copy of the EPL is + available at <a href="http://www.eclipse.org/legal/epl-2.0">http://www.eclipse.org/legal/epl-2.0</a>. + For purposes of the EPL, "Program" will mean the Content. + </p> + + <p> + If you did not receive this Content directly from the Eclipse + Foundation, the Content is being redistributed by another party + ("Redistributor") and different terms and conditions may + apply to your use of any object code in the Content. Check the + Redistributor's license that was provided with the Content. If no such + license exists, contact the Redistributor. Unless otherwise indicated + below, the terms and conditions of the EPL still apply to any source + code in the Content and such source code may be obtained at <a + href="http://www.eclipse.org/">http://www.eclipse.org</a>. + </p> + +</body> +</html>
\ No newline at end of file diff --git a/org.eclipse.unittest.ui/build.properties b/org.eclipse.unittest.ui/build.properties new file mode 100644 index 000000000..136d1bca6 --- /dev/null +++ b/org.eclipse.unittest.ui/build.properties @@ -0,0 +1,25 @@ +############################################################################### +# Copyright (c) 2000, 2011 IBM Corporation and others. +# +# This program and the accompanying materials +# are made available under the terms of the Eclipse Public License 2.0 +# which accompanies this distribution, and is available at +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# IBM Corporation - initial API and implementation +############################################################################### +bin.includes = plugin.xml,\ + about.html,\ + icons/,\ + plugin.properties,\ + .,\ + META-INF/ + +source.. = src/ +src.includes = about.html,\ + schema/ + +javacWarnings..=-unavoidableGenericProblems diff --git a/org.eclipse.unittest.ui/icons-work/unitViewIcon.xcf b/org.eclipse.unittest.ui/icons-work/unitViewIcon.xcf Binary files differnew file mode 100644 index 000000000..9391c1c7d --- /dev/null +++ b/org.eclipse.unittest.ui/icons-work/unitViewIcon.xcf diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/cfilter.png b/org.eclipse.unittest.ui/icons/full/dlcl16/cfilter.png Binary files differnew file mode 100644 index 000000000..fd883fb43 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/cfilter.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/cfilter@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/cfilter@2x.png Binary files differnew file mode 100644 index 000000000..1473192df --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/cfilter@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/compare.png b/org.eclipse.unittest.ui/icons/full/dlcl16/compare.png Binary files differnew file mode 100644 index 000000000..cbeeeabe5 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/compare.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/compare@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/compare@2x.png Binary files differnew file mode 100644 index 000000000..bd6e98107 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/compare@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/flatLayout.png b/org.eclipse.unittest.ui/icons/full/dlcl16/flatLayout.png Binary files differnew file mode 100644 index 000000000..061b593ca --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/flatLayout.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/flatLayout@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/flatLayout@2x.png Binary files differnew file mode 100644 index 000000000..23540f72b --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/flatLayout@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/hierarchicalLayout.png b/org.eclipse.unittest.ui/icons/full/dlcl16/hierarchicalLayout.png Binary files differnew file mode 100644 index 000000000..64822eaa0 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/hierarchicalLayout.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/hierarchicalLayout@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/hierarchicalLayout@2x.png Binary files differnew file mode 100644 index 000000000..b84fb01bf --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/hierarchicalLayout@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/history_list.png b/org.eclipse.unittest.ui/icons/full/dlcl16/history_list.png Binary files differnew file mode 100644 index 000000000..033bef13d --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/history_list.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/history_list@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/history_list@2x.png Binary files differnew file mode 100644 index 000000000..2a9eeb412 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/history_list@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/lock.png b/org.eclipse.unittest.ui/icons/full/dlcl16/lock.png Binary files differnew file mode 100644 index 000000000..615dbbb64 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/lock.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/lock@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/lock@2x.png Binary files differnew file mode 100644 index 000000000..cde2a0f41 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/lock@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/open_console.png b/org.eclipse.unittest.ui/icons/full/dlcl16/open_console.png Binary files differnew file mode 100644 index 000000000..735f57b08 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/open_console.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/open_console@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/open_console@2x.png Binary files differnew file mode 100644 index 000000000..e0232a871 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/open_console@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/relaunch.png b/org.eclipse.unittest.ui/icons/full/dlcl16/relaunch.png Binary files differnew file mode 100644 index 000000000..f84f509a0 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/relaunch.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/relaunch@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/relaunch@2x.png Binary files differnew file mode 100644 index 000000000..42a5cbddf --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/relaunch@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/relaunchf.png b/org.eclipse.unittest.ui/icons/full/dlcl16/relaunchf.png Binary files differnew file mode 100644 index 000000000..b30f5738a --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/relaunchf.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/relaunchf@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/relaunchf@2x.png Binary files differnew file mode 100644 index 000000000..a4a23a5e6 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/relaunchf@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/select_next.png b/org.eclipse.unittest.ui/icons/full/dlcl16/select_next.png Binary files differnew file mode 100644 index 000000000..1a71a7923 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/select_next.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/select_next@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/select_next@2x.png Binary files differnew file mode 100644 index 000000000..064c3002c --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/select_next@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/select_prev.png b/org.eclipse.unittest.ui/icons/full/dlcl16/select_prev.png Binary files differnew file mode 100644 index 000000000..7eaaff979 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/select_prev.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/select_prev@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/select_prev@2x.png Binary files differnew file mode 100644 index 000000000..0b95e38b5 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/select_prev@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/stop.png b/org.eclipse.unittest.ui/icons/full/dlcl16/stop.png Binary files differnew file mode 100644 index 000000000..ed5e84804 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/stop.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/stop@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/stop@2x.png Binary files differnew file mode 100644 index 000000000..561447b40 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/stop@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/th_automatic.png b/org.eclipse.unittest.ui/icons/full/dlcl16/th_automatic.png Binary files differnew file mode 100644 index 000000000..0d8f6ddd1 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/th_automatic.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/th_automatic@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/th_automatic@2x.png Binary files differnew file mode 100644 index 000000000..7e5f9d7a8 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/th_automatic@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/th_horizontal.png b/org.eclipse.unittest.ui/icons/full/dlcl16/th_horizontal.png Binary files differnew file mode 100644 index 000000000..a7b1f6bbd --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/th_horizontal.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/th_horizontal@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/th_horizontal@2x.png Binary files differnew file mode 100644 index 000000000..943c571bb --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/th_horizontal@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/th_vertical.png b/org.eclipse.unittest.ui/icons/full/dlcl16/th_vertical.png Binary files differnew file mode 100644 index 000000000..a524112f9 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/th_vertical.png diff --git a/org.eclipse.unittest.ui/icons/full/dlcl16/th_vertical@2x.png b/org.eclipse.unittest.ui/icons/full/dlcl16/th_vertical@2x.png Binary files differnew file mode 100644 index 000000000..a12d09145 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dlcl16/th_vertical@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/dtool16/new_testcase.png b/org.eclipse.unittest.ui/icons/full/dtool16/new_testcase.png Binary files differnew file mode 100644 index 000000000..d9cb08eac --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dtool16/new_testcase.png diff --git a/org.eclipse.unittest.ui/icons/full/dtool16/new_testcase@2x.png b/org.eclipse.unittest.ui/icons/full/dtool16/new_testcase@2x.png Binary files differnew file mode 100644 index 000000000..5c596075d --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dtool16/new_testcase@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/dtool16/new_testsuite.png b/org.eclipse.unittest.ui/icons/full/dtool16/new_testsuite.png Binary files differnew file mode 100644 index 000000000..eda2dad33 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dtool16/new_testsuite.png diff --git a/org.eclipse.unittest.ui/icons/full/dtool16/new_testsuite@2x.png b/org.eclipse.unittest.ui/icons/full/dtool16/new_testsuite@2x.png Binary files differnew file mode 100644 index 000000000..2441d86cf --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/dtool16/new_testsuite@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/cfilter.png b/org.eclipse.unittest.ui/icons/full/elcl16/cfilter.png Binary files differnew file mode 100644 index 000000000..e779b3e29 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/cfilter.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/cfilter@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/cfilter@2x.png Binary files differnew file mode 100644 index 000000000..3bea811c8 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/cfilter@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/compare.png b/org.eclipse.unittest.ui/icons/full/elcl16/compare.png Binary files differnew file mode 100644 index 000000000..07fb14ee9 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/compare.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/compare@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/compare@2x.png Binary files differnew file mode 100644 index 000000000..89c631a38 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/compare@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/flatLayout.png b/org.eclipse.unittest.ui/icons/full/elcl16/flatLayout.png Binary files differnew file mode 100644 index 000000000..13f83a237 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/flatLayout.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/flatLayout@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/flatLayout@2x.png Binary files differnew file mode 100644 index 000000000..280561aeb --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/flatLayout@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/hierarchicalLayout.png b/org.eclipse.unittest.ui/icons/full/elcl16/hierarchicalLayout.png Binary files differnew file mode 100644 index 000000000..665aa5cee --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/hierarchicalLayout.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/hierarchicalLayout@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/hierarchicalLayout@2x.png Binary files differnew file mode 100644 index 000000000..84039be3a --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/hierarchicalLayout@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/history_list.png b/org.eclipse.unittest.ui/icons/full/elcl16/history_list.png Binary files differnew file mode 100644 index 000000000..7808b5070 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/history_list.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/history_list@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/history_list@2x.png Binary files differnew file mode 100644 index 000000000..585fc17b9 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/history_list@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/lock.png b/org.eclipse.unittest.ui/icons/full/elcl16/lock.png Binary files differnew file mode 100644 index 000000000..91fef558a --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/lock.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/lock@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/lock@2x.png Binary files differnew file mode 100644 index 000000000..8742944db --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/lock@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/open_console.png b/org.eclipse.unittest.ui/icons/full/elcl16/open_console.png Binary files differnew file mode 100644 index 000000000..fdb3b0cb3 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/open_console.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/open_console@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/open_console@2x.png Binary files differnew file mode 100644 index 000000000..55bcdc0e2 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/open_console@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/relaunch.png b/org.eclipse.unittest.ui/icons/full/elcl16/relaunch.png Binary files differnew file mode 100644 index 000000000..89228fa08 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/relaunch.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/relaunch@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/relaunch@2x.png Binary files differnew file mode 100644 index 000000000..c0a3d3ab8 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/relaunch@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/relaunchf.png b/org.eclipse.unittest.ui/icons/full/elcl16/relaunchf.png Binary files differnew file mode 100644 index 000000000..d25cafa34 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/relaunchf.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/relaunchf@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/relaunchf@2x.png Binary files differnew file mode 100644 index 000000000..74550215f --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/relaunchf@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/select_next.png b/org.eclipse.unittest.ui/icons/full/elcl16/select_next.png Binary files differnew file mode 100644 index 000000000..9fcc646d9 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/select_next.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/select_next@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/select_next@2x.png Binary files differnew file mode 100644 index 000000000..d46b3a062 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/select_next@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/select_prev.png b/org.eclipse.unittest.ui/icons/full/elcl16/select_prev.png Binary files differnew file mode 100644 index 000000000..f2e6a039c --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/select_prev.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/select_prev@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/select_prev@2x.png Binary files differnew file mode 100644 index 000000000..8e26ea4b4 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/select_prev@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/stop.png b/org.eclipse.unittest.ui/icons/full/elcl16/stop.png Binary files differnew file mode 100644 index 000000000..032a26c1c --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/stop.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/stop@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/stop@2x.png Binary files differnew file mode 100644 index 000000000..a7dc14cc9 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/stop@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/th_automatic.png b/org.eclipse.unittest.ui/icons/full/elcl16/th_automatic.png Binary files differnew file mode 100644 index 000000000..a1103e8a5 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/th_automatic.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/th_automatic@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/th_automatic@2x.png Binary files differnew file mode 100644 index 000000000..bf6057510 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/th_automatic@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/th_horizontal.png b/org.eclipse.unittest.ui/icons/full/elcl16/th_horizontal.png Binary files differnew file mode 100644 index 000000000..ca4602849 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/th_horizontal.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/th_horizontal@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/th_horizontal@2x.png Binary files differnew file mode 100644 index 000000000..217f8560e --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/th_horizontal@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/th_vertical.png b/org.eclipse.unittest.ui/icons/full/elcl16/th_vertical.png Binary files differnew file mode 100644 index 000000000..07baf5138 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/th_vertical.png diff --git a/org.eclipse.unittest.ui/icons/full/elcl16/th_vertical@2x.png b/org.eclipse.unittest.ui/icons/full/elcl16/th_vertical@2x.png Binary files differnew file mode 100644 index 000000000..62e0a379a --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/elcl16/th_vertical@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/etool16/new_testcase.png b/org.eclipse.unittest.ui/icons/full/etool16/new_testcase.png Binary files differnew file mode 100644 index 000000000..7c4692f69 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/etool16/new_testcase.png diff --git a/org.eclipse.unittest.ui/icons/full/etool16/new_testcase@2x.png b/org.eclipse.unittest.ui/icons/full/etool16/new_testcase@2x.png Binary files differnew file mode 100644 index 000000000..eb4401afe --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/etool16/new_testcase@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/etool16/new_testsuite.png b/org.eclipse.unittest.ui/icons/full/etool16/new_testsuite.png Binary files differnew file mode 100644 index 000000000..8ceabbcb7 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/etool16/new_testsuite.png diff --git a/org.eclipse.unittest.ui/icons/full/etool16/new_testsuite@2x.png b/org.eclipse.unittest.ui/icons/full/etool16/new_testsuite@2x.png Binary files differnew file mode 100644 index 000000000..87bc8eb52 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/etool16/new_testsuite@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/eview16/stackframe.png b/org.eclipse.unittest.ui/icons/full/eview16/stackframe.png Binary files differnew file mode 100644 index 000000000..cffa1aaea --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/eview16/stackframe.png diff --git a/org.eclipse.unittest.ui/icons/full/eview16/stackframe@2x.png b/org.eclipse.unittest.ui/icons/full/eview16/stackframe@2x.png Binary files differnew file mode 100644 index 000000000..3f880c529 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/eview16/stackframe@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/eview16/unit.png b/org.eclipse.unittest.ui/icons/full/eview16/unit.png Binary files differnew file mode 100644 index 000000000..358eba121 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/eview16/unit.png diff --git a/org.eclipse.unittest.ui/icons/full/eview16/unit@2x.png b/org.eclipse.unittest.ui/icons/full/eview16/unit@2x.png Binary files differnew file mode 100644 index 000000000..70cf88e8b --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/eview16/unit@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/eview16/uniterr.png b/org.eclipse.unittest.ui/icons/full/eview16/uniterr.png Binary files differnew file mode 100644 index 000000000..834deffeb --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/eview16/uniterr.png diff --git a/org.eclipse.unittest.ui/icons/full/eview16/uniterr@2x.png b/org.eclipse.unittest.ui/icons/full/eview16/uniterr@2x.png Binary files differnew file mode 100644 index 000000000..7b71aa58b --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/eview16/uniterr@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/eview16/uniterrq.png b/org.eclipse.unittest.ui/icons/full/eview16/uniterrq.png Binary files differnew file mode 100644 index 000000000..9e17bb9f5 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/eview16/uniterrq.png diff --git a/org.eclipse.unittest.ui/icons/full/eview16/uniterrq@2x.png b/org.eclipse.unittest.ui/icons/full/eview16/uniterrq@2x.png Binary files differnew file mode 100644 index 000000000..aa2e0b5ab --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/eview16/uniterrq@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/eview16/unitsucc.png b/org.eclipse.unittest.ui/icons/full/eview16/unitsucc.png Binary files differnew file mode 100644 index 000000000..ef6dd9412 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/eview16/unitsucc.png diff --git a/org.eclipse.unittest.ui/icons/full/eview16/unitsucc@2x.png b/org.eclipse.unittest.ui/icons/full/eview16/unitsucc@2x.png Binary files differnew file mode 100644 index 000000000..53b19687b --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/eview16/unitsucc@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/eview16/unitsuccq.png b/org.eclipse.unittest.ui/icons/full/eview16/unitsuccq.png Binary files differnew file mode 100644 index 000000000..97884575b --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/eview16/unitsuccq.png diff --git a/org.eclipse.unittest.ui/icons/full/eview16/unitsuccq@2x.png b/org.eclipse.unittest.ui/icons/full/eview16/unitsuccq@2x.png Binary files differnew file mode 100644 index 000000000..1a7a42572 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/eview16/unitsuccq@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/exc_catch.png b/org.eclipse.unittest.ui/icons/full/obj16/exc_catch.png Binary files differnew file mode 100644 index 000000000..2962f6e63 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/exc_catch.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/exc_catch@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/exc_catch@2x.png Binary files differnew file mode 100644 index 000000000..b943d360d --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/exc_catch@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/faillist.png b/org.eclipse.unittest.ui/icons/full/obj16/faillist.png Binary files differnew file mode 100644 index 000000000..c33980f65 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/faillist.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/faillist@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/faillist@2x.png Binary files differnew file mode 100644 index 000000000..d3686b843 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/faillist@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/failures.png b/org.eclipse.unittest.ui/icons/full/obj16/failures.png Binary files differnew file mode 100644 index 000000000..c2cd90d96 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/failures.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/failures@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/failures@2x.png Binary files differnew file mode 100644 index 000000000..1d6815daf --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/failures@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/julaunch.png b/org.eclipse.unittest.ui/icons/full/obj16/julaunch.png Binary files differnew file mode 100644 index 000000000..ede46fbac --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/julaunch.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/julaunch@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/julaunch@2x.png Binary files differnew file mode 100644 index 000000000..e9e9b1d69 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/julaunch@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/stkfrm_obj.png b/org.eclipse.unittest.ui/icons/full/obj16/stkfrm_obj.png Binary files differnew file mode 100644 index 000000000..cffa1aaea --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/stkfrm_obj.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/stkfrm_obj@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/stkfrm_obj@2x.png Binary files differnew file mode 100644 index 000000000..3f880c529 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/stkfrm_obj@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/test.png b/org.eclipse.unittest.ui/icons/full/obj16/test.png Binary files differnew file mode 100644 index 000000000..c95f6b58b --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/test.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/test@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/test@2x.png Binary files differnew file mode 100644 index 000000000..c404f6507 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/test@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testassumptionfailed.png b/org.eclipse.unittest.ui/icons/full/obj16/testassumptionfailed.png Binary files differnew file mode 100644 index 000000000..e12b607ed --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/testassumptionfailed.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testassumptionfailed@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/testassumptionfailed@2x.png Binary files differnew file mode 100644 index 000000000..4702a33f3 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/testassumptionfailed@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testerr.png b/org.eclipse.unittest.ui/icons/full/obj16/testerr.png Binary files differnew file mode 100644 index 000000000..1814c22a0 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/testerr.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testerr@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/testerr@2x.png Binary files differnew file mode 100644 index 000000000..221811a32 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/testerr@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testfail.png b/org.eclipse.unittest.ui/icons/full/obj16/testfail.png Binary files differnew file mode 100644 index 000000000..f7ebe5efd --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/testfail.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testfail@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/testfail@2x.png Binary files differnew file mode 100644 index 000000000..944e6381c --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/testfail@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testfile_obj.png b/org.eclipse.unittest.ui/icons/full/obj16/testfile_obj.png Binary files differnew file mode 100644 index 000000000..0905b505f --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/testfile_obj.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testfile_obj@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/testfile_obj@2x.png Binary files differnew file mode 100644 index 000000000..9cb969611 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/testfile_obj@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testhier.png b/org.eclipse.unittest.ui/icons/full/obj16/testhier.png Binary files differnew file mode 100644 index 000000000..acfde8818 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/testhier.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testhier@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/testhier@2x.png Binary files differnew file mode 100644 index 000000000..9ab1d917f --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/testhier@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testignored.png b/org.eclipse.unittest.ui/icons/full/obj16/testignored.png Binary files differnew file mode 100644 index 000000000..7f241980b --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/testignored.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testignored@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/testignored@2x.png Binary files differnew file mode 100644 index 000000000..459d022cf --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/testignored@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testok.png b/org.eclipse.unittest.ui/icons/full/obj16/testok.png Binary files differnew file mode 100644 index 000000000..c27c47ea5 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/testok.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testok@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/testok@2x.png Binary files differnew file mode 100644 index 000000000..272d3577b --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/testok@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testrun.png b/org.eclipse.unittest.ui/icons/full/obj16/testrun.png Binary files differnew file mode 100644 index 000000000..8a08a1d33 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/testrun.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/testrun@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/testrun@2x.png Binary files differnew file mode 100644 index 000000000..54c54d615 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/testrun@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuite.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuite.png Binary files differnew file mode 100644 index 000000000..d45394e5a --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuite.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuite@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuite@2x.png Binary files differnew file mode 100644 index 000000000..b3bdb79f7 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuite@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuiteerror.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuiteerror.png Binary files differnew file mode 100644 index 000000000..53ab5187e --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuiteerror.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuiteerror@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuiteerror@2x.png Binary files differnew file mode 100644 index 000000000..c1dd70b1e --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuiteerror@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuitefail.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuitefail.png Binary files differnew file mode 100644 index 000000000..21402750d --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuitefail.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuitefail@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuitefail@2x.png Binary files differnew file mode 100644 index 000000000..830da7e99 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuitefail@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuiteok.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuiteok.png Binary files differnew file mode 100644 index 000000000..a6bfbf83e --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuiteok.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuiteok@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuiteok@2x.png Binary files differnew file mode 100644 index 000000000..bb0604352 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuiteok@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuiterun.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuiterun.png Binary files differnew file mode 100644 index 000000000..d4d5170be --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuiterun.png diff --git a/org.eclipse.unittest.ui/icons/full/obj16/tsuiterun@2x.png b/org.eclipse.unittest.ui/icons/full/obj16/tsuiterun@2x.png Binary files differnew file mode 100644 index 000000000..d829ce8f1 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/obj16/tsuiterun@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/ovr16/error_ovr.png b/org.eclipse.unittest.ui/icons/full/ovr16/error_ovr.png Binary files differnew file mode 100644 index 000000000..412588ac5 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/ovr16/error_ovr.png diff --git a/org.eclipse.unittest.ui/icons/full/ovr16/error_ovr@2x.png b/org.eclipse.unittest.ui/icons/full/ovr16/error_ovr@2x.png Binary files differnew file mode 100644 index 000000000..683b3f743 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/ovr16/error_ovr@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/ovr16/failed_ovr.png b/org.eclipse.unittest.ui/icons/full/ovr16/failed_ovr.png Binary files differnew file mode 100644 index 000000000..a2f7f2812 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/ovr16/failed_ovr.png diff --git a/org.eclipse.unittest.ui/icons/full/ovr16/failed_ovr@2x.png b/org.eclipse.unittest.ui/icons/full/ovr16/failed_ovr@2x.png Binary files differnew file mode 100644 index 000000000..453e52792 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/ovr16/failed_ovr@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/ovr16/success_ovr.png b/org.eclipse.unittest.ui/icons/full/ovr16/success_ovr.png Binary files differnew file mode 100644 index 000000000..40c5322bd --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/ovr16/success_ovr.png diff --git a/org.eclipse.unittest.ui/icons/full/ovr16/success_ovr@2x.png b/org.eclipse.unittest.ui/icons/full/ovr16/success_ovr@2x.png Binary files differnew file mode 100644 index 000000000..d912a1772 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/ovr16/success_ovr@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/wizban/newsuite_wiz.png b/org.eclipse.unittest.ui/icons/full/wizban/newsuite_wiz.png Binary files differnew file mode 100644 index 000000000..55f6dbb33 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/wizban/newsuite_wiz.png diff --git a/org.eclipse.unittest.ui/icons/full/wizban/newsuite_wiz@2x.png b/org.eclipse.unittest.ui/icons/full/wizban/newsuite_wiz@2x.png Binary files differnew file mode 100644 index 000000000..425b38e38 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/wizban/newsuite_wiz@2x.png diff --git a/org.eclipse.unittest.ui/icons/full/wizban/newtest_wiz.png b/org.eclipse.unittest.ui/icons/full/wizban/newtest_wiz.png Binary files differnew file mode 100644 index 000000000..d601a97f2 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/wizban/newtest_wiz.png diff --git a/org.eclipse.unittest.ui/icons/full/wizban/newtest_wiz@2x.png b/org.eclipse.unittest.ui/icons/full/wizban/newtest_wiz@2x.png Binary files differnew file mode 100644 index 000000000..9011edd06 --- /dev/null +++ b/org.eclipse.unittest.ui/icons/full/wizban/newtest_wiz@2x.png diff --git a/org.eclipse.unittest.ui/plugin.properties b/org.eclipse.unittest.ui/plugin.properties new file mode 100644 index 000000000..473441c53 --- /dev/null +++ b/org.eclipse.unittest.ui/plugin.properties @@ -0,0 +1,31 @@ +############################################################################### +# Copyright (c) 2000, 2019 IBM Corporation and others. +# +# This program and the accompanying materials +# are made available under the terms of the Eclipse Public License 2.0 +# which accompanies this distribution, and is available at +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# IBM Corporation - initial API and implementation +############################################################################### +pluginName=Debug Unit Test support +providerName=Eclipse.org + +testRunListeners.name= Test Run Listeners + +testViewSupport.name= Unit Test View Support + +View.label= Test Results + +UnitTestShortcut.label= Unit Test +UnitTestShortcut.description.run= Run Unit Test +UnitTestShortcut.description.debug= Debug Unit Test +UnitTestShortcut.description.rerunLast= Rerun Unit Test +UnitTestShortcut.description.rerunFailedCases= Rerun Unit Test - Failures only +UnitTestShortcut.description.history=Test Sessions History + +GotoTestCommand.name= Referring Tests +GotoTestCommand.description= Referring Tests diff --git a/org.eclipse.unittest.ui/plugin.xml b/org.eclipse.unittest.ui/plugin.xml new file mode 100644 index 000000000..1e351367e --- /dev/null +++ b/org.eclipse.unittest.ui/plugin.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.0"?> +<plugin> + + <extension-point id="unittestViewSupport" name="%testViewSupport.name" schema="schema/unittestViewSupport.exsd"/> + + <extension + point="org.eclipse.ui.views"> + <view + name="%View.label" + icon="$nl$/icons/full/eview16/unit.png" + category="org.eclipse.debug.ui" + fastViewWidthRatio="0.40" + class="org.eclipse.unittest.internal.ui.TestRunnerViewPart" + id="org.eclipse.unittest.ui.ResultView"> + </view> + </extension> + + <extension + point="org.eclipse.ui.commands"> + <command + name="%GotoTestCommand.name" + description="%GotoTestCommand.description" + categoryId="org.eclipse.search.ui.category.search" + id="org.eclipse.unittest.ui.gotoTest"> + </command> + <command + name="%UnitTestShortcut.description.run" + description="%UnitTestShortcut.description.run" + categoryId="org.eclipse.debug.ui.category.run" + id="org.eclipse.unittest.ui.UnitTestShortcut.run"> + </command> + <command + name="%UnitTestShortcut.description.debug" + description="%UnitTestShortcut.description.debug" + categoryId="org.eclipse.debug.ui.category.run" + id="org.eclipse.unittest.ui.UnitTestShortcut.debug"> + </command> + <command + name="%UnitTestShortcut.description.rerunLast" + description="%UnitTestShortcut.description.rerunLast" + categoryId="org.eclipse.debug.ui.category.run" + id="org.eclipse.unittest.ui.UnitTestShortcut.rerunLast"> + </command> + <command + name="%UnitTestShortcut.description.rerunFailedCases" + description="%UnitTestShortcut.description.rerunFailedCases" + categoryId="org.eclipse.debug.ui.category.run" + id="org.eclipse.unittest.ui.UnitTestShortcut.rerunFailedCases"> + </command> + <command + defaultHandler="org.eclipse.unittest.internal.ui.history.HistoryHandler" + description="%UnitTestShortcut.description.history" + id="org.eclipse.unittest.ui.history" + name="%UnitTestShortcut.description.history"> + </command> + </extension> + <extension + point="org.eclipse.ui.commandImages"> + <image + commandId="org.eclipse.unittest.ui.history" + disabledIcon="icons/full/dlcl16/history_list.png" + icon="icons/full/elcl16/history_list.png"> + </image> + </extension> + +</plugin> diff --git a/org.eclipse.unittest.ui/pom.xml b/org.eclipse.unittest.ui/pom.xml new file mode 100644 index 000000000..b2f8e31cf --- /dev/null +++ b/org.eclipse.unittest.ui/pom.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (c) 2012, 2019 Eclipse Foundation and others. + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Distribution License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/org/documents/edl-v10.php + + Contributors: + Igor Fedorenko - initial implementation + Jens Reimann (jreimann@redhat.com) - add copy & paste support +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <artifactId>eclipse.platform.debug</artifactId> + <groupId>eclipse.platform.debug</groupId> + <version>4.18.0-SNAPSHOT</version> + </parent> + <groupId>org.eclipse.debug</groupId> + <artifactId>org.eclipse.unittest.ui</artifactId> + <version>0.1.0-SNAPSHOT</version> + <packaging>eclipse-plugin</packaging> + <properties> + <skipAPIAnalysis>true</skipAPIAnalysis> + </properties> +</project> diff --git a/org.eclipse.unittest.ui/schema/unittestViewSupport.exsd b/org.eclipse.unittest.ui/schema/unittestViewSupport.exsd new file mode 100644 index 000000000..2217935a8 --- /dev/null +++ b/org.eclipse.unittest.ui/schema/unittestViewSupport.exsd @@ -0,0 +1,98 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.unittest.ui" xmlns="http://www.w3.org/2001/XMLSchema"> +<annotation> + <appInfo> + <meta.schema plugin="org.eclipse.unittest.ui" id="unittestViewSupport" name="UnitTest View Support"/> + </appInfo> + <documentation> + Internal extension point to register Unit Test View support. + </documentation> + </annotation> + + <element name="extension"> + <annotation> + <appInfo> + <meta.element /> + </appInfo> + </annotation> + <complexType> + <sequence> + <element ref="viewSupport" minOccurs="1" maxOccurs="unbounded"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + a fully qualified extention point identifier + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + a fully qualified identifier of the target extension point + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + a name of this extention point + </documentation> + <appInfo> + <meta.attribute translatable="true"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="viewSupport"> + <annotation> + <documentation> + A Test View Support definition that implements org.eclipse.unittest.launcher.ITestViewSupport. +This is used by the <i>Unit Test</i> view to provide support for test engines. + </documentation> + </annotation> + <complexType> + <attribute name="id" type="string" use="required"> + <annotation> + <documentation> + a fully qualified extention point identifier. +This identifier is used to load support for the <i>Unit Test</i> view when some ILaunch is running, and the underlying launch configuration sets attributes <code>UnitTestLaunchConfigurationConstants.ATTR_UNIT_TEST_VIEW_SUPPORT</code> to the given id. + </documentation> + </annotation> + </attribute> + <attribute name="class" type="string" use="required"> + <annotation> + <documentation> + a fully qualified class name that implements org.eclipse.unittest.launcher.ITestViewSupport. +This is used by the <i>Unit Test</i> view to provide support for test engines. +The class defines the method that creates a Unit Test Runner Client dedicated to communicate with a test engine and to gather the results of testing as well as a number of methods that help to the <i>Unit Test</i> view in creation of unit test related actions, like openning a test source or a file reported on a stack trace in editor or returning a ILaunchConfiguration for Rerun action. + </documentation> + <appInfo> + <meta.attribute kind="java" basedOn=":org.eclipse.unittest.launcher.ITestViewSupport"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + + + + + <annotation> + <appInfo> + <meta.section type="copyright"/> + </appInfo> + <documentation> + Copyright (c) 2006, 2020 IBM Corporation and others. + +This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at <a href="https://www.eclipse.org/legal/epl-2.0">https://www.eclipse.org/legal/epl-v20.html</a>/ + +SPDX-License-Identifier: EPL-2.0 + </documentation> + </annotation> + +</schema> diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/UnitTestPlugin.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/UnitTestPlugin.java new file mode 100644 index 000000000..b1f3a2585 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/UnitTestPlugin.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2000, 2017 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal; + +import org.osgi.framework.BundleContext; + +import org.eclipse.unittest.internal.model.UnitTestLaunchListener; +import org.eclipse.unittest.internal.model.UnitTestModel; +import org.eclipse.unittest.internal.ui.history.History; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.preferences.InstanceScope; + +import org.eclipse.ui.plugin.AbstractUIPlugin; + +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunchListener; + +/** + * The plug-in runtime class for the Unit Test plug-in. + */ +public class UnitTestPlugin extends AbstractUIPlugin { + + /** + * The single instance of this plug-in runtime class. + */ + private static UnitTestPlugin fgPlugin = null; + + private final ILaunchListener fLaunchListener = new UnitTestLaunchListener(); + + public static final String PLUGIN_ID = "org.eclipse.unittest.ui"; //$NON-NLS-1$ + + /** + * Constructs a {@link UnitTestPlugin} object + */ + public UnitTestPlugin() { + fgPlugin = this; + } + + /** + * Returns the {@link UnitTestPlugin} instance + * + * @return a {@link UnitTestPlugin} instance + */ + public static UnitTestPlugin getDefault() { + return fgPlugin; + } + + /** + * Logs the given exception. + * + * @param e the {@link Throwable} to log + */ + public static void log(Throwable e) { + log(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.ERROR, "Error", e)); //$NON-NLS-1$ + } + + /** + * Logs the given status. + * + * @param status the status to log + */ + public static void log(IStatus status) { + getDefault().getLog().log(status); + } + + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + UnitTestModel.getInstance().start(); + DebugPlugin.getDefault().getLaunchManager().addLaunchListener(fLaunchListener); + } + + @Override + public void stop(BundleContext context) throws Exception { + try { + InstanceScope.INSTANCE.getNode(UnitTestPlugin.PLUGIN_ID).flush(); + UnitTestModel.getInstance().stop(); + DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(fLaunchListener); + History.INSTANCE.clear(); + } finally { + super.stop(context); + } + } + +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/UnitTestPreferencesConstants.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/UnitTestPreferencesConstants.java new file mode 100644 index 000000000..1afcd46b4 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/UnitTestPreferencesConstants.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2000, 2017 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.preferences.InstanceScope; + +/** + * Defines constants which are used to refer to values in the plugin's + * preference store. + */ +public class UnitTestPreferencesConstants { + /** + * Boolean preference controlling whether the failure stack should be filtered. + */ + public static final String DO_FILTER_STACK = UnitTestPlugin.PLUGIN_ID + ".do_filter_stack"; //$NON-NLS-1$ + + /** + * Boolean preference controlling whether the Unit Test view should be shown on + * errors only. + */ + public static final String SHOW_ON_ERROR_ONLY = UnitTestPlugin.PLUGIN_ID + ".show_on_error"; //$NON-NLS-1$ + + /** + * Maximum number of remembered test runs. + */ + public static final String MAX_TEST_RUNS = UnitTestPlugin.PLUGIN_ID + ".max_test_runs"; //$NON-NLS-1$ + + private UnitTestPreferencesConstants() { + // no instance + } + + /** + * Serializes the array of strings into one comma-separated string. + * + * @param list array of strings + * @return a single string composed of the given list + */ + public static String serializeList(String[] list) { + if (list == null) + return ""; //$NON-NLS-1$ + + StringBuilder buffer = new StringBuilder(); + for (int i = 0; i < list.length; i++) { + if (i > 0) + buffer.append(','); + + buffer.append(list[i]); + } + return buffer.toString(); + } + + /** + * Parses the comma-separated string into an array of strings. + * + * @param listString a comma-separated string + * @return an array of strings + */ + public static String[] parseList(String listString) { + List<String> list = new ArrayList<>(10); + StringTokenizer tokenizer = new StringTokenizer(listString, ","); //$NON-NLS-1$ + while (tokenizer.hasMoreTokens()) + list.add(tokenizer.nextToken()); + return list.toArray(new String[list.size()]); + } + + /** + * Indicates if a filter patterns are to be applied on a stacktrace/error + * messages + * + * @return <code>true</code> in case the stacktrace is to be filtered, otherwise + * - <code>false</code> + */ + public static boolean getFilterStack() { + return Platform.getPreferencesService().getBoolean(UnitTestPlugin.PLUGIN_ID, DO_FILTER_STACK, false, null); + } + + /** + * Sets up a value for the DO_FILTER_STACK preference + * + * @param filter boolean indicating if a stacktrace is to be filtered + */ + public static void setFilterStack(boolean filter) { + InstanceScope.INSTANCE.getNode(UnitTestPlugin.PLUGIN_ID).putBoolean(DO_FILTER_STACK, filter); + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/HistoryEntryHandler.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/HistoryEntryHandler.java new file mode 100644 index 000000000..c55df2bd3 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/HistoryEntryHandler.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2020 Red Ha, Inc. and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package org.eclipse.unittest.internal.junitXmlReport; + +import java.time.Instant; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import org.eclipse.core.runtime.OperationCanceledException; + +/** + * Minimal reader to populate history entry data. + */ +public class HistoryEntryHandler extends DefaultHandler { + + private int failuresAndErrors; + private Instant startTime; + private String name; + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if (Thread.interrupted()) + throw new OperationCanceledException(); + + if (IXMLTags.NODE_TESTRUN.equals(qName)) { + String attribute = attributes.getValue(IXMLTags.ATTR_ERRORS); + if (attribute != null) { + failuresAndErrors = getFailuresAndErrors() + Integer.parseInt(attribute); + } + attribute = attributes.getValue(IXMLTags.ATTR_FAILURES); + if (attribute != null) { + failuresAndErrors = getFailuresAndErrors() + Integer.parseInt(attribute); + } + name = attributes.getValue(IXMLTags.ATTR_NAME); + attribute = attributes.getValue(IXMLTags.ATTR_START_TIME); + if (attribute != null) { + startTime = Instant.parse(attribute); + } + } + } + + /** + * Returns the number of failures and errors for the history entry + * + * @return a number of failures and errors + */ + public int getFailuresAndErrors() { + return failuresAndErrors; + } + + /** + * Returns an {@link Instant} object instance indicating a unit test start time. + * + * @return an {@link Instant} object instance + */ + public Instant getStartTime() { + return startTime; + } + + /** + * Returns a name of history entry + * + * @return a name of history entry + */ + public String getName() { + return name; + } +}
\ No newline at end of file diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/IXMLTags.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/IXMLTags.java new file mode 100644 index 000000000..efe388a47 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/IXMLTags.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * Copyright (c) 2007, 2017 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.unittest.internal.junitXmlReport; + +interface IXMLTags { + + String NODE_TESTRUN = "testrun"; //$NON-NLS-1$ + String NODE_TESTSUITES = "testsuites"; //$NON-NLS-1$ + String NODE_TESTSUITE = "testsuite"; //$NON-NLS-1$ + String NODE_PROPERTIES = "properties"; //$NON-NLS-1$ + String NODE_PROPERTY = "property"; //$NON-NLS-1$ + String NODE_TESTCASE = "testcase"; //$NON-NLS-1$ + String NODE_ERROR = "error"; //$NON-NLS-1$ + String NODE_FAILURE = "failure"; //$NON-NLS-1$ + String NODE_EXPECTED = "expected"; //$NON-NLS-1$ + String NODE_ACTUAL = "actual"; //$NON-NLS-1$ + String NODE_SYSTEM_OUT = "system-out"; //$NON-NLS-1$ + String NODE_SYSTEM_ERR = "system-err"; //$NON-NLS-1$ + String NODE_SKIPPED = "skipped"; //$NON-NLS-1$ + + /** + * value: String + */ + String ATTR_NAME = "name"; //$NON-NLS-1$ + /** + * value: String + */ + String ATTR_LAUNCH_CONFIG_NAME = "launch_config_name"; //$NON-NLS-1$ + /** + * value: Integer + */ + String ATTR_TESTS = "tests"; //$NON-NLS-1$ + /** + * value: Integer + */ + String ATTR_STARTED = "started"; //$NON-NLS-1$ + /** + * value: Integer + */ + String ATTR_FAILURES = "failures"; //$NON-NLS-1$ + /** + * value: Integer + */ + String ATTR_ERRORS = "errors"; //$NON-NLS-1$ + /** + * value: Boolean + */ + String ATTR_IGNORED = "ignored"; //$NON-NLS-1$ + /** + * value: String + */ + String ATTR_PACKAGE = "package"; //$NON-NLS-1$ + /** + * value: String + */ + String ATTR_ID = "id"; //$NON-NLS-1$ + /** + * value: String + */ + String ATTR_CLASSNAME = "classname"; //$NON-NLS-1$ + /** + * value: Boolean + */ + String ATTR_INCOMPLETE = "incomplete"; //$NON-NLS-1$ + /** + * value: Duration.toString() + */ + String ATTR_START_TIME = "startTime"; //$NON-NLS-1$ + /** + * value: Double (duration in seconds) + */ + String ATTR_DURATION = "time"; //$NON-NLS-1$ + /** + * value: String + */ + String ATTR_MESSAGE = "message"; //$NON-NLS-1$ + /** + * value: String + */ + String ATTR_DISPLAY_NAME = "displayname"; //$NON-NLS-1$ + /** + * value: Boolean + */ + String ATTR_DYNAMIC_TEST = "dynamicTest"; //$NON-NLS-1$ + /** + * value: String + */ + String ATTR_DATA = "data"; //$NON-NLS-1$ + + /** + * value: String + */ + String ATTR_INCLUDE_TAGS = "include_tags"; //$NON-NLS-1$ + /** + * value: String + */ + String ATTR_EXCLUDE_TAGS = "exclude_tags"; //$NON-NLS-1$ + +// public static final String ATTR_TYPE= "type"; //$NON-NLS-1$ +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/TestRunHandler.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/TestRunHandler.java new file mode 100644 index 000000000..29a49c38f --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/TestRunHandler.java @@ -0,0 +1,365 @@ +/******************************************************************************* + * Copyright (c) 2007, 2017 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.unittest.internal.junitXmlReport; + +import java.time.Duration; +import java.time.Instant; +import java.util.Stack; + +import org.xml.sax.Attributes; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.DefaultHandler; + +import org.eclipse.osgi.util.NLS; +import org.eclipse.unittest.internal.UnitTestPlugin; +import org.eclipse.unittest.internal.model.ModelMessages; +import org.eclipse.unittest.internal.model.TestCaseElement; +import org.eclipse.unittest.internal.model.TestElement; +import org.eclipse.unittest.internal.model.TestRunSession; +import org.eclipse.unittest.internal.model.TestSuiteElement; +import org.eclipse.unittest.model.ITestElement; +import org.eclipse.unittest.model.ITestElement.FailureTrace; +import org.eclipse.unittest.model.ITestElement.Result; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; + +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunchConfiguration; + +public class TestRunHandler extends DefaultHandler { + + /* + * TODO: validate (currently assumes correct XML) + */ + + private int fId; + + private TestRunSession fTestRunSession; + private TestSuiteElement fTestSuite; + private TestCaseElement fTestCase; + private Stack<Boolean> fNotRun = new Stack<>(); + + private StringBuilder fFailureBuffer; + private boolean fInExpected; + private boolean fInActual; + private StringBuilder fExpectedBuffer; + private StringBuilder fActualBuffer; + + private Locator fLocator; + + private Result fStatus; + + private final IProgressMonitor fMonitor; + private int fLastReportedLine; + + /** + * Constructs a default {@link TestRunHandler} object instance + */ + public TestRunHandler() { + fMonitor = new NullProgressMonitor(); + } + + /** + * Constructs a {@link TestRunHandler} object instance + * + * @param monitor a progress monitor + */ + public TestRunHandler(IProgressMonitor monitor) { + fMonitor = monitor != null ? monitor : new NullProgressMonitor(); + } + + @Override + public void setDocumentLocator(Locator locator) { + fLocator = locator; + } + + @Override + public void startDocument() throws SAXException { + // Nothing to do + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if (fMonitor.isCanceled()) + throw new OperationCanceledException(); + + if (fLocator != null) { + int line = fLocator.getLineNumber(); + if (line - 20 >= fLastReportedLine) { + line -= line % 20; + fLastReportedLine = line; + fMonitor.subTask(NLS.bind(ModelMessages.TestRunHandler_lines_read, Integer.valueOf(line))); + } + } + + if (Thread.interrupted()) + throw new OperationCanceledException(); + + switch (qName) { + case IXMLTags.NODE_TESTRUN: + if (fTestRunSession == null) { + String name = attributes.getValue(IXMLTags.ATTR_NAME); + String launchConfigName = attributes.getValue(IXMLTags.ATTR_LAUNCH_CONFIG_NAME); + ILaunchConfiguration launchConfiguration = null; + if (launchConfigName != null) { + try { + for (ILaunchConfiguration config : DebugPlugin.getDefault().getLaunchManager() + .getLaunchConfigurations()) { + if (config.getName().equals(launchConfigName)) { + launchConfiguration = config; + } + } + } catch (CoreException e) { + UnitTestPlugin.log(e); + } + } + fTestRunSession = new TestRunSession(name, Instant.parse(attributes.getValue(IXMLTags.ATTR_START_TIME)), + launchConfiguration); + readDuration(fTestRunSession, attributes); + // TODO: read counts? + } else { + fTestRunSession.reset(); + } + fTestSuite = fTestRunSession; + break; + case IXMLTags.NODE_TESTSUITES: + break; + case IXMLTags.NODE_TESTSUITE: { + String name = attributes.getValue(IXMLTags.ATTR_NAME); + String pack = attributes.getValue(IXMLTags.ATTR_PACKAGE); + String suiteName = pack == null ? name : pack + "." + name; //$NON-NLS-1$ + String displayName = attributes.getValue(IXMLTags.ATTR_DISPLAY_NAME); + String data = attributes.getValue(IXMLTags.ATTR_DATA); + if (data != null && data.isBlank()) { + data = null; + } + fTestSuite = (TestSuiteElement) fTestRunSession.createTestElement(fTestSuite, getNextId(), suiteName, true, + null, false, displayName, data); + readDuration(fTestSuite, attributes); + fNotRun.push(Boolean.valueOf(attributes.getValue(IXMLTags.ATTR_INCOMPLETE))); + break; + } + // not interested + case IXMLTags.NODE_PROPERTIES: + case IXMLTags.NODE_PROPERTY: + break; + case IXMLTags.NODE_TESTCASE: { + String name = attributes.getValue(IXMLTags.ATTR_NAME); + String classname = attributes.getValue(IXMLTags.ATTR_CLASSNAME); + String testName = name + '(' + classname + ')'; + boolean isDynamicTest = Boolean.valueOf(attributes.getValue(IXMLTags.ATTR_DYNAMIC_TEST)).booleanValue(); + String displayName = attributes.getValue(IXMLTags.ATTR_DISPLAY_NAME); + String data = attributes.getValue(IXMLTags.ATTR_DATA); + if (data != null && data.isBlank()) { + data = null; + } + fTestCase = (TestCaseElement) fTestRunSession.createTestElement(fTestSuite, getNextId(), testName, false, 1, + isDynamicTest, displayName, data); + fNotRun.push(Boolean.valueOf(attributes.getValue(IXMLTags.ATTR_INCOMPLETE))); + fTestCase.setIgnored(Boolean.parseBoolean(attributes.getValue(IXMLTags.ATTR_IGNORED))); + readDuration(fTestCase, attributes); + break; + } + case IXMLTags.NODE_ERROR: + // TODO: multiple failures: https://bugs.eclipse.org/bugs/show_bug.cgi?id=125296 + fStatus = Result.ERROR; + fFailureBuffer = new StringBuilder(); + break; + case IXMLTags.NODE_FAILURE: + // TODO: multiple failures: https://bugs.eclipse.org/bugs/show_bug.cgi?id=125296 + fStatus = Result.FAILURE; + fFailureBuffer = new StringBuilder(); + break; + case IXMLTags.NODE_EXPECTED: + fInExpected = true; + fExpectedBuffer = new StringBuilder(); + break; + case IXMLTags.NODE_ACTUAL: + fInActual = true; + fActualBuffer = new StringBuilder(); + break; + // not interested + case IXMLTags.NODE_SYSTEM_OUT: + case IXMLTags.NODE_SYSTEM_ERR: + break; + case IXMLTags.NODE_SKIPPED: + // before Ant 1.9.0: not an Ant JUnit tag, see + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=276068 + // later: child of <suite> or <test>, see + // https://issues.apache.org/bugzilla/show_bug.cgi?id=43969 + fStatus = Result.OK; + fFailureBuffer = new StringBuilder(); + String message = attributes.getValue(IXMLTags.ATTR_MESSAGE); + if (message != null) { + fFailureBuffer.append(message).append('\n'); + } + break; + default: + throw new SAXParseException("unknown node '" + qName + "'", fLocator); //$NON-NLS-1$//$NON-NLS-2$ + } + } + + private void readDuration(ITestElement testElement, Attributes attributes) { + if (testElement instanceof TestElement) { + TestElement element = (TestElement) testElement; + String timeString = attributes.getValue(IXMLTags.ATTR_DURATION); + if (timeString != null) { + try { + double seconds = Double.parseDouble(timeString); + long millis = (long) (seconds * 1000); + element.setDuration(Duration.ofMillis(millis)); + } catch (NumberFormatException e) { + // Ignore + } + } + } + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + if (fInExpected) { + fExpectedBuffer.append(ch, start, length); + + } else if (fInActual) { + fActualBuffer.append(ch, start, length); + + } else if (fFailureBuffer != null) { + fFailureBuffer.append(ch, start, length); + } + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + switch (qName) { + // OK + case IXMLTags.NODE_TESTRUN: + break; + // OK + case IXMLTags.NODE_TESTSUITES: + break; + case IXMLTags.NODE_TESTSUITE: + handleTestElementEnd(fTestSuite); + fTestSuite = fTestSuite.getParent(); + // TODO: end suite: compare counters? + break; + // OK + case IXMLTags.NODE_PROPERTIES: + case IXMLTags.NODE_PROPERTY: + break; + case IXMLTags.NODE_TESTCASE: + handleTestElementEnd(fTestCase); + fTestCase = null; + break; + case IXMLTags.NODE_FAILURE: + case IXMLTags.NODE_ERROR: { + ITestElement testElement = fTestCase; + if (testElement == null) + testElement = fTestSuite; + handleFailure(testElement); + break; + } + case IXMLTags.NODE_EXPECTED: + fInExpected = false; + if (fFailureBuffer != null) { + // skip whitespace from before <expected> and <actual> nodes + fFailureBuffer.setLength(0); + } + break; + case IXMLTags.NODE_ACTUAL: + fInActual = false; + if (fFailureBuffer != null) { + // skip whitespace from before <expected> and <actual> nodes + fFailureBuffer.setLength(0); + } + break; + // OK + case IXMLTags.NODE_SYSTEM_OUT: + case IXMLTags.NODE_SYSTEM_ERR: + break; + case IXMLTags.NODE_SKIPPED: { + TestElement testElement = fTestCase; + if (testElement == null) + testElement = fTestSuite; + if (fFailureBuffer != null && fFailureBuffer.length() > 0) { + handleFailure(testElement); + testElement.setAssumptionFailed(true); + } else if (fTestCase != null) { + fTestCase.setIgnored(true); + } else { // not expected + testElement.setAssumptionFailed(true); + } + break; + } + default: + handleUnknownNode(qName); + break; + } + } + + private void handleTestElementEnd(ITestElement testElement) { + boolean completed = !fNotRun.pop().booleanValue(); + fTestRunSession.registerTestEnded((TestElement) testElement, completed); + } + + private void handleFailure(ITestElement testElement) { + if (fFailureBuffer != null) { + fTestRunSession.registerTestFailureStatus((TestElement) testElement, fStatus, + new FailureTrace(fFailureBuffer.toString(), toString(fExpectedBuffer), toString(fActualBuffer))); + fFailureBuffer = null; + fExpectedBuffer = null; + fActualBuffer = null; + fStatus = null; + } + } + + private String toString(StringBuilder buffer) { + return buffer != null ? buffer.toString() : null; + } + + private void handleUnknownNode(String qName) throws SAXException { + // TODO: just log if debug option is enabled? + String msg = "unknown node '" + qName + "'"; //$NON-NLS-1$//$NON-NLS-2$ + if (fLocator != null) { + msg += " at line " + fLocator.getLineNumber() + ", column " + fLocator.getColumnNumber(); //$NON-NLS-1$//$NON-NLS-2$ + } + throw new SAXException(msg); + } + + @Override + public void error(SAXParseException e) throws SAXException { + throw e; + } + + @Override + public void warning(SAXParseException e) throws SAXException { + throw e; + } + + private String getNextId() { + return Integer.toString(fId++); + } + + /** + * @return the parsed test run session, or <code>null</code> + */ + public TestRunSession getTestRunSession() { + return fTestRunSession; + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/TestRunSessionSerializer.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/TestRunSessionSerializer.java new file mode 100644 index 000000000..4147bfb80 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/junitXmlReport/TestRunSessionSerializer.java @@ -0,0 +1,316 @@ +/******************************************************************************* + * Copyright (c) 2007, 2017 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.unittest.internal.junitXmlReport; + +import java.io.IOException; + +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.DTDHandler; +import org.xml.sax.EntityResolver; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.AttributesImpl; + +import org.eclipse.unittest.internal.model.ProgressState; +import org.eclipse.unittest.internal.model.TestCaseElement; +import org.eclipse.unittest.internal.model.TestElement; +import org.eclipse.unittest.internal.model.TestRunSession; +import org.eclipse.unittest.internal.model.TestSuiteElement; +import org.eclipse.unittest.model.ITestElement; +import org.eclipse.unittest.model.ITestElement.FailureTrace; +import org.eclipse.unittest.model.ITestElement.Result; + +import org.eclipse.core.runtime.Assert; + +import org.eclipse.debug.core.ILaunchConfiguration; + +/** + * A {@link TestRunSession} object serializer + */ +public class TestRunSessionSerializer implements XMLReader { + + private static final String EMPTY = ""; //$NON-NLS-1$ + private static final String CDATA = "CDATA"; //$NON-NLS-1$ + private static final Attributes NO_ATTS = new AttributesImpl(); + + private final TestRunSession fTestRunSession; + private ContentHandler fHandler; + private ErrorHandler fErrorHandler; + + /** + * @param testRunSession the test run session to serialize + */ + public TestRunSessionSerializer(TestRunSession testRunSession) { + Assert.isNotNull(testRunSession); + fTestRunSession = testRunSession; + } + + @Override + public void parse(InputSource input) throws IOException, SAXException { + if (fHandler == null) + throw new SAXException("ContentHandler missing"); //$NON-NLS-1$ + + fHandler.startDocument(); + handleTestRun(); + fHandler.endDocument(); + } + + private void handleTestRun() throws SAXException { + AttributesImpl atts = new AttributesImpl(); + addCDATA(atts, IXMLTags.ATTR_NAME, fTestRunSession.getTestRunName()); + + ILaunchConfiguration launchConfig = fTestRunSession.getLaunch() != null + ? fTestRunSession.getLaunch().getLaunchConfiguration() + : null; + if (launchConfig != null) { + addCDATA(atts, IXMLTags.ATTR_LAUNCH_CONFIG_NAME, launchConfig.getName()); + } + + Integer total = fTestRunSession.getFinalTestCaseCount(); + if (total != null) { + addCDATA(atts, IXMLTags.ATTR_TESTS, total.intValue()); + } + addCDATA(atts, IXMLTags.ATTR_STARTED, fTestRunSession.countStartedTestCases()); + addCDATA(atts, IXMLTags.ATTR_FAILURES, fTestRunSession.getCurrentFailureCount()); + addCDATA(atts, IXMLTags.ATTR_ERRORS, fTestRunSession.getCurrentErrorCount()); + addCDATA(atts, IXMLTags.ATTR_IGNORED, fTestRunSession.getCurrentIgnoredCount()); + addCDATA(atts, IXMLTags.ATTR_START_TIME, fTestRunSession.getStartTime().toString()); + if (fTestRunSession.getDuration() != null) { + addCDATA(atts, IXMLTags.ATTR_DURATION, fTestRunSession.getDuration().toString()); + } + startElement(IXMLTags.NODE_TESTRUN, atts); + + for (ITestElement topSuite : fTestRunSession.getChildren()) { + handleTestElement(topSuite); + } + + endElement(IXMLTags.NODE_TESTRUN); + } + + private void handleTestElement(ITestElement testElement) throws SAXException { + if (testElement instanceof TestSuiteElement) { + TestSuiteElement testSuiteElement = (TestSuiteElement) testElement; + + AttributesImpl atts = new AttributesImpl(); + // Need to store the full #getTestName instead of only the #getSuiteTypeName for + // test factory methods + addCDATA(atts, IXMLTags.ATTR_NAME, testSuiteElement.getTestName()); + if (testSuiteElement.getDuration() != null) { + addCDATA(atts, IXMLTags.ATTR_DURATION, + Double.toString(testSuiteElement.getDuration().toMillis() / 1000.)); + } + if (testSuiteElement.getProgressState() != ProgressState.COMPLETED + || testSuiteElement.getTestResult(false) != Result.UNDEFINED) + addCDATA(atts, IXMLTags.ATTR_INCOMPLETE, Boolean.TRUE.toString()); + if (testSuiteElement.getDisplayName() != null) { + addCDATA(atts, IXMLTags.ATTR_DISPLAY_NAME, testSuiteElement.getDisplayName()); + } + if (testSuiteElement.getData() != null) { + addCDATA(atts, IXMLTags.ATTR_DATA, testSuiteElement.getData()); + } + startElement(IXMLTags.NODE_TESTSUITE, atts); + addFailure(testSuiteElement); + + for (ITestElement child : testSuiteElement.getChildren()) { + handleTestElement(child); + } + endElement(IXMLTags.NODE_TESTSUITE); + + } else if (testElement instanceof TestCaseElement) { + TestCaseElement testCaseElement = (TestCaseElement) testElement; + + AttributesImpl atts = new AttributesImpl(); + if (testCaseElement.getDuration() != null) { + addCDATA(atts, IXMLTags.ATTR_DURATION, testCaseElement.getDuration().toString()); + } + if (testCaseElement.getProgressState() != ProgressState.COMPLETED) + addCDATA(atts, IXMLTags.ATTR_INCOMPLETE, Boolean.TRUE.toString()); + if (testCaseElement.isIgnored()) + addCDATA(atts, IXMLTags.ATTR_IGNORED, Boolean.TRUE.toString()); + if (testCaseElement.isDynamicTest()) { + addCDATA(atts, IXMLTags.ATTR_DYNAMIC_TEST, Boolean.TRUE.toString()); + } + if (testCaseElement.getDisplayName() != null) { + addCDATA(atts, IXMLTags.ATTR_DISPLAY_NAME, testCaseElement.getDisplayName()); + } + if (testCaseElement.getData() != null) { + addCDATA(atts, IXMLTags.ATTR_DATA, testCaseElement.getData()); + } + startElement(IXMLTags.NODE_TESTCASE, atts); + addFailure(testCaseElement); + + endElement(IXMLTags.NODE_TESTCASE); + + } else { + throw new IllegalStateException(String.valueOf(testElement)); + } + + } + + private void addFailure(TestElement testElement) throws SAXException { + FailureTrace failureTrace = testElement.getFailureTrace(); + + if (testElement.isAssumptionFailure()) { + startElement(IXMLTags.NODE_SKIPPED, NO_ATTS); + if (failureTrace != null) { + addCharacters(failureTrace.getTrace()); + } + endElement(IXMLTags.NODE_SKIPPED); + + } else if (failureTrace != null) { + AttributesImpl failureAtts = new AttributesImpl(); +// addCDATA(failureAtts, IXMLTags.ATTR_MESSAGE, xx); +// addCDATA(failureAtts, IXMLTags.ATTR_TYPE, xx); + String failureKind = testElement.getTestResult(false) == Result.ERROR ? IXMLTags.NODE_ERROR + : IXMLTags.NODE_FAILURE; + startElement(failureKind, failureAtts); + String expected = failureTrace.getExpected(); + String actual = failureTrace.getActual(); + if (expected != null) { + startElement(IXMLTags.NODE_EXPECTED, NO_ATTS); + addCharacters(expected); + endElement(IXMLTags.NODE_EXPECTED); + } + if (actual != null) { + startElement(IXMLTags.NODE_ACTUAL, NO_ATTS); + addCharacters(actual); + endElement(IXMLTags.NODE_ACTUAL); + } + String trace = failureTrace.getTrace(); + addCharacters(trace); + endElement(failureKind); + } + } + + private void startElement(String name, Attributes atts) throws SAXException { + fHandler.startElement(EMPTY, name, name, atts); + } + + private void endElement(String name) throws SAXException { + fHandler.endElement(EMPTY, name, name); + } + + private static void addCDATA(AttributesImpl atts, String name, int value) { + addCDATA(atts, name, Integer.toString(value)); + } + + private static void addCDATA(AttributesImpl atts, String name, String value) { + atts.addAttribute(EMPTY, EMPTY, name, CDATA, value); + } + + private void addCharacters(String string) throws SAXException { + string = escapeNonUnicodeChars(string); + fHandler.characters(string.toCharArray(), 0, string.length()); + } + + /** + * Replaces all non-Unicode characters in the given string. + * + * @param string a string + * @return string with Java-escapes + */ + private static String escapeNonUnicodeChars(String string) { + StringBuilder buf = null; + for (int i = 0; i < string.length(); i++) { + char ch = string.charAt(i); + if (!(ch == 9 || ch == 10 || ch == 13 || ch >= 32)) { + if (buf == null) { + buf = new StringBuilder(string.substring(0, i)); + } + buf.append("\\u"); //$NON-NLS-1$ + String hex = Integer.toHexString(ch); + for (int j = hex.length(); j < 4; j++) + buf.append('0'); + buf.append(hex); + } else if (buf != null) { + buf.append(ch); + } + } + if (buf != null) { + return buf.toString(); + } + return string; + } + + @Override + public void setContentHandler(ContentHandler handler) { + this.fHandler = handler; + } + + @Override + public ContentHandler getContentHandler() { + return fHandler; + } + + @Override + public void setErrorHandler(ErrorHandler handler) { + fErrorHandler = handler; + } + + @Override + public ErrorHandler getErrorHandler() { + return fErrorHandler; + } + + // ignored: + + @Override + public void parse(String systemId) throws IOException, SAXException { + // Nothing to do + } + + @Override + public void setDTDHandler(DTDHandler handler) { + // Nothing to do + } + + @Override + public DTDHandler getDTDHandler() { + return null; + } + + @Override + public void setEntityResolver(EntityResolver resolver) { + // Nothing to do + } + + @Override + public EntityResolver getEntityResolver() { + return null; + } + + @Override + public void setProperty(java.lang.String name, java.lang.Object value) { + // Nothing to do + } + + @Override + public Object getProperty(java.lang.String name) { + return null; + } + + @Override + public void setFeature(java.lang.String name, boolean value) { + // Nothing to do + } + + @Override + public boolean getFeature(java.lang.String name) { + return false; + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestListenerRegistry.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestListenerRegistry.java new file mode 100644 index 000000000..a6ed3eaf9 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestListenerRegistry.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2006, 2018 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.launcher; + +import org.eclipse.unittest.internal.ui.UITestRunListener; + +import org.eclipse.core.runtime.ListenerList; + +/** + * Test View Support registry + */ +public class TestListenerRegistry { + + /** + * Returns a {@link TestListenerRegistry} object instance + * + * @return a {@link TestListenerRegistry} object + */ + public static TestListenerRegistry getDefault() { + if (fgRegistry != null) + return fgRegistry; + + fgRegistry = new TestListenerRegistry(); + return fgRegistry; + } + + private static TestListenerRegistry fgRegistry; + + /** + * List storing the registered test run listeners + */ + private ListenerList<TestRunListener> fUnitTestRunListeners = new ListenerList<>(); + + private TestListenerRegistry() { + } + + /** + * @return a <code>ListenerList</code> of all <code>TestRunListener</code>s + */ + public ListenerList<TestRunListener> getUnitTestRunListeners() { + loadUnitTestRunListeners(); + return fUnitTestRunListeners; + } + + /** + * Initializes TestRun Listener extensions + */ + private synchronized void loadUnitTestRunListeners() { + if (!fUnitTestRunListeners.isEmpty()) { + return; + } + fUnitTestRunListeners.add(new UITestRunListener()); + } + +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestRunListener.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestRunListener.java new file mode 100644 index 000000000..0150734cd --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestRunListener.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.launcher; + +import org.eclipse.unittest.model.ITestCaseElement; +import org.eclipse.unittest.model.ITestRunSession; + +/** + * A test run listener default implementation. + * + * <p> + * A test run starts with the call to {@link #sessionLaunched(ITestRunSession)} + * and {@link #sessionStarted(ITestRunSession)}, followed by calls to + * {@link #testCaseStarted(ITestCaseElement)} and + * {@link #testCaseFinished(ITestCaseElement)} for all test cases contained in + * the tree. + * </p> + * <p> + * A test run session is ended with the call to + * {@link #sessionFinished(ITestRunSession)}. After that call, no references + * must be kept to the session or any of the test cases or suites. + * </p> + */ +public class TestRunListener { + + /** + * A test run session has been launched. The test tree is not available yet. + * <p> + * Important: The implementor of this method must not keep a reference to the + * session element after {@link #sessionFinished(ITestRunSession)} has finished. + * </p> + * + * @param session the session that has just been launched + */ + public void sessionLaunched(ITestRunSession session) { + // does nothing + } + + /** + * A test run session has started. The test tree can be accessed through the + * session element. + * <p> + * Important: The implementor of this method must not keep a reference to the + * session element after {@link #sessionFinished(ITestRunSession)} has finished. + * </p> + * + * @param session the session that has just started. + */ + public void sessionStarted(ITestRunSession session) { + // does nothing + } + + /** + * A test run session has finished. The test tree can be accessed through the + * session element. + * + * <p> + * Important: The implementor of this method must not keep the session element + * when the method is finished. + * </p> + * + * @param session the test + */ + public void sessionFinished(ITestRunSession session) { + // does nothing + } + + /** + * A test case has started. The result can be accessed from the element. + * <p> + * Important: The implementor of this method must not keep a reference to the + * test case element after {@link #sessionFinished(ITestRunSession)} has + * finished. + * </p> + * + * @param testCaseElement the test that has started to run + */ + public void testCaseStarted(ITestCaseElement testCaseElement) { + // does nothing + } + + /** + * A test case has ended. The result can be accessed from the element. + * <p> + * Important: The implementor of this method must not keep a reference to the + * test case element after {@link #sessionFinished(ITestRunSession)} has + * finished. + * </p> + * + * @param testCaseElement the test that has finished running + */ + public void testCaseFinished(ITestCaseElement testCaseElement) { + // does nothing + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestViewSupportExtension.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestViewSupportExtension.java new file mode 100644 index 000000000..7e61cfba3 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestViewSupportExtension.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2006, 2020 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + * Victor Rubezhny, Mickael Istria (Red Hat Inc.) - Adaptation from JUnit + *******************************************************************************/ + +package org.eclipse.unittest.internal.launcher; + +import org.eclipse.unittest.internal.UnitTestPlugin; +import org.eclipse.unittest.internal.model.ModelMessages; +import org.eclipse.unittest.ui.ITestViewSupport; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * A {@link TestViewSupportExtension} implementation + */ +public class TestViewSupportExtension { + + private static final String ID = "id"; //$NON-NLS-1$ + private static final String CLASS = "class"; //$NON-NLS-1$ + + private final IConfigurationElement fElement; + + /** + * Constructs a {@link TestViewSupportExtension} object + * + * @param element an {@link IConfigurationElement} object + */ + public TestViewSupportExtension(IConfigurationElement element) { + fElement = element; + } + + /** + * Returns an identifier of the test view support extension + * + * @return A test view support identifier + */ + public String getId() { + return getAttribute(ID); + } + + /** + * Returns an attribute by a given name + * + * @param attributeName an attribute name + * + * @return a value of an attribute + */ + protected String getAttribute(String attributeName) { + return fElement.getAttribute(attributeName); + } + + /** + * Instantiates an {@link ITestViewSupport} object + * + * @return an instance of {@link ITestViewSupport} for the given extension. + * <code>null</code> if class couldn't be loaded. + * @throws CoreException In case of error during the Test View Support + * instantiation + */ + ITestViewSupport instantiateTestViewSupport() throws CoreException { + try { + return (ITestViewSupport) fElement.createExecutableExtension(CLASS); + } catch (Exception e) { + throw new CoreException(new Status(IStatus.ERROR, UnitTestPlugin.PLUGIN_ID, + ModelMessages.UnitTestModel_could_not_instantiate_support, e)); + } + } + + @Override + public String toString() { + return getId(); + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestViewSupportRegistry.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestViewSupportRegistry.java new file mode 100644 index 000000000..c8d2f8bd0 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/TestViewSupportRegistry.java @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (c) 2006, 2018 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.launcher; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.eclipse.unittest.internal.UnitTestPlugin; +import org.eclipse.unittest.ui.ITestViewSupport; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.Platform; + +import org.eclipse.debug.core.ILaunchConfiguration; + +/** + * Test View Support registry + */ +public class TestViewSupportRegistry { + /** + * An identifier of Test View Support extension point + */ + public static final String ID_EXTENSION_POINT_TEST_VIEW_SUPPORTS = UnitTestPlugin.PLUGIN_ID + "." //$NON-NLS-1$ + + "unittestViewSupport"; //$NON-NLS-1$ + + /** + * Returns an instance of {@link TestViewSupportRegistry} object + * + * @return a {@link TestViewSupportRegistry} object + */ + public static TestViewSupportRegistry getDefault() { + if (fgRegistry != null) + return fgRegistry; + + fgRegistry = new TestViewSupportRegistry( + Platform.getExtensionRegistry().getExtensionPoint(ID_EXTENSION_POINT_TEST_VIEW_SUPPORTS)); + return fgRegistry; + } + + private static TestViewSupportRegistry fgRegistry; + + private final IExtensionPoint fPoint; + private List<TestViewSupportExtension> fTestViewSupportExtensions; + + private TestViewSupportRegistry(IExtensionPoint point) { + fPoint = point; + } + + /** + * Returns all the registered View Support extensions + * + * @return a {@link List} containing all the registered View Support extensions + */ + private List<TestViewSupportExtension> getAllTestViewSupportExtensions() { + loadTestViewSupportExtensions(); + return fTestViewSupportExtensions; + } + + /** + * Returns all the registered View Support extensions that suit the specified + * filter + * + * @param filter a registry identifier filter + * @return an {@link ArrayList} containing the registry kings filtered by + * identifier + */ + public List<TestViewSupportExtension> getTestViewSupportExtensions(final String filter) { + List<TestViewSupportExtension> all = getAllTestViewSupportExtensions(); + if (all == null) + return Collections.emptyList(); + + return all.stream().filter(p -> p.getId().startsWith(filter)).collect(Collectors.toList()); + } + + private void loadTestViewSupportExtensions() { + if (fTestViewSupportExtensions != null) + return; + + List<TestViewSupportExtension> items = getConfigurationElements().stream().map(TestViewSupportExtension::new) + .collect(Collectors.toList()); + + fTestViewSupportExtensions = items; + } + + /* + * Returns an {@link Optional <ITestViewSupport>} object instance by its + * identifier + * + * @param id an identifier, can be <code>null</code> + * + * @return an {@link Optional <ITestViewSupport>} object instance + */ + private Optional<ITestViewSupport> findTestViewSupport(String id) { + return getAllTestViewSupportExtensions().stream().filter(ext -> ext.getId().equals(id)).findFirst().map(t -> { + try { + return t.instantiateTestViewSupport(); + } catch (CoreException e) { + UnitTestPlugin.log(e); + return null; + } + }); + } + + /** + * Returns {@link ITestViewSupport} instance from the given launch configuration + * + * @param launchConfiguration a launch configuration + * @return an {@link Optional <ITestViewSupport>} object instance + */ + public static Optional<ITestViewSupport> newTestRunnerViewSupport(ILaunchConfiguration launchConfiguration) { + try { + return getDefault().findTestViewSupport(launchConfiguration + .getAttribute(UnitTestLaunchConfigurationConstants.ATTR_UNIT_TEST_VIEW_SUPPORT, (String) null)); + } catch (CoreException e) { + UnitTestPlugin.log(e); + return Optional.empty(); + } + } + + private List<IConfigurationElement> getConfigurationElements() { + List<IConfigurationElement> items = new ArrayList<>(); + for (IExtension extension : fPoint.getExtensions()) { + Collections.addAll(items, extension.getConfigurationElements()); + } + return items; + } + +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/UnitTestLaunchConfigurationConstants.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/UnitTestLaunchConfigurationConstants.java new file mode 100644 index 000000000..6431887d5 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/launcher/UnitTestLaunchConfigurationConstants.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2000, 2018 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.launcher; + +import org.eclipse.unittest.internal.UnitTestPlugin; +import org.eclipse.unittest.ui.ITestViewSupport; + +import org.eclipse.debug.core.ILaunchConfiguration; + +/** + * Attribute keys used by the UnitTest LaunchConfiguration. Note that these + * constants are not API and might change in the future. + */ +public class UnitTestLaunchConfigurationConstants { + + /** + * An identifier of a property to be set on a {@link ILaunchConfiguration} to + * identify an {@link ITestViewSupport} implementation. + */ + public static final String ATTR_UNIT_TEST_VIEW_SUPPORT = UnitTestPlugin.PLUGIN_ID + ".TEST_VIEW_SUPPPORT"; //$NON-NLS-1$ + + private UnitTestLaunchConfigurationConstants() { + // No instance allowed + } + +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestRunSessionListener.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestRunSessionListener.java new file mode 100644 index 000000000..d647ec149 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestRunSessionListener.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.unittest.internal.model; + +import org.eclipse.unittest.model.ITestRunSession; + +/** + * An interface to listen to the events from the on added/removed + * {@link ITestRunSession} instances. + */ +public interface ITestRunSessionListener { + /** + * Notifies on an added {@link ITestRunSession} instance. + * + * @param testRunSession the new session + */ + void sessionAdded(ITestRunSession testRunSession); + + /** + * Notifies on a removed {@link ITestRunSession} instance. + * + * @param testRunSession the new session + */ + void sessionRemoved(ITestRunSession testRunSession); +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestRunSessionReport.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestRunSessionReport.java new file mode 100644 index 000000000..acb0ac094 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestRunSessionReport.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2020 Red Hat Inc. and others + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.unittest.internal.model; + +/** + * An {@link ITestRunSessionReport} object + */ +public interface ITestRunSessionReport { + + /** + * Returns the name of the test run. The name is the name of the launch + * configuration use to run this test. + * + * @return returns the test run name + */ + String getTestRunName(); + + /** + * Indicates if the test run session is starting + * + * @return <code>true</code> in case of the test session is starting, otherwise + * returns <code>false</code> + */ + boolean isStarting(); + + /** + * Indicates if the test run session is running + * + * @return <code>true</code> in case of the test session is running, otherwise + * returns <code>false</code> + */ + boolean isRunning(); + + /** + * Indicates if the test run session has been stopped or terminated + * + * @return <code>true</code> if the session has been stopped or terminated, + * otherwise returns <code>false</code> + */ + boolean isStopped(); + +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestSessionListener.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestSessionListener.java new file mode 100644 index 000000000..ed35d4ff4 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ITestSessionListener.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.unittest.internal.model; + +import java.time.Duration; + +import org.eclipse.unittest.model.ITestCaseElement; +import org.eclipse.unittest.model.ITestElement; +import org.eclipse.unittest.model.ITestElement.FailureTrace; +import org.eclipse.unittest.model.ITestElement.Result; + +/** + * A listener interface for observing the execution of a test session (initial + * run and reruns). + */ +public interface ITestSessionListener { + /** + * A test run has started. + */ + void sessionStarted(); + + /** + * A test run has ended. + * + * @param duration the total elapsed time of the test run + */ + void sessionCompleted(Duration duration); + + /** + * A test run has been stopped prematurely. + * + * @param duration the time elapsed before the test run was stopped + */ + void sessionAborted(Duration duration); + + /** + * A test has been added to the plan. + * + * @param testElement the test + */ + void testAdded(ITestElement testElement); + + /** + * All test have been added and running begins + */ + void runningBegins(); + + /** + * An individual test has started. + * + * @param testCaseElement the test + */ + void testStarted(ITestCaseElement testCaseElement); + + /** + * An individual test has ended. + * + * @param testCaseElement the test + */ + void testEnded(ITestCaseElement testCaseElement); + + /** + * An individual test has failed with a stack trace. + * + * @param testElement the test + * @param status the outcome of the test; one of + * {@link org.eclipse.unittest.model.ITestElement.Result#ERROR} + * or + * {@link org.eclipse.unittest.model.ITestElement.Result#FAILURE} + * @param trace the stack trace + */ + void testFailed(ITestElement testElement, Result status, FailureTrace trace); + +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ModelMessages.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ModelMessages.java new file mode 100644 index 000000000..f2ddf946d --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ModelMessages.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.unittest.internal.model; + +import org.eclipse.osgi.util.NLS; + +/** + * Unit Test Model messages + */ +public class ModelMessages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.unittest.internal.model.ModelMessages"; //$NON-NLS-1$ + + public static String UnitTestModel_could_not_instantiate_support; + public static String UnitTestModel_could_not_import; + public static String UnitTestModel_could_not_export; + public static String UnitTestModel_could_not_read; + public static String UnitTestModel_could_not_write; + public static String UnitTestModel_importing_from_url; + public static String TestRunHandler_lines_read; + + public static String TestingSession_finished_status; + public static String TestingSession_name_format; + public static String TestingSession_starting_status; + public static String TestingSession_stopped_status; + + public static String TestRunSession_unrootedTests; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, ModelMessages.class); + } + + private ModelMessages() { + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ModelMessages.properties b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ModelMessages.properties new file mode 100644 index 000000000..521f0eff3 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ModelMessages.properties @@ -0,0 +1,31 @@ +############################################################################### +# Copyright (c) 2009, 2010 IBM Corporation and others. +# +# This program and the accompanying materials +# are made available under the terms of the Eclipse Public License 2.0 +# which accompanies this distribution, and is available at +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# IBM Corporation - initial API and implementation +############################################################################### + +# From JUnit Model +UnitTestModel_could_not_instantiate_support=A Test View Support instance couldn't be instantiated. +UnitTestModel_could_not_export=Test run could not be exported. +UnitTestModel_could_not_import=Test run could not be imported. +UnitTestModel_could_not_write=The test run could not be written to file ''{0}''. +UnitTestModel_could_not_read=The test run could not be imported from file ''{0}''. +UnitTestModel_importing_from_url=Importing from URL... +TestRunHandler_lines_read={0} lines read + +# From CDT Model +TestingSession_name_format={0} ({1}) +TestingSession_finished_status=Finished after {0} seconds +TestingSession_starting_status=Starting... +TestingSession_stopped_status=Testing was stopped by user + +TestRunSession_unrootedTests=Unrooted Tests + diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ProgressState.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ProgressState.java new file mode 100644 index 000000000..051d18526 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/ProgressState.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2000, 2016 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.unittest.internal.model; + +/** + * Running states of a test. + */ +public enum ProgressState { + /** state that describes that the test element has not started */ + NOT_STARTED("Not Started"), //$NON-NLS-1$ + /** state that describes that the test element is running */ + RUNNING("Running"), //$NON-NLS-1$ + /** + * state that describes that the test element has been stopped before being + * completed + */ + ABORTED("Aborted"), //$NON-NLS-1$ + /** state that describes that the test element has completed */ + COMPLETED("Completed"); //$NON-NLS-1$ + + private String fName; + + private ProgressState(String name) { + fName = name; + } + + @Override + public String toString() { + return fName; + } +}
\ No newline at end of file diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/Status.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/Status.java new file mode 100644 index 000000000..b9bd597f8 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/Status.java @@ -0,0 +1,182 @@ +/******************************************************************************* + * Copyright (c) 2000, 2020 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.model; + +import org.eclipse.unittest.model.ITestElement.Result; + +/** + * An object describing a test current status + */ +public final class Status { + public static final Status RUNNING_ERROR = new Status("RUNNING_ERROR"); //$NON-NLS-1$ + public static final Status RUNNING_FAILURE = new Status("RUNNING_FAILURE"); //$NON-NLS-1$ + public static final Status RUNNING = new Status("RUNNING"); //$NON-NLS-1$ + + public static final Status ERROR = new Status("ERROR"); //$NON-NLS-1$ + public static final Status FAILURE = new Status("FAILURE"); //$NON-NLS-1$ + public static final Status OK = new Status("OK"); //$NON-NLS-1$ + public static final Status NOT_RUN = new Status("NOT_RUN"); //$NON-NLS-1$ + + private static final Status[] OLD_CODE = { OK, ERROR, FAILURE }; + + private final String fName; + + private Status(String name) { + fName = name; + } + + @Override + public String toString() { + return fName; + } + + /* error state predicates */ + + /** + * Indicates if current test status is OK + * + * @return <code>true</code> if current status is OK, otherwise returns + * <code>false</code> + */ + public boolean isOK() { + return this == OK || this == RUNNING || this == NOT_RUN; + } + + /** + * Indicates if current test has failures + * + * @return <code>true</code> if current test has failures, otherwise return + * <code>false</code> + */ + public boolean isFailure() { + return this == FAILURE || this == RUNNING_FAILURE; + } + + /** + * Indicates if current test has errors + * + * @return <code>true</code> if current test has errors, otherwise return + * <code>false</code> + */ + public boolean isError() { + return this == ERROR || this == RUNNING_ERROR; + } + + /** + * Indicates if current test has errors and/or failures + * + * @return <code>true</code> if current test has errors and/or failures, + * otherwise return <code>false</code> + */ + public boolean isErrorOrFailure() { + return isError() || isFailure(); + } + + /* progress state predicates */ + + /** + * Indicates if current test isn't run yet + * + * @return <code>true</code> if current test isn't run yet, otherwise return + * <code>false</code> + */ + public boolean isNotRun() { + return this == NOT_RUN; + } + + /** + * Indicates if current test is running + * + * @return <code>true</code> if current test is running, otherwise return + * <code>false</code> + */ + public boolean isRunning() { + return this == RUNNING || this == RUNNING_FAILURE || this == RUNNING_ERROR; + } + + public boolean isDone() { + return this == OK || this == FAILURE || this == ERROR; + } + + /** + * Converts from an old status code to a {@link Status} constant + * + * @param oldStatus one of {@link Status}'s constants + * @return the {@link Status} constant + */ + public static Status convert(int oldStatus) { + return OLD_CODE[oldStatus]; + } + + /** + * Converts the current {@link Status} object into a + * {@link org.eclipse.unittest.model.ITestElement.Result} object + * + * @return a {@link org.eclipse.unittest.model.ITestElement.Result} object + * instance + */ + public Result convertToResult() { + if (isNotRun()) + return Result.UNDEFINED; + if (isError()) + return Result.ERROR; + if (isFailure()) + return Result.FAILURE; + if (isRunning()) { + return Result.UNDEFINED; + } + return Result.OK; + } + + /** + * Converts the current {@link Status} object into a {@link ProgressState} + * object + * + * @return a {@link ProgressState} object instance + */ + public ProgressState convertToProgressState() { + if (isRunning()) { + return ProgressState.RUNNING; + } + if (isDone()) { + return ProgressState.COMPLETED; + } + return ProgressState.NOT_STARTED; + } + + /** + * Creates a {@link Status} object from a given + * {@link org.eclipse.unittest.model.ITestElement.Result} object + * + * @param status a {@link Status} object + * @return an {@link org.eclipse.unittest.model.ITestElement.Result} object + * instance + */ + public static Status fromResult(Result status) { + switch (status) { + case ERROR: + return Status.ERROR; + case FAILURE: + return Status.FAILURE; + case OK: + return Status.OK; + case IGNORED: + return Status.OK; + case UNDEFINED: + return Status.NOT_RUN; + default: + return Status.NOT_RUN; + } + } +}
\ No newline at end of file diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestCaseElement.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestCaseElement.java new file mode 100644 index 000000000..9e0c4f075 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestCaseElement.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2000, 2017 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.unittest.internal.model; + +import org.eclipse.unittest.model.ITestCaseElement; + +import org.eclipse.core.runtime.Assert; + +/** + * A test case element. Holds all information about a test case + */ +public class TestCaseElement extends TestElement implements ITestCaseElement { + + private boolean fIgnored; + private boolean fIsDynamicTest; + + /** + * Constructs a {@link TestCaseElement} object + * + * @param parent a parent {@link TestSuiteElement} object + * @param id an identifier of the object + * @param testName a name of the test case + * @param displayName a display name for a test case + * @param isDynamicTest an indicator of a "dynamic" test case + * @param uniqueId an unique test case identifier or <code>null</code> + */ + public TestCaseElement(TestSuiteElement parent, String id, String testName, String displayName, + boolean isDynamicTest, String uniqueId) { + super(parent, id, testName, displayName, uniqueId); + Assert.isNotNull(parent); + fIsDynamicTest = isDynamicTest; + } + + @Override + public Result getTestResult(boolean includeChildren) { + return isIgnored() ? Result.IGNORED : super.getTestResult(includeChildren); + } + + public void setIgnored(boolean ignored) { + fIgnored = ignored; + } + + @Override + public boolean isIgnored() { + return fIgnored; + } + + @Override + public String toString() { + return "TestCase: " + super.toString(); //$NON-NLS-1$ + } + + @Override + public boolean isDynamicTest() { + return fIsDynamicTest; + } + + @Override + Integer getFinalTestCaseCount() { + return Integer.valueOf(1); + } + + @Override + int countStartedTestCases() { + return getProgressState() != ProgressState.NOT_STARTED || testStartedInstant != null ? 1 : 0; + } + + @Override + int getCurrentFailureCount() { + return getStatus() == Status.FAILURE ? 1 : 0; + } + + @Override + int getCurrentAssumptionFailureCount() { + return isAssumptionFailure() ? 1 : 0; + } + + @Override + int getCurrentIgnoredCount() { + return isIgnored() ? 1 : 0; + } + + @Override + int getCurrentErrorCount() { + return getStatus() == Status.ERROR ? 1 : 0; + } +}
\ No newline at end of file diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestElement.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestElement.java new file mode 100644 index 000000000..7171e12f4 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestElement.java @@ -0,0 +1,313 @@ +/******************************************************************************* + * Copyright (c) 2000, 2017 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.model; + +import java.time.Duration; +import java.time.Instant; + +import org.eclipse.unittest.model.ITestElement; + +import org.eclipse.core.runtime.Assert; + +/** + * A test element n holds basic information about a test case or a test suite + * element + */ +public abstract class TestElement implements ITestElement { + private final TestSuiteElement fParent; + private final String fId; + private final String fTestName; + /** + * Extra (runner-specific) data, can be <code>null</code> + */ + private final String fData; + + /** + * The display name of the test element, can be <code>null</code>. In that case, + * use {@link TestElement#fTestName fTestName}. + */ + private final String fDisplayName; + + private Status fStatus; + protected FailureTrace fTrace; + + private boolean fAssumptionFailed; + + protected Instant testStartedInstant = null; + protected Duration fDuration = null; + + /** + * Constructs the test element object + * + * @param parent the parent, can be <code>null</code> + * @param id the test id + * @param testName the test name + * @param displayName the test display name, can be <code>null</code> + * @param data some runner-specific data, can be <code>null</code> + */ + public TestElement(TestSuiteElement parent, String id, String testName, String displayName, String data) { + Assert.isNotNull(id); + Assert.isNotNull(testName); + fParent = parent; + fId = id; + fTestName = testName; + fDisplayName = displayName; + fData = data; + fStatus = Status.NOT_RUN; + if (parent != null) { + parent.addChild(this); + } else if (!(this instanceof TestRunSession)) { + throw new IllegalArgumentException("Test elements must have a parent"); //$NON-NLS-1$ + } + } + + /** + * Returns the progress state of this test element. + * <ul> + * <li>{@link ProgressState#NOT_STARTED}: the test has not yet started</li> + * <li>{@link ProgressState#RUNNING}: the test is currently running</li> + * <li>{@link ProgressState#ABORTED}: the test has stopped before being + * completed</li> + * <li>{@link ProgressState#COMPLETED}: the test (and all its children) has + * completed</li> + * </ul> + * + * @return returns one of {@link ProgressState#NOT_STARTED}, + * {@link ProgressState#RUNNING}, {@link ProgressState#ABORTED} or + * {@link ProgressState#COMPLETED}. + */ + public ProgressState getProgressState() { + return getStatus().convertToProgressState(); + } + + /** + * Returns the result of the test element. + * <ul> + * <li>{@link org.eclipse.unittest.model.ITestElement.Result#UNDEFINED}: the + * result is not yet evaluated</li> + * <li>{@link org.eclipse.unittest.model.ITestElement.Result#OK}: the test has + * succeeded</li> + * <li>{@link org.eclipse.unittest.model.ITestElement.Result#ERROR}: the test + * has returned an error</li> + * <li>{@link org.eclipse.unittest.model.ITestElement.Result#FAILURE}: the test + * has returned an failure</li> + * <li>{@link org.eclipse.unittest.model.ITestElement.Result#IGNORED}: the test + * has been ignored (skipped)</li> + * </ul> + * + * @param includeChildren if <code>true</code>, the returned result is the + * combined result of the test and its children (if it + * has any). If <code>false</code>, only the test's + * result is returned. + * + * @return returns one of + * {@link org.eclipse.unittest.model.ITestElement.Result#UNDEFINED}, + * {@link org.eclipse.unittest.model.ITestElement.Result#OK}, + * {@link org.eclipse.unittest.model.ITestElement.Result#ERROR}, + * {@link org.eclipse.unittest.model.ITestElement.Result#FAILURE} or + * {@link org.eclipse.unittest.model.ITestElement.Result#IGNORED}. + * Clients should also prepare for other, new values. + */ + public Result getTestResult(boolean includeChildren) { + if (fAssumptionFailed) { + return Result.IGNORED; + } + return getStatus().convertToResult(); + } + + /** + * Returns the parent test element container or <code>null</code> if the test + * element is the test run session. + * + * @return the parent test suite + */ + public TestSuiteElement getParentContainer() { + return fParent; + } + + @Override + public FailureTrace getFailureTrace() { + Result testResult = getTestResult(false); + if ((testResult == Result.ERROR || testResult == Result.FAILURE + || (testResult == Result.IGNORED) && fTrace != null)) { + return fTrace; + } + return null; + } + + @Override + public TestSuiteElement getParent() { + return fParent; + } + + @Override + public String getId() { + return fId; + } + + @Override + public String getTestName() { + return fTestName; + } + + /** + * Sets the current test element status + * + * @param status one of {@link Status#NOT_RUN}, {@link Status#OK}, + * {@link Status#ERROR} or {@link Status#FAILURE}. + */ + public void setStatus(Status status) { + if (status == Status.RUNNING) { + testStartedInstant = Instant.now(); + } else if (status.convertToProgressState() == ProgressState.COMPLETED && testStartedInstant != null) { + this.fDuration = Duration.between(testStartedInstant, Instant.now()); + } + + fStatus = status; + TestSuiteElement parent = getParent(); + if (parent != null) { + parent.childChangedStatus(this, status); + } + } + + /** + * Sets the extended status for this test element + * + * @param status one of {@link Status#NOT_RUN}, {@link Status#OK}, + * {@link Status#ERROR} or {@link Status#FAILURE}. + * @param failureTrace stacktracee/error message or null + */ + public void setStatus(Status status, FailureTrace failureTrace) { + if (failureTrace != null && fTrace != null) { + // don't overwrite first trace if same test run logs multiple errors + fTrace = new FailureTrace(fTrace.getTrace() + failureTrace.getTrace(), fTrace.getExpected(), + fTrace.getActual()); + } else { + fTrace = failureTrace; + } + setStatus(status); + } + + /** + * Returns the status of this test element + * <ul> + * <li>{@link Status#NOT_RUN}: the test has not executed</li> + * <li>{@link Status#OK}: the test is successful</li> + * <li>{@link Status#ERROR}: the test had an error</li> + * <li>{@link Status#FAILURE}: the test had an assertion failure</li> + * </ul> + * + * @return returns one of {@link Status#NOT_RUN}, {@link Status#OK}, + * {@link Status#ERROR} or {@link Status#FAILURE}. + */ + public Status getStatus() { + return fStatus; + } + + /** + * Sets a duration value for a test element + * + * @param duration a duration value + */ + public void setDuration(Duration duration) { + this.fDuration = duration; + } + + @Override + public Duration getDuration() { + return this.fDuration; + } + + /** + * Sets up the assumption failure flag for this test + * + * @param assumptionFailed a flag indicating the assumption failure + */ + public void setAssumptionFailed(boolean assumptionFailed) { + fAssumptionFailed = assumptionFailed; + } + + /** + * Indicates if there was an assumption failure + * + * @return true if there was a comparison failure, otherwise return false + */ + public boolean isAssumptionFailure() { + return fAssumptionFailed; + } + + @Override + public String toString() { + return getTestName() + '[' + getProgressState() + " - " + getTestResult(true) + ']'; //$NON-NLS-1$ + } + + @Override + public String getDisplayName() { + return fDisplayName != null ? fDisplayName : getTestName(); + } + + @Override + public String getData() { + return fData; + } + + @Override + public TestRunSession getTestRunSession() { + return getParent().getTestRunSession(); + } + + /** + * Returns the total number of expected test case elements, or the total number + * of ran test case elements if completed, or <code>null</null> if tests are + * still running and we can't know the final count. + * + * @return a total number of test cases + */ + abstract Integer getFinalTestCaseCount(); + + /** + * Returns the number of started test case elements + * + * @return a number of started test cases + */ + abstract int countStartedTestCases(); + + /** + * Returns the number of failed test case elements + * + * @return a number of failed test cases + */ + abstract int getCurrentFailureCount(); + + /** + * Returns the number of assumption failures + * + * @return a number of assumption failures + */ + abstract int getCurrentAssumptionFailureCount(); + + /** + * Returns the number of ignored test case elements + * + * @return a number of ignored test cases + */ + abstract int getCurrentIgnoredCount(); + + /** + * Returns the number of test case elements with errors + * + * @return a number of test cases with errors + */ + abstract int getCurrentErrorCount(); +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestRunListenerAdapter.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestRunListenerAdapter.java new file mode 100644 index 000000000..f3a04a1d5 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestRunListenerAdapter.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2000, 2016 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.model; + +import java.time.Duration; + +import org.eclipse.unittest.internal.launcher.TestListenerRegistry; +import org.eclipse.unittest.internal.launcher.TestRunListener; +import org.eclipse.unittest.model.ITestCaseElement; +import org.eclipse.unittest.model.ITestElement; +import org.eclipse.unittest.model.ITestElement.FailureTrace; +import org.eclipse.unittest.model.ITestElement.Result; + +import org.eclipse.core.runtime.ListenerList; + +/** + * Notifier for the callback listener API {@link TestRunListener}. + */ +public class TestRunListenerAdapter implements ITestSessionListener { + + private final TestRunSession fSession; + + /** + * Constructs a {@link TestRunListenerAdapter} object + * + * @param session a {@link TestRunSession} object + */ + public TestRunListenerAdapter(TestRunSession session) { + fSession = session; + } + + private ListenerList<TestRunListener> getListenerList() { + return TestListenerRegistry.getDefault().getUnitTestRunListeners(); + } + + private void fireSessionStarted() { + for (TestRunListener listener : getListenerList()) { + listener.sessionStarted(fSession); + } + } + + private void fireSessionFinished() { + for (TestRunListener listener : getListenerList()) { + listener.sessionFinished(fSession); + } + } + + private void fireTestCaseStarted(ITestCaseElement testCaseElement) { + for (TestRunListener listener : getListenerList()) { + listener.testCaseStarted(testCaseElement); + } + } + + private void fireTestCaseFinished(ITestCaseElement testCaseElement) { + for (TestRunListener listener : getListenerList()) { + listener.testCaseFinished(testCaseElement); + } + } + + @Override + public void sessionStarted() { + // wait until all test are added + } + + @Override + public void sessionCompleted(Duration duration) { + fireSessionFinished(); + } + + @Override + public void sessionAborted(Duration duration) { + fireSessionFinished(); + } + + @Override + public void testAdded(ITestElement testElement) { + // do nothing + } + + @Override + public void runningBegins() { + fireSessionStarted(); + } + + @Override + public void testStarted(ITestCaseElement testCaseElement) { + fireTestCaseStarted(testCaseElement); + } + + @Override + public void testEnded(ITestCaseElement testCaseElement) { + fireTestCaseFinished(testCaseElement); + } + + @Override + public void testFailed(ITestElement testElement, Result status, FailureTrace trace) { + // ignore + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestRunSession.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestRunSession.java new file mode 100644 index 000000000..333ee659d --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestRunSession.java @@ -0,0 +1,749 @@ +/******************************************************************************* + * Copyright (c) 2000, 2018 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.model; + +import java.text.DateFormat; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; + +import org.eclipse.unittest.internal.UnitTestPlugin; +import org.eclipse.unittest.internal.launcher.TestViewSupportRegistry; +import org.eclipse.unittest.launcher.ITestRunnerClient; +import org.eclipse.unittest.model.ITestCaseElement; +import org.eclipse.unittest.model.ITestElement; +import org.eclipse.unittest.model.ITestRunSession; +import org.eclipse.unittest.model.ITestSuiteElement; +import org.eclipse.unittest.ui.ITestViewSupport; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.SafeRunner; + +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchManager; +import org.eclipse.debug.core.ILaunchesListener2; +import org.eclipse.debug.core.Launch; +import org.eclipse.debug.core.model.ISourceLocator; + +/** + * A test run session holds all information about a test run, i.e. launch + * configuration, launch, test tree (including results). + */ +public class TestRunSession extends TestSuiteElement implements ITestRunSession, ITestRunSessionReport { + + /** + * The launch, or <code>null</code> iff this session was run externally. + */ + private final ILaunch fLaunch; + private final String fTestRunName; + private final ITestViewSupport fTestRunnerSupport; + + /** + * Test runner client or <code>null</code>. + */ + private ITestRunnerClient fTestRunnerClient; + + private final ListenerList<ITestSessionListener> fSessionListeners; + private final TestSessionNotifier fSessionNotifier = new TestSessionNotifier(); + + /** + * The test run session's cached result, or <code>null</code> if + * <code>fTestRoot != null</code>. + */ + private Result fTestResult; + + /** + * Map from testId to testElement. + */ + private HashMap<String, TestElement> fIdToTest; + + volatile Instant fStartTime; + volatile Integer fPredefinedTestCount; + + volatile boolean fIsAborted; + private Integer predefinedTestCount; + private boolean completedOrAborted; + + /** + * Constructs a test run session object. + * + * @param testRunName name of the test run + * @param startTime a start time of a test run + * @param launchConfiguration a launch configuration for a test run + */ + public TestRunSession(String testRunName, Instant startTime, ILaunchConfiguration launchConfiguration) { + super(null, "-1", testRunName, null, null, null); //$NON-NLS-1$ + // TODO: check assumptions about non-null fields + + fLaunch = new NoopLaunch(launchConfiguration, ILaunchManager.RUN_MODE, null); + fTestRunnerSupport = TestViewSupportRegistry.newTestRunnerViewSupport(launchConfiguration).orElse(null); + + Assert.isNotNull(testRunName); + fTestRunName = testRunName; + + fIdToTest = new HashMap<>(); + + fTestRunnerClient = null; + fStartTime = startTime; + + fSessionListeners = new ListenerList<>(); + } + + /** + * Constructs a test run session object from a launch. + * + * @param launch an {@link ILaunch} object + */ + public TestRunSession(ILaunch launch) { + super(null, "-1", "<TestRunSession>", null, null, null); //$NON-NLS-1$ //$NON-NLS-2$ + Assert.isNotNull(launch); + + fLaunch = launch; + + ILaunchConfiguration launchConfiguration = launch.getLaunchConfiguration(); + if (launchConfiguration != null) { + fTestRunName = launchConfiguration.getName(); + fTestRunnerSupport = TestViewSupportRegistry.newTestRunnerViewSupport(launchConfiguration).orElse(null); + } else { + fTestRunName = "<TestRunSession>"; //$NON-NLS-1$ + fTestRunnerSupport = null; + } + + fIdToTest = new HashMap<>(); + + if (fTestRunnerSupport != null) { + fTestRunnerClient = fTestRunnerSupport.newTestRunnerClient(this); + } + + final ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager(); + launchManager.addLaunchListener(new ILaunchesListener2() { + @Override + public void launchesTerminated(ILaunch[] launches) { + if (Arrays.asList(launches).contains(fLaunch)) { + launchManager.removeLaunchListener(this); + } + } + + @Override + public void launchesRemoved(ILaunch[] launches) { + if (Arrays.asList(launches).contains(fLaunch)) { + launchManager.removeLaunchListener(this); + } + } + + @Override + public void launchesChanged(ILaunch[] launches) { + // do nothing + } + + @Override + public void launchesAdded(ILaunch[] launches) { + // do nothing + } + }); + fSessionListeners = new ListenerList<>(); + setStatus(Status.RUNNING); + addTestSessionListener(new TestRunListenerAdapter(this)); + fTestRunnerClient.startMonitoring(); + } + + /** + * Resets a test run session object (used when re-creating a test run session + * from an XML file( + * + * TODO Consider removal as it's only use in XML parsing + */ + public void reset() { + fTestResult = null; + fIdToTest = new HashMap<>(); + } + + @Override + public ProgressState getProgressState() { + if (isRunning()) { + return ProgressState.RUNNING; + } + if (isStopped()) { + return ProgressState.ABORTED; + } + return ProgressState.COMPLETED; + } + + @Override + public FailureTrace getFailureTrace() { + return null; + } + + @Override + public TestSuiteElement getParentContainer() { + return null; + } + + @Override + public TestRunSession getTestRunSession() { + return this; + } + + /** + * Returns the Test Runner View Support for which this test run session has been + * launched, or <code>null</code> if not available. + * + * @return the test runner view support, or <code>null</code> is not available. + */ + public ITestViewSupport getTestViewSupport() { + return fTestRunnerSupport; + } + + /** + * @return the launch, or <code>null</code> iff this session was run externally + */ + @Override + public ILaunch getLaunch() { + return fLaunch; + } + + @Override + public String getTestRunName() { + return fTestRunName; + } + + @Override + public int getCurrentErrorCount() { + return getChildren().stream().mapToInt(TestElement::getCurrentErrorCount).sum(); + } + + @Override + public int getCurrentFailureCount() { + return getChildren().stream().mapToInt(TestElement::getCurrentFailureCount).sum(); + } + + @Override + public int getCurrentAssumptionFailureCount() { + return getChildren().stream().mapToInt(TestElement::getCurrentAssumptionFailureCount).sum(); + } + + @Override + public int getCurrentIgnoredCount() { + return getChildren().stream().mapToInt(TestElement::getCurrentIgnoredCount).sum(); + } + + /** + * Returns start time for a run session + * + * @return an {@link Instant} object indicating a test run session start time + */ + public Instant getStartTime() { + return fStartTime; + } + + /** + * Indicates if the test run session has been stopped or terminated + * + * @return <code>true</code> if the session has been stopped or terminated, + * otherwise returns <code>false</code> + */ + @Override + public boolean isStopped() { + return fIsAborted; + } + + /** + * Adds an {@link ITestSessionListener} listener + * + * @param listener an {@link ITestSessionListener} object + */ + public synchronized void addTestSessionListener(ITestSessionListener listener) { + fSessionListeners.add(listener); + } + + /** + * Removes an {@link ITestSessionListener} listener + * + * @param listener an {@link ITestSessionListener} object + */ + public void removeTestSessionListener(ITestSessionListener listener) { + fSessionListeners.remove(listener); + } + + @Override + public boolean isStarting() { + return getStartTime() == null && fLaunch != null && !fLaunch.isTerminated(); + } + + /** + * Forces a test run session to abort its execution + */ + public void abortTestRun() { + fIsAborted = true; + if (fTestRunnerClient != null) { + fTestRunnerClient.stopTest(); + fTestRunnerClient.stopMonitoring(); + } + } + + @Override + public boolean isRunning() { + return getStartTime() != null && fTestRunnerClient != null && !completedOrAborted; + } + + @Override + public TestElement getTestElement(String id) { + return fIdToTest.get(id); + } + + private TestElement addTreeEntry(String id, String testName, boolean isSuite, Integer testCount, + boolean isDynamicTest, TestSuiteElement parent, String displayName, String data) { + return createTestElement(parent != null ? parent : this, id, testName, isSuite, testCount, isDynamicTest, + displayName, data); + } + + /** + * Creates a test element, either {@link ITestSuiteElement} or + * {@link ITestCaseElement} instance, depending on the arguments. + * + * @param parent a parent test suite element + * @param id an identifier of the test element + * @param testName a name of the test element + * @param isSuite a flag indicating if the test element should be + * represented by a test suite element + * @param testCount a number of predefined test cases in case of test suite + * element + * @param isDynamicTest a flag indicating that test suite is dynamic (that + * doesn't have predefined tests) + * @param displayName a display name for the test element + * @param data some test runner specific data, can be <code>null</code> + * @return a created {@link ITestSuiteElement} or {@link ITestCaseElement} + * instance + */ + public TestElement createTestElement(TestSuiteElement parent, String id, String testName, boolean isSuite, + Integer testCount, boolean isDynamicTest, String displayName, String data) { + TestElement testElement; + if (isSuite) { + TestSuiteElement testSuiteElement = new TestSuiteElement(parent != null ? parent : this, id, testName, + testCount, displayName, data); + testElement = testSuiteElement; + } else { + testElement = new TestCaseElement(parent != null ? parent : this, id, testName, displayName, isDynamicTest, + data); + } + fIdToTest.put(id, testElement); + return testElement; + } + + private final class NoopLaunch extends Launch { + private NoopLaunch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator) { + super(launchConfiguration, mode, locator); + } + + @Override + public boolean isTerminated() { + return true; + } + + @Override + public boolean isDisconnected() { + return true; + } + } + + /** + * Listens to events from the and translates {@link ITestRunnerClient} them into + * high-level model events (broadcasted to {@link ITestSessionListener}s). + */ + private class TestSessionNotifier { + + private boolean firstStart; + + /** + * Notifies on a test run started + * + * @param testCount number of tests in this run + */ + public void testRunStarted(Integer testCount) { + fStartTime = Instant.now(); + fPredefinedTestCount = testCount; + + for (ITestSessionListener listener : fSessionListeners) { + listener.sessionStarted(); + } + } + + /** + * Notifies on a test run ended + * + * @param duration a duration of this test run + */ + public void testRunEnded(Duration duration) { + for (ITestSessionListener listener : fSessionListeners) { + listener.sessionCompleted(duration); + } + } + + /** + * Notifies on a test run stopped (aborted) + * + * @param duration a duration of this test run + */ + public void testRunStopped(Duration duration) { + fIsAborted = true; + + for (ITestSessionListener listener : fSessionListeners) { + listener.sessionAborted(duration); + } + } + + /** + * Notifies on a test tree entry is created + * + * @param testId an identifier of a test entry + * @param testName a name of test + * @param isSuite <code>true</code> indicates that a test entry is a test + * suite, <code>false</code> - a test case + * @param testCount a number of tests in a test suite or <code>null</code> + * @param isDynamicTest indicates if a test is a dynamic test (doesn't have a + * predefined number of tests) + * @param parent a parent test suite element + * @param displayName a display name for the test + * @param uniqueId an unique identifier of test entry or <code>null</code> + * @return an {@link ITestElement} object instance + */ + public ITestElement testTreeEntry(String testId, String testName, boolean isSuite, Integer testCount, + boolean isDynamicTest, ITestSuiteElement parent, String displayName, String uniqueId) { + ITestElement testElement = addTreeEntry(testId, testName, isSuite, testCount, isDynamicTest, + (TestSuiteElement) parent, displayName, uniqueId); + + for (ITestSessionListener listener : fSessionListeners) { + listener.testAdded(testElement); + } + return testElement; + } + + /** + * Notifies on a test started + * + * @param test a test element object + */ + public void testStarted(ITestElement test) { + if (!(test instanceof TestCaseElement)) { + return; + } + if (firstStart) { + for (ITestSessionListener listener : fSessionListeners) { + listener.runningBegins(); + } + firstStart = false; + } + setStatus(test, Status.RUNNING); + + for (ITestSessionListener listener : fSessionListeners) { + listener.testStarted((ITestCaseElement) test); + } + } + + /** + * Notifies on a test ended + * + * @param testElement a test element object + * @param isIgnored indicates a skipped (not run) test element + */ + public void testEnded(ITestElement testElement, boolean isIgnored) { + if (testElement == null) { + return; + } + if (!(testElement instanceof TestCaseElement)) { + if (isIgnored) { + ((TestElement) testElement).setAssumptionFailed(true); + setStatus(testElement, Status.OK); + } else { + logUnexpectedTest(testElement.getId(), testElement); + } + return; + } + TestCaseElement testCaseElement = (TestCaseElement) testElement; + if (isIgnored) { + testCaseElement.setIgnored(true); + } + + if (testCaseElement.getStatus() == Status.RUNNING) + setStatus(testCaseElement, Status.OK); + + for (ITestSessionListener listener : fSessionListeners) { + listener.testEnded(testCaseElement); + } + } + + /** + * Notifies on a failed test element + * + * @param testElement a failed test element + * @param status a result status of test execution + * @param isAssumptionFailed indicates if the failure is an assumption failure + * @param trace a failure trace + */ + public void testFailed(ITestElement testElement, Result status, boolean isAssumptionFailed, + FailureTrace trace) { + if (testElement == null) { + return; + } + + if (isAssumptionFailed) { + ((TestElement) testElement).setAssumptionFailed(true); + status = Result.OK; + } + + registerTestFailureStatus((TestElement) testElement, status, trace); + + for (ITestSessionListener listener : fSessionListeners) { + listener.testFailed(testElement, status, trace); + } + } + + private void logUnexpectedTest(String testId, ITestElement testElement) { + UnitTestPlugin + .log(new Exception("Unexpected TestElement type for testId '" + testId + "': " + testElement)); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + /** + * Registers a failure status for a test element + * + * @param testElement a test element + * @param status a result of test execution + * @param failureTrace a failure trace + */ + public void registerTestFailureStatus(TestElement testElement, Result status, FailureTrace failureTrace) { + testElement.setStatus(Status.fromResult(status), failureTrace); + } + + /** + * Registers an ended test element + * + * @param testElement a test element + * @param completed <code>true</code> indicates that the test was completed. + * <code>false</code> otherwise + */ + public void registerTestEnded(TestElement testElement, boolean completed) { + if (testElement instanceof TestCaseElement) { + if (!completed) { + return; + } + TestCaseElement testCaseElement = (TestCaseElement) testElement; + if (!testCaseElement.getStatus().isErrorOrFailure()) + setStatus(testElement, Status.OK); + } + } + + /** + * Sets a {@link Status} for a test element + * + * @param testElement a test element + * @param status an execution status + */ + private void setStatus(ITestElement testElement, Status status) { + ((TestElement) testElement).setStatus(status); + } + + /** + * Returns an array of all failed {@link ITestElement}s + * + * @return an array of failed {@link ITestElement}s + */ + public List<TestElement> getAllFailedTestElements() { + List<TestElement> failures = new ArrayList<>(); + addFailures(failures, this); + return Collections.unmodifiableList(failures); + } + + private void addFailures(Collection<TestElement> failures, TestElement testElement) { + Result testResult = testElement.getTestResult(true); + if (testResult == Result.ERROR || testResult == Result.FAILURE) { + failures.add(testElement); + } + if (testElement instanceof TestSuiteElement) { + TestSuiteElement testSuiteElement = (TestSuiteElement) testElement; + for (TestElement child : testSuiteElement.getChildren()) { + addFailures(failures, child); + } + } + } + + @Override + public String toString() { + return fTestRunName + " " + DateFormat.getDateTimeInstance().format(new Date(fStartTime.toEpochMilli())); //$NON-NLS-1$ + } + + @Override + public TestSuiteElement getParent() { + return null; + } + + @Override + public String getTestName() { + return getTestRunName(); + } + + /** + * An abstract base class for a listener safe runnable + */ + public abstract class ListenerSafeRunnable implements ISafeRunnable { + @Override + public void handleException(Throwable exception) { + UnitTestPlugin.log(exception); + } + } + + @Override + public TestCaseElement newTestCase(String testId, String testName, ITestSuiteElement parent, String displayName, + String data) { + return (TestCaseElement) fSessionNotifier.testTreeEntry(testId, testName, false, Integer.valueOf(1), false, + parent, displayName, data); + } + + @Override + public TestSuiteElement newTestSuite(String testId, String testName, Integer testCount, ITestSuiteElement parent, + String displayName, String data) { + return (TestSuiteElement) fSessionNotifier.testTreeEntry(testId, testName, true, testCount, testCount == null, + parent, displayName, data); + } + + @Override + public void notifyTestSessionAborted(final Duration reportDuration, Exception cause) { + if (isStopped()) { + return; + } + if (reportDuration != null) { + setDuration(reportDuration); + } + fTestRunnerClient.stopMonitoring(); + this.completedOrAborted = true; + SafeRunner.run(new ListenerSafeRunnable() { + @Override + public void run() { + fSessionNotifier.testRunStopped(fDuration); + } + }); + } + + @Override + public void notifyTestSessionCompleted(final Duration reportDuration) { + if (isStopped()) { + return; + } + if (reportDuration != null) { + setDuration(reportDuration); + } + fTestRunnerClient.stopMonitoring(); + this.completedOrAborted = true; + SafeRunner.run(new ListenerSafeRunnable() { + @Override + public void run() { + fSessionNotifier.testRunEnded(fDuration); + } + }); + } + + @Override + public void notifyTestEnded(ITestElement test, boolean isIgnored) { + if (isStopped()) { + return; + } + SafeRunner.run(new ListenerSafeRunnable() { + @Override + public void run() { + fSessionNotifier.testEnded(test, isIgnored); + } + }); + } + + @Override + public void notifyTestStarted(ITestElement test) { + if (isStopped()) { + return; + } + SafeRunner.run(new ListenerSafeRunnable() { + @Override + public void run() { + fSessionNotifier.testStarted(test); + } + }); + } + + @Override + public void notifyTestSessionStarted(final Integer count) { + if (isStopped()) { + return; + } + this.predefinedTestCount = count; + SafeRunner.run(new ListenerSafeRunnable() { + @Override + public void run() { + fSessionNotifier.testRunStarted(count); + } + }); + } + + @Override + public void notifyTestFailed(ITestElement test, Result status, boolean isAssumptionFailed, + FailureTrace failureTrace) { + if (isStopped()) { + return; + } + if (status != Result.FAILURE && status != Result.ERROR) { + throw new IllegalArgumentException("Status has to be FAILURE or ERROR"); //$NON-NLS-1$ + } + SafeRunner.run(new ListenerSafeRunnable() { + @Override + public void run() { + fSessionNotifier.testFailed(test, status, isAssumptionFailed, failureTrace); + } + }); + } + + @Override + public Integer getFinalTestCaseCount() { + if (predefinedTestCount != null) { + return predefinedTestCount; + } + if (getChildren().isEmpty()) { + return null; + } + if (!isRunning()) { + int res = 0; + for (TestElement child : getChildren()) { + Integer childCount = child.getFinalTestCaseCount(); + if (childCount == null) { + return null; + } + res += childCount.intValue(); + } + return Integer.valueOf(res); + } + return null; + } + + @Override + public Result getTestResult(boolean includeChildren) { + return this.fTestResult != null ? this.fTestResult : super.getTestResult(includeChildren); + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestSuiteElement.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestSuiteElement.java new file mode 100644 index 000000000..f285502df --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/TestSuiteElement.java @@ -0,0 +1,287 @@ +/******************************************************************************* + * Copyright (c) 2000, 2017 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.unittest.internal.model; + +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import org.eclipse.unittest.model.ITestElement; +import org.eclipse.unittest.model.ITestSuiteElement; + +/** + * A test suite element. Holds all information about a test case + */ +public class TestSuiteElement extends TestElement implements ITestSuiteElement { + + private final List<TestElement> fChildren; + private Status fChildrenStatus; + private Integer expectedTestCount; + + /** + * Constructs a test suite object + * + * @param parent a parent {@link TestSuiteElement} object + * @param id an identifier of a test suite + * @param testName a name of test suite + * @param expectedChildrenCount an expected children count + * @param displayName a display name of test suite + * @param data an optional additional data for a test suite + */ + public TestSuiteElement(TestSuiteElement parent, String id, String testName, Integer expectedChildrenCount, + String displayName, String data) { + super(parent, id, testName, displayName, data); + this.expectedTestCount = expectedChildrenCount; + fChildren = new ArrayList<>(expectedChildrenCount == null ? 0 : expectedChildrenCount.intValue()); + } + + @Override + public Result getTestResult(boolean includeChildren) { + if (includeChildren) { + return getStatus().convertToResult(); + } else { + return super.getStatus().convertToResult(); + } + } + + @Override + public List<TestElement> getChildren() { + return Collections.unmodifiableList(fChildren); + } + + /** + * Adds a child {@link ITestElement} to this test suite element + * + * @param child a child {@link ITestElement} + */ + public void addChild(TestElement child) { + fChildren.add(child); + } + + /** + * Removes a child {@link ITestElement} from this test suite element + * + * @param child a child {@link ITestElement} + */ + public void removeChild(TestElement child) { + fChildren.remove(child); + } + + @Override + public Status getStatus() { + Status suiteStatus = getSuiteStatus(); + if (fChildrenStatus != null) { + // must combine children and suite status here, since failures can occur e.g. in + // @AfterClass + return combineStatus(fChildrenStatus, suiteStatus); + } else { + return suiteStatus; + } + } + + private Status getCumulatedStatus() { + TestElement[] children = fChildren.toArray(new TestElement[fChildren.size()]); // copy list to avoid concurreny + // problems + if (children.length == 0) + return getSuiteStatus(); + + Status cumulated = children[0].getStatus(); + + for (int i = 1; i < children.length; i++) { + Status childStatus = children[i].getStatus(); + cumulated = combineStatus(cumulated, childStatus); + } + // not necessary, see special code in Status.combineProgress() +// if (suiteStatus.isErrorOrFailure() && cumulated.isNotRun()) +// return suiteStatus; //progress is Done if error in Suite and no children run + return cumulated; + } + + /** + * Returns a test suite execution status + * + * @return a test suite execution status + */ + public Status getSuiteStatus() { + return super.getStatus(); + } + + /** + * Notifies on the status changes in a specified child {@link ITestElement} + * element + * + * @param child a child {@link ITestElement} element + * @param childStatus a new status value + */ + public void childChangedStatus(ITestElement child, Status childStatus) { + int childCount = fChildren.size(); + if (child == fChildren.get(0) && childStatus.isRunning()) { + // is first child, and is running -> copy status + internalSetChildrenStatus(childStatus); + return; + } + TestElement lastChild = fChildren.get(childCount - 1); + if (child == lastChild) { + if (childStatus.isDone()) { + // all children done, collect cumulative status + internalSetChildrenStatus(getCumulatedStatus()); + return; + } + // go on (child could e.g. be a TestSuiteElement with RUNNING_FAILURE) + + } else if (!lastChild.getStatus().isNotRun()) { + // child is not last, but last child has been run -> child has been rerun or is + // rerunning + internalSetChildrenStatus(getCumulatedStatus()); + return; + } + + // finally, set RUNNING_FAILURE/ERROR if child has failed but suite has not + // failed: + if (childStatus.isFailure()) { + if (fChildrenStatus == null || !fChildrenStatus.isErrorOrFailure()) { + internalSetChildrenStatus(Status.RUNNING_FAILURE); + return; + } + } else if (childStatus.isError()) { + if (fChildrenStatus == null || !fChildrenStatus.isError()) { + internalSetChildrenStatus(Status.RUNNING_ERROR); + return; + } + } + } + + private void internalSetChildrenStatus(Status status) { + if (fChildrenStatus == status) + return; + + if (status == Status.RUNNING) { + if (fDuration != null) { + // re-running child: ignore change + } else { + testStartedInstant = Instant.now(); + } + } else if (status.convertToProgressState() == ProgressState.COMPLETED && fDuration == null + && testStartedInstant != null) { + fDuration = Duration.between(testStartedInstant, Instant.now()); + } + + fChildrenStatus = status; + + TestSuiteElement parent = getParent(); + if (parent != null) { + parent.childChangedStatus(this, getStatus()); + } + } + + @Override + public String toString() { + return "TestSuite: " + getTestName() + " : " + super.toString() + " (" + fChildren.size() + ")"; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + + private static Status combineStatus(Status one, Status two) { + Status progress = combineProgress(one, two); + Status error = combineError(one, two); + return combineProgressAndErrorStatus(progress, error); + } + + private static Status combineProgress(Status one, Status two) { + if (one.isNotRun() && two.isNotRun()) { + return Status.NOT_RUN; + } + if (one.isDone() && two.isDone()) { + return Status.OK; + } + if (!one.isRunning() && !two.isRunning()) { + return Status.OK; // one done, one not-run -> a parent failed and its children are not run + } + return Status.RUNNING; + } + + private static Status combineError(Status one, Status two) { + if (one.isError() || two.isError()) { + return Status.ERROR; + } + if (one.isFailure() || two.isFailure()) { + return Status.FAILURE; + } + return Status.OK; + } + + private static Status combineProgressAndErrorStatus(Status progress, Status error) { + if (progress.isDone()) { + if (error.isError()) { + return Status.ERROR; + } + if (error.isFailure()) { + return Status.FAILURE; + } + return Status.OK; + } + + if (progress.isNotRun()) { + return Status.NOT_RUN; + } + + if (error.isError()) { + return Status.RUNNING_ERROR; + } + if (error.isFailure()) { + return Status.RUNNING_FAILURE; + } + return Status.RUNNING; + } + + @Override + Integer getFinalTestCaseCount() { + if (expectedTestCount != null) { + return expectedTestCount; + } + if (getStatus().isDone()) { + return Integer.valueOf(getChildren().stream().map(TestElement::getFinalTestCaseCount) + .filter(Objects::nonNull).mapToInt(Integer::intValue).sum()); + } + return null; + } + + @Override + public int countStartedTestCases() { + return getChildren().stream().mapToInt(TestElement::countStartedTestCases).sum(); + } + + @Override + int getCurrentFailureCount() { + return getChildren().stream().mapToInt(TestElement::getCurrentFailureCount).sum(); + } + + @Override + int getCurrentAssumptionFailureCount() { + return getChildren().stream().mapToInt(TestElement::getCurrentAssumptionFailureCount).sum(); + } + + @Override + int getCurrentIgnoredCount() { + return getChildren().stream().mapToInt(TestElement::getCurrentIgnoredCount).sum(); + } + + @Override + int getCurrentErrorCount() { + return getChildren().stream().mapToInt(TestElement::getCurrentErrorCount).sum(); + } + +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/UnitTestLaunchListener.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/UnitTestLaunchListener.java new file mode 100644 index 000000000..5801547e3 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/UnitTestLaunchListener.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2020 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.model; + +import java.util.HashSet; + +import org.eclipse.unittest.internal.UnitTestPlugin; +import org.eclipse.unittest.internal.launcher.TestListenerRegistry; +import org.eclipse.unittest.internal.launcher.TestRunListener; +import org.eclipse.unittest.internal.launcher.TestViewSupportRegistry; +import org.eclipse.unittest.internal.launcher.UnitTestLaunchConfigurationConstants; +import org.eclipse.unittest.ui.ITestViewSupport; + +import org.eclipse.core.runtime.CoreException; + +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchListener; + +/** + * Used to track new launches. We need to do this so that we only attach a + * TestRunner once to a launch. Once a test runner is connected, it is removed + * from the set. + */ +public class UnitTestLaunchListener implements ILaunchListener { + + /** + * Used to track new launches. We need to do this so that we only attach a + * TestRunner once to a launch. Once a test runner is connected, it is removed + * from the set. + */ + private HashSet<ILaunch> fTrackedLaunches = new HashSet<>(20); + + @Override + public void launchAdded(ILaunch launch) { + ILaunchConfiguration config = launch.getLaunchConfiguration(); + if (config == null) + return; + + try { + if (!config.hasAttribute(UnitTestLaunchConfigurationConstants.ATTR_UNIT_TEST_VIEW_SUPPORT)) + return; + } catch (CoreException e1) { + UnitTestPlugin.log(e1); + return; + } + + ITestViewSupport testRunnerViewSupport = TestViewSupportRegistry.newTestRunnerViewSupport(config).orElse(null); + if (testRunnerViewSupport == null) + return; + + fTrackedLaunches.add(launch); + } + + @Override + public void launchRemoved(final ILaunch launch) { + fTrackedLaunches.remove(launch); + } + + @Override + public void launchChanged(final ILaunch launch) { + if (!fTrackedLaunches.contains(launch)) + return; + + // Load session on 1st change (usually 1st process added), although it's not + // much reliable. Each TestRunnerClient should take care of listening to the + // launch to get the right IProcess or stream or whatever else i useful + if (UnitTestModel.getInstance().getTestRunSessions().stream() + .noneMatch(session -> launch.equals(session.getLaunch()))) { + TestRunSession testRunSession = new TestRunSession(launch); + UnitTestModel.getInstance().addTestRunSession(testRunSession); + for (TestRunListener listener : TestListenerRegistry.getDefault().getUnitTestRunListeners()) { + listener.sessionLaunched(testRunSession); + } + } + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/UnitTestModel.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/UnitTestModel.java new file mode 100644 index 000000000..2e270c7ba --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/model/UnitTestModel.java @@ -0,0 +1,287 @@ +/******************************************************************************* + * Copyright (c) 2000, 2020 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.model; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.xml.sax.SAXException; + +import org.eclipse.unittest.internal.UnitTestPlugin; +import org.eclipse.unittest.internal.UnitTestPreferencesConstants; +import org.eclipse.unittest.internal.junitXmlReport.TestRunHandler; +import org.eclipse.unittest.model.ITestRunSession; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Platform; + +/** + * Central registry for Unit Test test runs. + */ +public final class UnitTestModel { + + private final ListenerList<ITestRunSessionListener> fTestRunSessionListeners = new ListenerList<>(); + /** + * Active test run sessions, youngest first. + */ + private final LinkedList<TestRunSession> fTestRunSessions = new LinkedList<>(); + + private static UnitTestModel INSTANCE = null; + + private UnitTestModel() { + + } + + /** + * Returns a {@link UnitTestModel} object instance + * + * @return a {@link UnitTestModel} object instance + */ + public static synchronized UnitTestModel getInstance() { + if (INSTANCE == null) { + INSTANCE = new UnitTestModel(); + } + return INSTANCE; + } + + /** + * Starts the model (called by the {@link UnitTestPlugin} on startup). + */ + public void start() { + /* + * TODO: restore on restart: - only import headers! - only import last n + * sessions; remove all other files in historyDirectory + */ +// File historyDirectory= UnitTestPlugin.getHistoryDirectory(); +// File[] swapFiles= historyDirectory.listFiles(); +// if (swapFiles != null) { +// Arrays.sort(swapFiles, new Comparator() { +// public int compare(Object o1, Object o2) { +// String name1= ((File) o1).getName(); +// String name2= ((File) o2).getName(); +// return name1.compareTo(name2); +// } +// }); +// for (int i= 0; i < swapFiles.length; i++) { +// final File file= swapFiles[i]; +// SafeRunner.run(new ISafeRunnable() { +// public void run() throws Exception { +// importTestRunSession(file ); +// } +// public void handleException(Throwable exception) { +// UnitTestPlugin.log(exception); +// } +// }); +// } +// } + +// addTestRunSessionListener(new LegacyTestRunSessionListener()); + } + + /** + * Stops the model (called by the {@link UnitTestPlugin} on shutdown). + */ + public void stop() { +// for (Iterator iter= fTestRunSessions.iterator(); iter.hasNext();) { +// final TestRunSession session= (TestRunSession) iter.next(); +// SafeRunner.run(new ISafeRunnable() { +// public void run() throws Exception { +// session.swapOut(); +// } +// public void handleException(Throwable exception) { +// UnitTestPlugin.log(exception); +// } +// }); +// } + } + + /** + * Adds an {@link ITestRunSessionListener} object + * + * @param listener a listener object + */ + public void addTestRunSessionListener(ITestRunSessionListener listener) { + fTestRunSessionListeners.add(listener); + } + + /** + * Removes an {@link ITestRunSessionListener} object + * + * @param listener a listener object + */ + public void removeTestRunSessionListener(ITestRunSessionListener listener) { + fTestRunSessionListeners.remove(listener); + } + + /** + * Returns a list of {@link TestRunSession} objects + * + * @return a list of {@link TestRunSession} objects + */ + public synchronized List<TestRunSession> getTestRunSessions() { + return new ArrayList<>(fTestRunSessions); + } + + /** + * Adds a specified {@link TestRunSession} object into the list of processed + * test run sessions. + * + * The list length is limited by the value of + * {@link UnitTestPreferencesConstants#MAX_TEST_RUNS} preference. + * + * @param testRunSession a {@link TestRunSession} object to be added + * @see org.eclipse.unittest.internal.UnitTestPreferencesConstants#MAX_TEST_RUNS + * @see org.eclipse.unittest.internal.model.UnitTestModel#removeTestRunSession(TestRunSession) + */ + void addTestRunSession(TestRunSession testRunSession) { + Assert.isNotNull(testRunSession); + ArrayList<TestRunSession> toRemove = new ArrayList<>(); + + synchronized (this) { + Assert.isLegal(!fTestRunSessions.contains(testRunSession)); + fTestRunSessions.addFirst(testRunSession); + + int maxCount = Platform.getPreferencesService().getInt(UnitTestPlugin.PLUGIN_ID, + UnitTestPreferencesConstants.MAX_TEST_RUNS, 10, null); + int size = fTestRunSessions.size(); + if (size > maxCount) { + List<TestRunSession> excess = fTestRunSessions.subList(maxCount, size); + for (Iterator<TestRunSession> iter = excess.iterator(); iter.hasNext();) { + TestRunSession oldSession = iter.next(); + if (oldSession.isStopped()) { + toRemove.add(oldSession); + iter.remove(); + } + } + } + } + + toRemove.forEach(this::notifyTestRunSessionRemoved); + notifyTestRunSessionAdded(testRunSession); + } + + /** + * Imports a test run session from an URL + * + * @param url an URL to source file + * @param monitor a progress monitor object + * @return an {@link ITestRunSession} object instance + * + * @throws InvocationTargetException in case of problems during import operation + * @throws InterruptedException in case of import operation is interrupted + */ + public ITestRunSession importTestRunSession(String url, IProgressMonitor monitor) + throws InvocationTargetException, InterruptedException { + monitor.beginTask(ModelMessages.UnitTestModel_importing_from_url, IProgressMonitor.UNKNOWN); + final String trimmedUrl = url.trim().replaceAll("\r\n?|\n", ""); //$NON-NLS-1$ //$NON-NLS-2$ + final TestRunHandler handler = new TestRunHandler(monitor); + + final CoreException[] exception = { null }; + final TestRunSession[] session = { null }; + + Thread importThread = new Thread("UnitTest URL importer") { //$NON-NLS-1$ + @Override + public void run() { + try { + SAXParserFactory parserFactory = SAXParserFactory.newInstance(); +// parserFactory.setValidating(true); // TODO: add DTD and debug flag + SAXParser parser = parserFactory.newSAXParser(); + parser.parse(trimmedUrl, handler); + session[0] = handler.getTestRunSession(); + } catch (OperationCanceledException e) { + // canceled + } catch (ParserConfigurationException e) { + storeImportError(e); + } catch (SAXException e) { + storeImportError(e); + } catch (IOException e) { + storeImportError(e); + } catch (IllegalArgumentException e) { + // Bug in parser: can throw IAE even if URL is not null + storeImportError(e); + } + } + + private void storeImportError(Exception e) { + exception[0] = new CoreException(new org.eclipse.core.runtime.Status(IStatus.ERROR, + UnitTestPlugin.PLUGIN_ID, ModelMessages.UnitTestModel_could_not_import, e)); + } + }; + importThread.start(); + + while (session[0] == null && exception[0] == null && !monitor.isCanceled()) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // that's OK + } + } + if (session[0] == null) { + if (exception[0] != null) { + throw new InvocationTargetException(exception[0]); + } else { + importThread.interrupt(); // have to kill the thread since we don't control URLConnection and XML + // parsing + throw new InterruptedException(); + } + } + + addTestRunSession(session[0]); + monitor.done(); + return session[0]; + } + + /** + * Removes the given {@link TestRunSession} and notifies all registered + * {@link ITestRunSessionListener}s. + * + * @param testRunSession the session to remove + * @see org.eclipse.unittest.internal.model.UnitTestModel#addTestRunSession(TestRunSession) + */ + void removeTestRunSession(TestRunSession testRunSession) { + boolean existed; + synchronized (this) { + existed = fTestRunSessions.remove(testRunSession); + } + if (existed) { + notifyTestRunSessionRemoved(testRunSession); + } + } + + private void notifyTestRunSessionRemoved(TestRunSession testRunSession) { + for (ITestRunSessionListener listener : fTestRunSessionListeners) { + listener.sessionRemoved(testRunSession); + } + } + + private void notifyTestRunSessionAdded(ITestRunSession testRunSession) { + for (ITestRunSessionListener listener : fTestRunSessionListeners) { + listener.sessionAdded(testRunSession); + } + } + +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/BasicElementLabels.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/BasicElementLabels.java new file mode 100644 index 000000000..feab70e81 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/BasicElementLabels.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2008, 2009 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import java.io.File; + +import org.eclipse.osgi.util.TextProcessor; + +import org.eclipse.core.runtime.IPath; + +import org.eclipse.core.resources.IResource; + +/** + * A label provider for basic elements like paths. The label provider will make + * sure that the labels are correctly shown in RTL environments. + */ +public class BasicElementLabels { + + private BasicElementLabels() { + } + + /** + * Adds special marks so that that the given string is readable in a BIDI + * environment. + * + * @param string the string + * @param delimiters the additional delimiters + * @return the processed styled string + */ + private static String markLTR(String string, String delimiters) { + return TextProcessor.process(string, delimiters); + } + + /** + * Returns the label of a path. + * + * @param path the path + * @param isOSPath if <code>true</code>, the path represents an OS path, if + * <code>false</code> it is a workspace path. + * @return the label of the path to be used in the UI. + */ + public static String getPathLabel(IPath path, boolean isOSPath) { + String label; + if (isOSPath) { + label = path.toOSString(); + } else { + label = path.makeRelative().toString(); + } + return markLTR(label, "/\\:."); //$NON-NLS-1$ + } + + /** + * Returns the label of the path of a file. + * + * @param file the file + * @return the label of the file path to be used in the UI. + */ + public static String getPathLabel(File file) { + return markLTR(file.getAbsolutePath(), "/\\:."); //$NON-NLS-1$ + } + + /** + * Returns the label for a file pattern like '*.java' + * + * @param name the pattern + * @return the label of the pattern. + */ + public static String getFilePattern(String name) { + return markLTR(name, "*.?/\\:."); //$NON-NLS-1$ + } + + /** + * Returns the label for a URL, URI or URL part. Example is + * 'http://www.x.xom/s.html#1' + * + * @param name the URL string + * @return the label of the URL. + */ + public static String getURLPart(String name) { + return markLTR(name, ":@?-#/\\:."); //$NON-NLS-1$ + } + + /** + * Returns a label for a resource name. + * + * @param resource the resource + * @return the label of the resource name. + */ + public static String getResourceName(IResource resource) { + return markLTR(resource.getName(), ":."); //$NON-NLS-1$ + } + + /** + * Returns a label for a resource name. + * + * @param resourceName the resource name + * @return the label of the resource name. + */ + public static String getResourceName(String resourceName) { + return markLTR(resourceName, ":."); //$NON-NLS-1$ + } + + /** + * Returns a label for a version name. Example is '1.4.1' + * + * @param name the version string + * @return the version label + */ + public static String getVersionName(String name) { + return markLTR(name, ":."); //$NON-NLS-1$ + } + + /** + * Returns a label for Java element name. Example is 'new Test<? extends List>() + * { ...}'. This method should only be used for simple element names. Use + * JavaElementLabels to create a label from a Java element. + * + * @param name the Java element name. + * @return the label for the Java element + */ + public static String getJavaElementName(String name) { + return markLTR(name, "<>()?,{}.:"); //$NON-NLS-1$ + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CompareResultDialog.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CompareResultDialog.java new file mode 100644 index 000000000..7da6c656e --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CompareResultDialog.java @@ -0,0 +1,320 @@ +/******************************************************************************* + * Copyright (c) 2000, 2018 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import org.eclipse.unittest.internal.UnitTestPlugin; +import org.eclipse.unittest.internal.model.TestElement; +import org.eclipse.unittest.model.ITestElement; +import org.eclipse.unittest.model.ITestElement.FailureTrace; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.core.runtime.CoreException; + +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.dialogs.TrayDialog; + +import org.eclipse.jface.text.DocumentEvent; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITypedRegion; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.TextAttribute; +import org.eclipse.jface.text.TextPresentation; +import org.eclipse.jface.text.TextViewer; +import org.eclipse.jface.text.presentation.IPresentationDamager; +import org.eclipse.jface.text.presentation.IPresentationReconciler; +import org.eclipse.jface.text.presentation.IPresentationRepairer; +import org.eclipse.jface.text.presentation.PresentationReconciler; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.jface.text.source.SourceViewerConfiguration; + +import org.eclipse.ui.PlatformUI; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.CompareViewerPane; +import org.eclipse.compare.IEncodedStreamContentAccessor; +import org.eclipse.compare.ITypedElement; +import org.eclipse.compare.contentmergeviewer.TextMergeViewer; +import org.eclipse.compare.structuremergeviewer.DiffNode; + +/** + * A Compare result dialog + */ +public class CompareResultDialog extends TrayDialog { + private static final String PREFIX_SUFFIX_PROPERTY = UnitTestPlugin.PLUGIN_ID + ".CompareResultDialog.prefixSuffix"; //$NON-NLS-1$ + + private static class CompareResultMergeViewer extends TextMergeViewer { + private CompareResultMergeViewer(Composite parent, int style, CompareConfiguration configuration) { + super(parent, style, configuration); + } + + @Override + protected void createControls(Composite composite) { + super.createControls(composite); + PlatformUI.getWorkbench().getHelpSystem().setHelp(composite, IUnitTestHelpContextIds.RESULT_COMPARE_DIALOG); + } + +// protected void createToolItems(ToolBarManager tbm) { +// ResourceBundle bundle= CompareUI.getResourceBundle(); +// tbm.add(new IgnoreWhiteSpaceAction(bundle, getCompareConfiguration())); +// super.createToolItems(tbm); +// } + + @Override + protected void configureTextViewer(TextViewer textViewer) { + if (textViewer instanceof SourceViewer) { + int[] prefixSuffixOffsets = (int[]) getCompareConfiguration().getProperty(PREFIX_SUFFIX_PROPERTY); + ((SourceViewer) textViewer).configure(new CompareResultViewerConfiguration(prefixSuffixOffsets)); + } + } + } + + private static class CompareResultViewerConfiguration extends SourceViewerConfiguration { + private static class SimpleDamagerRepairer implements IPresentationDamager, IPresentationRepairer { + private IDocument fDocument; + private final int[] fPrefixSuffixOffsets2; + + public SimpleDamagerRepairer(int[] prefixSuffixOffsets) { + fPrefixSuffixOffsets2 = prefixSuffixOffsets; + } + + @Override + public void setDocument(IDocument document) { + fDocument = document; + } + + @Override + public IRegion getDamageRegion(ITypedRegion partition, DocumentEvent event, boolean changed) { + return new Region(0, fDocument.getLength()); + } + + @Override + public void createPresentation(TextPresentation presentation, ITypedRegion damage) { + presentation.setDefaultStyleRange(new StyleRange(0, fDocument.getLength(), null, null)); + int prefix = fPrefixSuffixOffsets2[0]; + int suffix = fPrefixSuffixOffsets2[1]; + TextAttribute attr = new TextAttribute(Display.getDefault().getSystemColor(SWT.COLOR_RED)); + presentation.addStyleRange(new StyleRange(prefix, fDocument.getLength() - suffix - prefix, + attr.getForeground(), attr.getBackground(), attr.getStyle())); + } + } + + private final int[] fPrefixSuffixOffsets; + + public CompareResultViewerConfiguration(int[] prefixSuffixOffsets) { + fPrefixSuffixOffsets = prefixSuffixOffsets; + } + + @Override + public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) { + PresentationReconciler reconciler = new PresentationReconciler(); + SimpleDamagerRepairer dr = new SimpleDamagerRepairer(fPrefixSuffixOffsets); + reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE); + reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE); + return reconciler; + } + } + + private static class CompareElement implements ITypedElement, IEncodedStreamContentAccessor { + private String fContent; + + public CompareElement(String content) { + fContent = content; + } + + @Override + public String getName() { + return "<no name>"; //$NON-NLS-1$ + } + + @Override + public Image getImage() { + return null; + } + + @Override + public String getType() { + return "txt"; //$NON-NLS-1$ + } + + @Override + public InputStream getContents() { + return new ByteArrayInputStream(fContent.getBytes(StandardCharsets.UTF_8)); + } + + @Override + public String getCharset() throws CoreException { + return "UTF-8"; //$NON-NLS-1$ + } + } + + private TextMergeViewer fViewer; + private FailureTrace trace; + private String fTestName; + + /** + * Lengths of common prefix and suffix. Note: this array is passed to the + * DamagerRepairer and the lengths are updated on content change. + */ + private final int[] fPrefixSuffix = new int[2]; + + private CompareViewerPane fCompareViewerPane; + + /** + * Constructs a compare results dialog + * + * @param parentShell a parent shell object + * @param element a {@link TestElement} object + */ + public CompareResultDialog(Shell parentShell, TestElement element) { + super(parentShell); + setShellStyle((getShellStyle() & ~SWT.APPLICATION_MODAL) | SWT.TOOL); + setFailedTest(element); + } + + @Override + protected boolean isResizable() { + return true; + } + + private void setFailedTest(TestElement failedTest) { + fTestName = failedTest.getTestName(); + trace = failedTest.getFailureTrace(); + computePrefixSuffix(); + } + + @Override + protected IDialogSettings getDialogBoundsSettings() { + return getDialogSettingsSection(getClass().getName()); + } + + /** + * Returns the section with the given name in this dialog settings. + * + * @param name the key + * @return {@link IDialogSettings} (the section), or <code>null</code> if none + */ + private IDialogSettings getDialogSettingsSection(String name) { + IDialogSettings dialogSettings = UnitTestPlugin.getDefault().getDialogSettings(); + IDialogSettings section = dialogSettings.getSection(name); + if (section == null) { + section = dialogSettings.addNewSection(name); + } + return section; + } + + private void computePrefixSuffix() { + String expected = trace.getExpected(); + String actual = trace.getActual(); + int end = Math.min(expected.length(), actual.length()); + int i = 0; + for (; i < end; i++) + if (expected.charAt(i) != actual.charAt(i)) + break; + fPrefixSuffix[0] = i; + + int j = expected.length() - 1; + int k = actual.length() - 1; + int l = 0; + for (; k >= i && j >= i; k--, j--) { + if (expected.charAt(j) != actual.charAt(k)) + break; + l++; + } + fPrefixSuffix[1] = l; + } + + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + newShell.setText(Messages.CompareResultDialog_title); + PlatformUI.getWorkbench().getHelpSystem().setHelp(newShell, IUnitTestHelpContextIds.RESULT_COMPARE_DIALOG); + } + + @Override + protected void createButtonsForButtonBar(Composite parent) { + createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true); + } + + @Override + protected Control createDialogArea(Composite parent) { + Composite composite = (Composite) super.createDialogArea(parent); + GridLayout layout = new GridLayout(); + layout.numColumns = 1; + composite.setLayout(layout); + + fCompareViewerPane = new CompareViewerPane(composite, SWT.BORDER | SWT.FLAT); + GridData data = new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL); + data.widthHint = convertWidthInCharsToPixels(120); + data.heightHint = convertHeightInCharsToPixels(13); + fCompareViewerPane.setLayoutData(data); + + Control previewer = createPreviewer(fCompareViewerPane); + fCompareViewerPane.setContent(previewer); + GridData gd = new GridData(GridData.FILL_BOTH); + previewer.setLayoutData(gd); + applyDialogFont(parent); + return composite; + } + + private Control createPreviewer(Composite parent) { + final CompareConfiguration compareConfiguration = new CompareConfiguration(); + compareConfiguration.setLeftLabel(Messages.CompareResultDialog_expectedLabel); + compareConfiguration.setLeftEditable(false); + compareConfiguration.setRightLabel(Messages.CompareResultDialog_actualLabel); + compareConfiguration.setRightEditable(false); + compareConfiguration.setProperty(CompareConfiguration.IGNORE_WHITESPACE, Boolean.FALSE); + compareConfiguration.setProperty(PREFIX_SUFFIX_PROPERTY, fPrefixSuffix); + + fViewer = new CompareResultMergeViewer(parent, SWT.NONE, compareConfiguration); + setCompareViewerInput(); + + Control control = fViewer.getControl(); + control.addDisposeListener(e -> compareConfiguration.dispose()); + return control; + } + + private void setCompareViewerInput() { + if (!fViewer.getControl().isDisposed()) { + fViewer.setInput( + new DiffNode(new CompareElement(trace.getExpected()), new CompareElement(trace.getActual()))); + fCompareViewerPane.setText(fTestName); + } + } + + /** + * Sets a failed {@link ITestElement} as input for the CompareResultDialog + * + * @param failedTest a failed test element + */ + public void setInput(TestElement failedTest) { + setFailedTest(failedTest); + setCompareViewerInput(); + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CompareResultsAction.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CompareResultsAction.java new file mode 100644 index 000000000..40082dc84 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CompareResultsAction.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2000, 2010 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import org.eclipse.unittest.internal.model.TestElement; +import org.eclipse.unittest.model.ITestElement; + +import org.eclipse.jface.action.Action; + +import org.eclipse.ui.PlatformUI; + +/** + * Action to enable/disable stack trace filtering. + */ +public class CompareResultsAction extends Action { + + private FailureTraceUIBlock fView; + private CompareResultDialog fOpenDialog; + + /** + * Constructs a compare result object + * + * @param view a {@link FailureTraceUIBlock} object + */ + public CompareResultsAction(FailureTraceUIBlock view) { + super(Messages.CompareResultsAction_label); + setDescription(Messages.CompareResultsAction_description); + setToolTipText(Messages.CompareResultsAction_tooltip); + + setDisabledImageDescriptor(Images.getImageDescriptor("dlcl16/compare.png")); //$NON-NLS-1$ + setHoverImageDescriptor(Images.getImageDescriptor("elcl16/compare.png")); //$NON-NLS-1$ + setImageDescriptor(Images.getImageDescriptor("elcl16/compare.png")); //$NON-NLS-1$ + PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IUnitTestHelpContextIds.ENABLEFILTER_ACTION); + fView = view; + } + + @Override + public void run() { + TestElement failedTest = fView.getFailedTest(); + if (fOpenDialog != null) { + fOpenDialog.setInput(failedTest); + fOpenDialog.getShell().setActive(); + + } else { + fOpenDialog = new CompareResultDialog(fView.getShell(), failedTest); + fOpenDialog.create(); + fOpenDialog.getShell().addDisposeListener(e -> fOpenDialog = null); + fOpenDialog.setBlockOnOpen(false); + fOpenDialog.open(); + } + } + + /** + * Updates the CompareResultDialog with a failed {@link ITestElement} as input + * + * @param failedTest a failed test element + */ + public void updateOpenDialog(TestElement failedTest) { + if (fOpenDialog != null) { + fOpenDialog.setInput(failedTest); + } + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CopyFailureListAction.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CopyFailureListAction.java new file mode 100644 index 000000000..68cf68bcc --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CopyFailureListAction.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2000, 2010 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import org.eclipse.unittest.internal.model.TestElement; +import org.eclipse.unittest.model.ITestElement.FailureTrace; + +import org.eclipse.swt.SWTError; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.dialogs.MessageDialog; + +import org.eclipse.ui.PlatformUI; + +/** + * Copies the names of the methods that failed and their traces to the + * clipboard. + */ +public class CopyFailureListAction extends Action { + + private final Clipboard fClipboard; + private final TestRunnerViewPart fRunner; + + /** + * Constructs a copy failure list action object + * + * @param runner a test runner view part object + * @param clipboard a clipboard object + */ + public CopyFailureListAction(TestRunnerViewPart runner, Clipboard clipboard) { + super(Messages.CopyFailureList_action_label); + fRunner = runner; + fClipboard = clipboard; + PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IUnitTestHelpContextIds.COPYFAILURELIST_ACTION); + } + + @Override + public void run() { + TextTransfer plainTextTransfer = TextTransfer.getInstance(); + + try { + fClipboard.setContents(new String[] { getAllFailureTraces() }, new Transfer[] { plainTextTransfer }); + } catch (SWTError e) { + if (e.code != DND.ERROR_CANNOT_SET_CLIPBOARD) + throw e; + if (MessageDialog.openQuestion(fRunner.getSite().getShell(), Messages.CopyFailureList_problem, + Messages.CopyFailureList_clipboard_busy)) + run(); + } + } + + /** + * Returns the failure trace lines as a string + * + * @return a failure traces string + */ + public String getAllFailureTraces() { + StringBuilder buf = new StringBuilder(); + String lineDelim = System.getProperty("line.separator", "\n"); //$NON-NLS-1$//$NON-NLS-2$ + for (TestElement failure : fRunner.getCurrentTestRunSession().getAllFailedTestElements()) { + buf.append(failure.getTestName()).append(lineDelim); + FailureTrace failureTrace = failure.getFailureTrace(); + String trace = failureTrace != null ? failureTrace.getTrace() : null; + if (trace != null) { + int start = 0; + while (start < trace.length()) { + int idx = trace.indexOf('\n', start); + if (idx != -1) { + String line = trace.substring(start, idx); + buf.append(line).append(lineDelim); + start = idx + 1; + } else { + start = Integer.MAX_VALUE; + } + } + } + } + return buf.toString(); + } + +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CounterPanel.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CounterPanel.java new file mode 100644 index 000000000..8e7959b99 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/CounterPanel.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (c) 2000, 2020 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import java.text.MessageFormat; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** + * A panel with counters for the number of Runs, Errors and Failures. + */ +public class CounterPanel extends Composite { + protected Text fNumberOfErrors; + protected Text fNumberOfFailures; + protected Text fNumberOfRuns; + protected Integer fTotal; + protected int fIgnoredCount; + protected int fAssumptionFailedCount; + + private final Image fErrorIcon = Images.createImage("ovr16/error_ovr.png"); //$NON-NLS-1$ + private final Image fFailureIcon = Images.createImage("ovr16/failed_ovr.png"); //$NON-NLS-1$ + + /** + * Constructs a CounterPanel object + * + * @param parent a parent composite + */ + public CounterPanel(Composite parent) { + super(parent, SWT.WRAP); + GridLayout gridLayout = new GridLayout(); + gridLayout.numColumns = 9; + gridLayout.makeColumnsEqualWidth = false; + gridLayout.marginWidth = 0; + setLayout(gridLayout); + + fNumberOfRuns = createLabel(Messages.CounterPanel_label_runs, null, " 0/0 "); //$NON-NLS-1$ + fNumberOfErrors = createLabel(Messages.CounterPanel_label_errors, fErrorIcon, " 0 "); //$NON-NLS-1$ + fNumberOfFailures = createLabel(Messages.CounterPanel_label_failures, fFailureIcon, " 0 "); //$NON-NLS-1$ + + addDisposeListener(e -> disposeIcons()); + } + + private void disposeIcons() { + fErrorIcon.dispose(); + fFailureIcon.dispose(); + } + + private Text createLabel(String name, Image image, String init) { + Label label = new Label(this, SWT.NONE); + if (image != null) { + image.setBackground(label.getBackground()); + label.setImage(image); + } + label.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING)); + + label = new Label(this, SWT.NONE); + label.setText(name); + label.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING)); + // label.setFont(JFaceResources.getBannerFont()); + + Text value = new Text(this, SWT.READ_ONLY); + value.setText(init); + fixReadonlyTextBackground(value); + value.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.HORIZONTAL_ALIGN_BEGINNING)); + return value; + } + + /** + * Resets the counters presented on the panel + */ + public void reset() { + fTotal = null; + setErrorValue(0); + setFailureValue(0); + setRunValue(0, 0, 0); + } + + /** + * Sets the total count value + * + * @param value total count value, can be <code>null</code> if unknown yet + */ + public void setTotal(Integer value) { + fTotal = value; + } + + /** + * Sets the run counter values + * + * @param value a run counter value + * @param ignoredCount an ignored tests counter value + * @param assumptionFailureCount a number of assumption failure counter value + */ + public void setRunValue(int value, int ignoredCount, int assumptionFailureCount) { + String runString; + String runStringTooltip; + String totalString = fTotal == null ? "?" : fTotal.toString(); //$NON-NLS-1$ + if (ignoredCount == 0 && assumptionFailureCount == 0) { + runString = MessageFormat.format(Messages.CounterPanel_runcount, Integer.toString(value), totalString); + runStringTooltip = runString; + } else if (ignoredCount != 0 && assumptionFailureCount == 0) { + runString = MessageFormat.format(Messages.CounterPanel_runcount_skipped, Integer.toString(value), + totalString, Integer.toString(ignoredCount)); + runStringTooltip = MessageFormat.format(Messages.CounterPanel_runcount_ignored, Integer.toString(value), + totalString, Integer.toString(ignoredCount)); + } else if (ignoredCount == 0 && assumptionFailureCount != 0) { + runString = MessageFormat.format(Messages.CounterPanel_runcount_skipped, Integer.toString(value), + totalString, Integer.toString(assumptionFailureCount)); + runStringTooltip = MessageFormat.format(Messages.CounterPanel_runcount_assumptionsFailed, + Integer.toString(value), totalString, Integer.toString(assumptionFailureCount)); + } else { + runString = MessageFormat.format(Messages.CounterPanel_runcount_skipped, Integer.toString(value), + totalString, Integer.toString(ignoredCount + assumptionFailureCount)); + runStringTooltip = MessageFormat.format(Messages.CounterPanel_runcount_ignored_assumptionsFailed, + Integer.toString(value), totalString, Integer.toString(ignoredCount), + Integer.toString(assumptionFailureCount)); + } + fNumberOfRuns.setText(runString); + fNumberOfRuns.setToolTipText(runStringTooltip); + + if (fIgnoredCount == 0 && ignoredCount > 0 || fIgnoredCount != 0 && ignoredCount == 0) { + layout(); + } else if (fAssumptionFailedCount == 0 && assumptionFailureCount > 0 + || fAssumptionFailedCount != 0 && assumptionFailureCount == 0) { + layout(); + } else { + fNumberOfRuns.redraw(); + redraw(); + } + fIgnoredCount = ignoredCount; + fAssumptionFailedCount = assumptionFailureCount; + } + + /** + * Sets an error counter value + * + * @param value am error counter value + */ + public void setErrorValue(int value) { + fNumberOfErrors.setText(Integer.toString(value)); + redraw(); + } + + /** + * Sets a failure counter value + * + * @param value a failure counter value + */ + public void setFailureValue(int value) { + fNumberOfFailures.setText(Integer.toString(value)); + redraw(); + } + + /** + * Fixes https://bugs.eclipse.org/71765 by setting the background color to + * {@code SWT.COLOR_WIDGET_BACKGROUND}. + * <p> + * Should be applied to all SWT.READ_ONLY Texts in dialogs (or at least those + * which don't have an SWT.BORDER). Search regex: + * {@code new Text\([^,]+,[^\)]+SWT\.READ_ONLY} + * + * @param textField the text field + */ + public static void fixReadonlyTextBackground(Text textField) { + textField.setBackground(textField.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND)); + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/EnableStackFilterAction.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/EnableStackFilterAction.java new file mode 100644 index 000000000..df2ecc85e --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/EnableStackFilterAction.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2000, 2010 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import org.eclipse.unittest.internal.UnitTestPreferencesConstants; + +import org.eclipse.jface.action.Action; + +import org.eclipse.ui.PlatformUI; + +/** + * Action to enable/disable stack trace filtering. + */ +public class EnableStackFilterAction extends Action { + + private FailureTraceUIBlock fView; + + /** + * Constructs an enable stack filter action object + * + * @param view a {@link FailureTraceUIBlock} object + */ + public EnableStackFilterAction(FailureTraceUIBlock view) { + super(Messages.EnableStackFilterAction_action_label); + setDescription(Messages.EnableStackFilterAction_action_description); + setToolTipText(Messages.EnableStackFilterAction_action_tooltip); + + setDisabledImageDescriptor(Images.getImageDescriptor("dlcl16/cfilter.png")); //$NON-NLS-1$ + setHoverImageDescriptor(Images.getImageDescriptor("elcl16/cfilter.png")); //$NON-NLS-1$ + setImageDescriptor(Images.getImageDescriptor("elcl16/cfilter.png")); //$NON-NLS-1$ + PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IUnitTestHelpContextIds.ENABLEFILTER_ACTION); + + fView = view; + setChecked(UnitTestPreferencesConstants.getFilterStack()); + } + + @Override + public void run() { + UnitTestPreferencesConstants.setFilterStack(isChecked()); + fView.refresh(); + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/FailureTableDisplay.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/FailureTableDisplay.java new file mode 100644 index 000000000..9d0a27adc --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/FailureTableDisplay.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +/** + * + */ +package org.eclipse.unittest.internal.ui; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableItem; + +/** + * A failure table display + */ +public class FailureTableDisplay implements ITraceDisplay { + private final Table fTable; + + private final Image fExceptionIcon = Images.createImage("obj16/exc_catch.png"); //$NON-NLS-1$ + + private final Image fStackIcon = Images.createImage("obj16/stkfrm_obj.png"); //$NON-NLS-1$ + + /** + * Constructs a failure table display + * + * @param table a table object + */ + public FailureTableDisplay(Table table) { + fTable = table; + fTable.getParent().addDisposeListener(e -> disposeIcons()); + } + + @Override + public void addTraceLine(int lineType, String label) { + TableItem tableItem = newTableItem(); + switch (lineType) { + case TextualTrace.LINE_TYPE_EXCEPTION: + tableItem.setImage(fExceptionIcon); + break; + case TextualTrace.LINE_TYPE_STACKFRAME: + tableItem.setImage(fStackIcon); + break; + case TextualTrace.LINE_TYPE_NORMAL: + default: + break; + } + tableItem.setText(label); + } + + /** + * Returns an exception icon image + * + * @return an exception icon image + */ + public Image getExceptionIcon() { + return fExceptionIcon; + } + + /** + * Returns a stack icon image + * + * @return a stack icon image + */ + public Image getStackIcon() { + return fStackIcon; + } + + /** + * Returns a table object + * + * @return a table object + */ + public Table getTable() { + return fTable; + } + + private void disposeIcons() { + if (fExceptionIcon != null && !fExceptionIcon.isDisposed()) + fExceptionIcon.dispose(); + if (fStackIcon != null && !fStackIcon.isDisposed()) + fStackIcon.dispose(); + } + + /** + * Returns a new table item + * + * @return a table item object instance + */ + public TableItem newTableItem() { + return new TableItem(fTable, SWT.NONE); + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/FailureTraceUIBlock.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/FailureTraceUIBlock.java new file mode 100644 index 000000000..9800f4c60 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/FailureTraceUIBlock.java @@ -0,0 +1,285 @@ +/******************************************************************************* + * Copyright (c) 2000, 2018 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import java.util.Collection; +import java.util.Collections; +import java.util.Objects; + +import org.eclipse.unittest.internal.UnitTestPreferencesConstants; +import org.eclipse.unittest.internal.model.TestElement; +import org.eclipse.unittest.internal.model.TestRunSession; +import org.eclipse.unittest.model.ITestRunSession; +import org.eclipse.unittest.ui.ITestViewSupport; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableItem; +import org.eclipse.swt.widgets.ToolBar; + +import org.eclipse.core.text.StringMatcher; + +import org.eclipse.core.runtime.Assert; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.ToolBarManager; +import org.eclipse.jface.util.OpenStrategy; + +/** + * A pane that shows a stack trace of a failed test. + * + * @noextend This class is not intended to be subclassed by clients. + * @noinstantiate This class is not intended to be instantiated by clients. + */ +public class FailureTraceUIBlock implements IMenuListener { + + private static final int MAX_LABEL_LENGTH = 256; + private Table fTable; + private TestRunnerViewPart fTestRunner; + private String fInputTrace; + private final Clipboard fClipboard; + private TestElement fFailure; + private CompareResultsAction fCompareAction; + private final FailureTableDisplay fFailureTableDisplay; + private ShowStackTraceInConsoleViewAction fShowTraceInConsoleAction; + + /** + * Constructs a {@link FailureTraceUIBlock} object + * + * @param parent a parent composite + * @param clipboard a {@link Clipboard} instance + * @param testRunner a Test Runner view part + * @param toolBar a {@link ToolBar} instance + */ + public FailureTraceUIBlock(Composite parent, Clipboard clipboard, TestRunnerViewPart testRunner, ToolBar toolBar) { + Assert.isNotNull(clipboard); + + // fill the failure trace viewer toolbar + ToolBarManager failureToolBarmanager = new ToolBarManager(toolBar); + fShowTraceInConsoleAction = new ShowStackTraceInConsoleViewAction(); + fShowTraceInConsoleAction.setDelegate(null); + fShowTraceInConsoleAction.setEnabled(false); + failureToolBarmanager.add(fShowTraceInConsoleAction); + failureToolBarmanager.add(new EnableStackFilterAction(this)); + fCompareAction = new CompareResultsAction(this); + fCompareAction.setEnabled(false); + failureToolBarmanager.add(fCompareAction); + failureToolBarmanager.update(true); + fTable = new Table(parent, SWT.SINGLE | SWT.V_SCROLL | SWT.H_SCROLL); + fTestRunner = testRunner; + fClipboard = clipboard; + + OpenStrategy handler = new OpenStrategy(fTable); + handler.addOpenListener(e -> { + if (fTable.getSelectionIndex() == 0 && fFailure.getFailureTrace() != null + && fFailure.getFailureTrace().isComparisonFailure()) { + fCompareAction.run(); + } + if (fTable.getSelection().length != 0) { + IAction a = createOpenEditorAction(getSelectedText()); + if (a != null) + a.run(); + } + }); + + initMenu(); + + fFailureTableDisplay = new FailureTableDisplay(fTable); + } + + private void initMenu() { + MenuManager menuMgr = new MenuManager(); + menuMgr.setRemoveAllWhenShown(true); + menuMgr.addMenuListener(this); + Menu menu = menuMgr.createContextMenu(fTable); + fTable.setMenu(menu); + } + + @Override + public void menuAboutToShow(IMenuManager manager) { + if (fTable.getSelectionCount() > 0) { + IAction a = createOpenEditorAction(getSelectedText()); + if (a != null) + manager.add(a); + manager.add(new UnitTestCopyAction(FailureTraceUIBlock.this, fClipboard)); + } + // fix for bug 68058 + if (fFailure != null && fFailure.getFailureTrace() != null + && fFailure.getFailureTrace().isComparisonFailure()) { + manager.add(fCompareAction); + } + } + + /** + * Returns the current trace + * + * @return a current trace or <code>null</code> + */ + public String getTrace() { + return fInputTrace; + } + + private String getSelectedText() { + return fTable.getSelection()[0].getText(); + } + + private IAction createOpenEditorAction(String traceLine) { + return fFailure.getTestRunSession().getTestViewSupport() + .createOpenEditorAction(fTestRunner.getSite().getShell(), fFailure, traceLine); + } + + /** + * Returns the composite used to present the trace + * + * @return The composite + */ + public Composite getComposite() { + return fTable; + } + + /** + * Refresh the table from the trace. + */ + public void refresh() { + updateTable(fInputTrace); + } + + /** + * Shows a TestFailure + * + * @param test the failed test + */ + public void showFailure(TestElement test) { + fFailure = test; + String trace = ""; //$NON-NLS-1$ + updateActions(test); + updateEnablement(test); + if (test != null && test.getFailureTrace() != null) { + trace = test.getFailureTrace().getTrace(); + } + if (Objects.equals(fInputTrace, trace)) { + return; + } + fInputTrace = trace; + updateTable(trace); + } + + private void updateActions(TestElement test) { + ITestViewSupport testViewSupport = test != null ? test.getTestRunSession().getTestViewSupport() : null; + fShowTraceInConsoleAction.setDelegate(testViewSupport != null && test.getFailureTrace() != null + ? testViewSupport.createShowStackTraceInConsoleViewActionDelegate(test) + : null); + } + + private void updateEnablement(TestElement test) { + boolean enableCompare = test != null && test.getFailureTrace() != null + && test.getFailureTrace().isComparisonFailure(); + fCompareAction.setEnabled(enableCompare); + if (enableCompare) { + fCompareAction.updateOpenDialog(test); + } + + boolean enableShowTraceInConsole = test != null && test.getFailureTrace() != null; + fShowTraceInConsoleAction.setEnabled(enableShowTraceInConsole); + } + + private void updateTable(String trace) { + if (trace == null || trace.trim().isEmpty()) { + clear(); + return; + } + trace = trace.trim(); + fTable.setRedraw(false); + fTable.removeAll(); + new TextualTrace(trace, getFilterPatterns()).display(fFailureTableDisplay, MAX_LABEL_LENGTH); + fTable.setRedraw(true); + } + + private Collection<StringMatcher> getFilterPatterns() { + if (UnitTestPreferencesConstants.getFilterStack()) + return getFilterPatterns(fFailure.getTestRunSession()); + return Collections.emptySet(); + } + + /** + * Returns an array of Filter patterns for Stacktraces/Error messages + * + * @param session a {@link ITestRunSession} to ask the filter pattern for + * @return an array of filter patterns + */ + public Collection<StringMatcher> getFilterPatterns(ITestRunSession session) { + if (session == null) { + return Collections.emptySet(); + } + ITestViewSupport viewSupport = ((TestRunSession) session).getTestViewSupport(); + if (viewSupport != null) { + Collection<StringMatcher> res = viewSupport.getTraceExclusionFilterPatterns(); + if (res != null) { + return res; + } + } + return Collections.emptySet(); + } + + /** + * Shows other information than a stack trace. + * + * @param text the informational message to be shown + */ + public void setInformation(String text) { + clear(); + TableItem tableItem = fFailureTableDisplay.newTableItem(); + tableItem.setText(text); + } + + /** + * Clears the non-stack trace info + */ + public void clear() { + fTable.removeAll(); + fInputTrace = null; + } + + /** + * Returns a failed test element + * + * @return a failed test element + */ + public TestElement getFailedTest() { + return fFailure; + } + + /** + * Returns a shell object + * + * @return a shell object + */ + public Shell getShell() { + return fTable.getShell(); + } + + /** + * Disposes the Failure trace UI Block + */ + public void dispose() { + // Nothing to dispose + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ITraceDisplay.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ITraceDisplay.java new file mode 100644 index 000000000..581ac594a --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ITraceDisplay.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2005, 2006 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +/** + * A trace display interface + */ +public interface ITraceDisplay { + + /** + * Adds a trace text line + * + * @param lineType a type of trace line + * @param label a trace line text + */ + void addTraceLine(int lineType, String label); +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/IUnitTestHelpContextIds.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/IUnitTestHelpContextIds.java new file mode 100644 index 000000000..b0d4e5d37 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/IUnitTestHelpContextIds.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import org.eclipse.unittest.internal.UnitTestPlugin; + +/** + * Help context ids for the Unit Test UI. + */ +public interface IUnitTestHelpContextIds { + String PREFIX = UnitTestPlugin.PLUGIN_ID + '.'; + + // Actions + String COPYTRACE_ACTION = PREFIX + "copy_trace_action_context"; //$NON-NLS-1$ + String COPYFAILURELIST_ACTION = PREFIX + "copy_failure_list_action_context"; //$NON-NLS-1$ + String ENABLEFILTER_ACTION = PREFIX + "enable_filter_action_context"; //$NON-NLS-1$ + String OPENEDITORATLINE_ACTION = PREFIX + "open_editor_atline_action_context"; //$NON-NLS-1$ + String OPENTEST_ACTION = PREFIX + "open_test_action_context"; //$NON-NLS-1$ + String RERUN_ACTION = PREFIX + "rerun_test_action_context"; //$NON-NLS-1$ + String GOTO_REFERENCED_TEST_ACTION_CONTEXT = PREFIX + "goto_referenced_test_action_context"; //$NON-NLS-1$ + String OUTPUT_SCROLL_LOCK_ACTION = PREFIX + "scroll_lock"; //$NON-NLS-1$ + + // view parts + String RESULTS_VIEW = PREFIX + "results_view_context"; //$NON-NLS-1$ + String RESULTS_VIEW_TOGGLE_ORIENTATION_ACTION = PREFIX + "results_view_toggle_call_mode_action_context"; //$NON-NLS-1$ + + // Dialogs + String TEST_SELECTION_DIALOG = PREFIX + "test_selection_context"; //$NON-NLS-1$ + String RESULT_COMPARE_DIALOG = PREFIX + "result_compare_context"; //$NON-NLS-1$ + +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Images.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Images.java new file mode 100644 index 000000000..6c7423b71 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Images.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2000, 2020 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import java.net.URL; + +import org.osgi.framework.Bundle; + +import org.eclipse.unittest.internal.UnitTestPlugin; + +import org.eclipse.swt.graphics.Image; + +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.resource.ImageDescriptor; + +/** + * Image related utilities + */ +public class Images { + + private static final IPath ICONS_PATH = new Path("$nl$/icons/full"); //$NON-NLS-1$ + + /** + * Create an {@link ImageDescriptor} from a given path + * + * @param relativePath relative path to the image + * @return an {@link ImageDescriptor}, or <code>null</code> iff there's no image + * at the given location and <code>useMissingImageDescriptor</code> is + * <code>true</code> + */ + public static ImageDescriptor getImageDescriptor(String relativePath) { + IPath path = ICONS_PATH.append(relativePath); + return createImageDescriptor(UnitTestPlugin.getDefault().getBundle(), path, true); + } + + /** + * Creates an {@link Image} from a given path + * + * @param path path to the image + * @return a new image or <code>null</code> if the image could not be created + */ + public static Image createImage(String path) { + return getImageDescriptor(path).createImage(); + } + + /** + * Sets the three image descriptors for enabled, disabled, and hovered to an + * action. The actions are retrieved from the *lcl16 folders. + * + * @param action the action + * @param iconName the icon name + */ + public static void setLocalImageDescriptors(IAction action, String iconName) { + setImageDescriptors(action, "lcl16", iconName); //$NON-NLS-1$ + } + + private static void setImageDescriptors(IAction action, String type, String relPath) { + ImageDescriptor id = createImageDescriptor("d" + type, relPath, false); //$NON-NLS-1$ + if (id != null) + action.setDisabledImageDescriptor(id); + + ImageDescriptor descriptor = createImageDescriptor("e" + type, relPath, true); //$NON-NLS-1$ + action.setHoverImageDescriptor(descriptor); + action.setImageDescriptor(descriptor); + } + + /* + * Creates an image descriptor for the given prefix and name in the JDT UI + * bundle. The path can contain variables like $NL$. If no image could be found, + * <code>useMissingImageDescriptor</code> decides if either the 'missing image + * descriptor' is returned or <code>null</code>. or <code>null</code>. + */ + private static ImageDescriptor createImageDescriptor(String pathPrefix, String imageName, + boolean useMissingImageDescriptor) { + IPath path = ICONS_PATH.append(pathPrefix).append(imageName); + return createImageDescriptor(UnitTestPlugin.getDefault().getBundle(), path, useMissingImageDescriptor); + } + + /** + * Creates an image descriptor for the given path in a bundle. The path can + * contain variables like $NL$. If no image could be found, + * <code>useMissingImageDescriptor</code> decides if either the 'missing image + * descriptor' is returned or <code>null</code>. + * + * @param bundle a bundle + * @param path path in the bundle + * @param useMissingImageDescriptor if <code>true</code>, returns the shared + * image descriptor for a missing image. + * Otherwise, returns <code>null</code> if the + * image could not be found + * @return an {@link ImageDescriptor}, or <code>null</code> iff there's no image + * at the given location and <code>useMissingImageDescriptor</code> is + * <code>true</code> + */ + private static ImageDescriptor createImageDescriptor(Bundle bundle, IPath path, boolean useMissingImageDescriptor) { + URL url = FileLocator.find(bundle, path, null); + if (url != null) { + return ImageDescriptor.createFromURL(url); + } + if (useMissingImageDescriptor) { + return ImageDescriptor.getMissingImageDescriptor(); + } + return null; + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Messages.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Messages.java new file mode 100644 index 000000000..3d4bcad5f --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Messages.java @@ -0,0 +1,130 @@ +/******************************************************************************* + * Copyright (c) 2000, 2017 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import org.eclipse.osgi.util.NLS; + +/** + * Unit Test View UI Messages + */ +public final class Messages extends NLS { + + static { + NLS.initializeMessages(Messages.class.getName(), Messages.class); + } + + private Messages() { + // Do not instantiate + } + + public static String CompareResultDialog_actualLabel; + public static String CompareResultDialog_expectedLabel; + public static String CompareResultDialog_title; + public static String CompareResultsAction_description; + public static String CompareResultsAction_label; + public static String CompareResultsAction_tooltip; + + public static String CopyFailureList_action_label; + public static String CopyFailureList_clipboard_busy; + public static String CopyFailureList_problem; + + public static String CopyTrace_action_label; + public static String CopyTraceAction_clipboard_busy; + public static String CopyTraceAction_problem; + + public static String CounterPanel_label_errors; + public static String CounterPanel_label_failures; + public static String CounterPanel_label_runs; + public static String CounterPanel_runcount; + public static String CounterPanel_runcount_assumptionsFailed; + public static String CounterPanel_runcount_ignored; + public static String CounterPanel_runcount_skipped; + public static String CounterPanel_runcount_ignored_assumptionsFailed; + + public static String EnableStackFilterAction_action_description; + public static String EnableStackFilterAction_action_label; + public static String EnableStackFilterAction_action_tooltip; + + public static String ExpandAllAction_text; + public static String ExpandAllAction_tooltip; + + public static String CollapseAllAction_text; + public static String CollapseAllAction_tooltip; + + public static String RerunAction_label_debug; + public static String RerunAction_label_run; + public static String RerunAction_label_rerun; + + public static String ScrollLockAction_action_label; + public static String ScrollLockAction_action_tooltip; + + public static String ShowNextFailureAction_label; + public static String ShowNextFailureAction_tooltip; + + public static String ShowPreviousFailureAction_label; + public static String ShowPreviousFailureAction_tooltip; + + public static String ShowStackTraceInConsoleViewAction_description; + public static String ShowStackTraceInConsoleViewAction_label; + public static String ShowStackTraceInConsoleViewAction_tooltip; + + public static String TestRunnerViewPart_activate_on_failure_only; + public static String TestRunnerViewPart_cannotrerun_title; + public static String TestRunnerViewPart_cannotrerurn_message; + public static String TestRunnerViewPart_configName; + public static String TestRunnerViewPart__error_cannotrun; + public static String TestRunnerViewPart_error_cannotrerun; + public static String TestRunnerViewPart_error_no_tests_found; + + public static String TestRunnerViewPart_jobName; + + public static String TestRunnerViewPart_label_failure; + public static String TestRunnerViewPart_Launching; + public static String TestRunnerViewPart_message_finish; + public static String TestRunnerViewPart_message_stopped; + public static String TestRunnerViewPart_message_terminated; + public static String TestRunnerViewPart_rerunaction_label; + public static String TestRunnerViewPart_rerunaction_tooltip; + public static String TestRunnerViewPart_rerunfailuresaction_label; + public static String TestRunnerViewPart_rerunfailuresaction_tooltip; + public static String TestRunnerViewPart_rerunFailedFirstLaunchConfigName; + public static String TestRunnerViewPart_stopaction_text; + public static String TestRunnerViewPart_stopaction_tooltip; + public static String TestRunnerViewPart_terminate_message; + public static String TestRunnerViewPart_terminate_title; + public static String TestRunnerViewPart_toggle_automatic_label; + public static String TestRunnerViewPart_toggle_horizontal_label; + public static String TestRunnerViewPart_toggle_vertical_label; + public static String TestRunnerViewPart_titleToolTip; + public static String TestRunnerViewPart_wrapperJobName; + public static String TestRunnerViewPart_show_execution_time; + public static String TestRunnerViewPart_show_failures_only; + public static String TestRunnerViewPart_show_ignored_only; + + public static String TestRunnerViewPart_hierarchical_layout; + public static String TestSessionLabelProvider_testName_elapsedTimeInSeconds; + public static String TestSessionLabelProvider_testName_RunnerVersion; + + public static String TestSessionLabelProvider_testMethodName_className; + + public static String TestRunnerViewPart_message_stopping; + public static String TestRunnerViewPart_PasteAction_cannotpaste_message; + public static String TestRunnerViewPart_PasteAction_cannotpaste_title; + public static String TestRunnerViewPart_PasteAction_label; + public static String TestRunnerViewPart_layout_menu; + public static String TestRunnerViewPart_editLaunchConfiguration; + public static String TestRunnerViewPart_sortAlphabetical; + public static String TestRunnerViewPart_sortRunner; + public static String TestRunnerViewPart_sort; +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Messages.properties b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Messages.properties new file mode 100644 index 000000000..701a41a95 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/Messages.properties @@ -0,0 +1,109 @@ +############################################################################### +# Copyright (c) 2000, 2017 IBM Corporation and others. +# +# This program and the accompanying materials +# are made available under the terms of the Eclipse Public License 2.0 +# which accompanies this distribution, and is available at +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# IBM Corporation - initial API and implementation +############################################################################### +CopyTrace_action_label=Copy Trace +CopyTraceAction_problem=Problem Copying to Clipboard +CopyTraceAction_clipboard_busy=There was a problem when accessing the system clipboard. Retry? + +CopyFailureList_action_label=Copy Failure List +CopyFailureList_problem=Problem Copying Failure List to Clipboard +CopyFailureList_clipboard_busy=There was a problem when accessing the system clipboard. Retry? + +CounterPanel_label_runs=Runs: +CounterPanel_label_errors=Errors: +CounterPanel_label_failures=Failures: +CounterPanel_runcount= {0}/{1} +CounterPanel_runcount_skipped= {0}/{1} ({2} skipped) +CounterPanel_runcount_ignored= {0}/{1} ({2} disabled) +CounterPanel_runcount_assumptionsFailed= {0}/{1} ({2} assumption failures) +CounterPanel_runcount_ignored_assumptionsFailed= {0}/{1} ({2} disabled, {3} assumption failures) + +EnableStackFilterAction_action_label=Filter +EnableStackFilterAction_action_description=Filter the stack trace +EnableStackFilterAction_action_tooltip=Filter Stack Trace + +ScrollLockAction_action_label=Scroll Lock +ScrollLockAction_action_tooltip=Scroll Lock + +TestRunnerViewPart_jobName=Update Unit Test +TestRunnerViewPart_wrapperJobName=Unit Test Starter Job +TestRunnerViewPart_stopaction_text=Stop Unit Test +TestRunnerViewPart_stopaction_tooltip=Stop Unit Test Run +TestRunnerViewPart_show_execution_time=Show Execution &Time +TestRunnerViewPart_show_failures_only=Show &Failures Only +TestRunnerViewPart_show_ignored_only=Show &Skipped Tests Only +TestRunnerViewPart_rerunaction_label=Rerun all tests +TestRunnerViewPart_rerunaction_tooltip=Rerun all tests +TestRunnerViewPart_hierarchical_layout=Show Tests in &Hierarchy +TestRunnerViewPart_rerunfailuresaction_label=Rerun Failed Tests +TestRunnerViewPart_rerunfailuresaction_tooltip=Rerun Failed Tests +TestRunnerViewPart_rerunFailedFirstLaunchConfigName={0} (Failed Tests) +TestRunnerViewPart_error_cannotrerun=Could not rerun test +TestRunnerViewPart_error_no_tests_found=No tests found with test runner ''{0}''. +TestRunnerViewPart_message_terminated=Terminated +TestRunnerViewPart_cannotrerun_title=Rerun Test +TestRunnerViewPart_cannotrerurn_message=To rerun tests they must be launched under the debugger\nand \'Keep Unit Test running\' must be set in the launch configuration. +TestRunnerViewPart_label_failure=Failure Trace +TestRunnerViewPart_message_finish= Finished after {0} +TestRunnerViewPart_message_stopped= Stopped +TestRunnerViewPart_message_stopping=Stopping... +TestRunnerViewPart_configName=Rerun {0} +TestRunnerViewPart__error_cannotrun=Could not run test +TestRunnerViewPart_layout_menu=&Layout +TestRunnerViewPart_Launching=Launching {0}... +TestRunnerViewPart_toggle_automatic_label=&Automatic +TestRunnerViewPart_toggle_horizontal_label=&Horizontal +TestRunnerViewPart_toggle_vertical_label=&Vertical +TestRunnerViewPart_sortAlphabetical=&Alphabetical +TestRunnerViewPart_sortRunner=&Runner natural order +TestRunnerViewPart_sort=Sort +TestRunnerViewPart_activate_on_failure_only=Activate on &Error/Failure Only +TestRunnerViewPart_PasteAction_cannotpaste_message=Cannot import test results from the clipboard. Please copy a valid URL first. +TestRunnerViewPart_PasteAction_cannotpaste_title=Paste +TestRunnerViewPart_PasteAction_label=Import &URL from Clipboard +TestRunnerViewPart_terminate_title=Rerun Test +TestRunnerViewPart_terminate_message=Terminate currently running tests? +TestRunnerViewPart_editLaunchConfiguration=Edit Launch Configuration + +# The first parameter is the test name and the second is the Test Kind name +TestRunnerViewPart_titleToolTip={0} [Runner: {1}] +TestSessionLabelProvider_testName_elapsedTimeInSeconds={0} \u23F1\uFE0F{1}s +TestSessionLabelProvider_testName_RunnerVersion={0} [Runner: {1}] + +TestSessionLabelProvider_testMethodName_className={0} - {1} + +ShowNextFailureAction_label=Next Failure +ShowNextFailureAction_tooltip=Next Failed Test +ShowPreviousFailureAction_label=Previous Failure +ShowPreviousFailureAction_tooltip=Previous Failed Test +ShowStackTraceInConsoleViewAction_description=Show the failure stack trace in Console view +ShowStackTraceInConsoleViewAction_label=Show Stack Trace in Console View +ShowStackTraceInConsoleViewAction_tooltip=Show Stack Trace in Console View + +ExpandAllAction_text=Expand All +ExpandAllAction_tooltip=Expand All Nodes + +CollapseAllAction_text=Collapse All +CollapseAllAction_tooltip=Collapse All Nodes + +CompareResultsAction_label=Compare Result +CompareResultsAction_description=Compare the actual and expected test result +CompareResultsAction_tooltip=Compare Actual With Expected Test Result + +CompareResultDialog_title=Result Comparison +CompareResultDialog_expectedLabel=Expected +CompareResultDialog_actualLabel=Actual + +RerunAction_label_rerun=Rerun ({0}) +RerunAction_label_run=&Run +RerunAction_label_debug=&Debug diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ProgressIcons.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ProgressIcons.java new file mode 100644 index 000000000..4cb6c1661 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ProgressIcons.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.RGB; + +/** + * Manages a set of images that can show progress in the image itself. + */ +public class ProgressIcons { + private final ImageData initialImageData; + + private static final class ProgressIconKey { + private final int pixels; + private final RGB color; + + public ProgressIconKey(int pixels, RGB color) { + this.pixels = pixels; + this.color = color; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ProgressIconKey)) { + return false; + } + ProgressIconKey other = (ProgressIconKey) obj; + return this.pixels == other.pixels && Objects.equals(this.color, other.color); + } + + @Override + public int hashCode() { + return Objects.hash(Integer.valueOf(pixels), color); + } + } + + private final Map<ProgressIconKey, Image> progressIcons = new HashMap<>(); + private Device display; + + /** + * Constructs a progress icons object + * + * @param sourceImage a source image object + */ + public ProgressIcons(Image sourceImage) { + this.initialImageData = sourceImage.getImageData(); + this.display = sourceImage.getDevice(); + } + + /** + * Disposes a progress icons object + */ + public void dispose() { + this.progressIcons.values().forEach(Image::dispose); + this.progressIcons.clear(); + } + + /** + * Returns an image added with counters + * + * @param current a current test run number + * @param total a total test count + * @param hasFailures a flag indicating if a test has failures + * @return an image object instance + */ + public Image getImage(int current, Integer total, boolean hasFailures) { + int totalAsInt = total != null ? total.intValue() : current + 1; + int pixelsToDraw = initialImageData.width * current / totalAsInt; + RGB color = display.getSystemColor(hasFailures ? SWT.COLOR_RED : SWT.COLOR_GREEN).getRGB(); + ProgressIconKey key = new ProgressIconKey(pixelsToDraw, color); + return progressIcons.computeIfAbsent(key, progressKey -> { + ImageData imageData = (ImageData) initialImageData.clone(); + int pixelColorCode = imageData.palette.getPixel(color); + for (int line = 4 * imageData.height / 5; line < imageData.height; line++) { + for (int column = 0; column < pixelsToDraw && column < imageData.width; column++) { + imageData.setAlpha(column, line, 255); + imageData.setPixel(column, line, pixelColorCode); + } + } + return new Image(display, imageData); + }); + } + +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/RerunAction.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/RerunAction.java new file mode 100644 index 000000000..64c8ce566 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/RerunAction.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2000, 2017 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import org.eclipse.osgi.util.NLS; +import org.eclipse.unittest.internal.UnitTestPlugin; + +import org.eclipse.core.runtime.CoreException; + +import org.eclipse.jface.action.Action; + +import org.eclipse.ui.PlatformUI; + +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunchConfiguration; + +/** + * Requests to rerun a test. + */ +public class RerunAction extends Action { + + private ILaunchConfiguration fLaunchConfiguration; + private String fLaunchMode; + + /** + * Constructs a rerun action + * + * @param launchConfiguration a launch configuration object + * @param launchMode a launch mode + */ + public RerunAction(ILaunchConfiguration launchConfiguration, String launchMode) { + super(NLS.bind(Messages.RerunAction_label_rerun, + DebugPlugin.getDefault().getLaunchManager().getLaunchMode(launchMode).getLabel())); + fLaunchConfiguration = launchConfiguration; + fLaunchMode = launchMode; + PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IUnitTestHelpContextIds.RERUN_ACTION); + } + + @Override + public void run() { + try { + DebugPlugin.getDefault().getLaunchManager().addLaunch(fLaunchConfiguration.launch(fLaunchMode, null)); + } catch (CoreException e) { + UnitTestPlugin.log(e); + } + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ScrollLockAction.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ScrollLockAction.java new file mode 100644 index 000000000..1f0eeca17 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ScrollLockAction.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2000, 2010 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import org.eclipse.jface.action.Action; + +import org.eclipse.ui.PlatformUI; + +/** + * Toggles console auto-scroll + */ +public class ScrollLockAction extends Action { + + private TestRunnerViewPart fRunnerViewPart; + + /** + * Constructs a scroll lock toggle action + * + * @param viewer a test runner viewer part object + */ + public ScrollLockAction(TestRunnerViewPart viewer) { + super(Messages.ScrollLockAction_action_label); + fRunnerViewPart = viewer; + setToolTipText(Messages.ScrollLockAction_action_tooltip); + setDisabledImageDescriptor(Images.getImageDescriptor("dlcl16/lock.png")); //$NON-NLS-1$ + setHoverImageDescriptor(Images.getImageDescriptor("elcl16/lock.png")); //$NON-NLS-1$ + setImageDescriptor(Images.getImageDescriptor("elcl16/lock.png")); //$NON-NLS-1$ + PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IUnitTestHelpContextIds.OUTPUT_SCROLL_LOCK_ACTION); + setChecked(false); + } + + @Override + public void run() { + fRunnerViewPart.setAutoScroll(!isChecked()); + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/SelectionProviderMediator.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/SelectionProviderMediator.java new file mode 100644 index 000000000..911df8096 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/SelectionProviderMediator.java @@ -0,0 +1,198 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Widget; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.ListenerList; + +import org.eclipse.jface.viewers.IPostSelectionProvider; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.StructuredViewer; + +/** + * A selection provider for view parts with more that one viewer. Tracks the + * focus of the viewers to provide the correct selection. + */ +public class SelectionProviderMediator implements IPostSelectionProvider { + + private class InternalListener implements ISelectionChangedListener, FocusListener { + @Override + public void selectionChanged(SelectionChangedEvent event) { + doSelectionChanged(event); + } + + @Override + public void focusGained(FocusEvent e) { + doFocusChanged(e.widget); + } + + @Override + public void focusLost(FocusEvent e) { + // do not reset due to focus behavior on GTK + // fViewerInFocus= null; + } + } + + private class InternalPostSelectionListener implements ISelectionChangedListener { + @Override + public void selectionChanged(SelectionChangedEvent event) { + doPostSelectionChanged(event); + } + + } + + private StructuredViewer[] fViewers; + + private StructuredViewer fViewerInFocus; + private ListenerList<ISelectionChangedListener> fSelectionChangedListeners; + private ListenerList<ISelectionChangedListener> fPostSelectionChangedListeners; + + /** + * Constructs a selection provider mediator object + * + * @param viewers All viewers that can provide a selection + * @param viewerInFocus the viewer currently in focus or <code>null</code> + */ + public SelectionProviderMediator(StructuredViewer[] viewers, StructuredViewer viewerInFocus) { + Assert.isNotNull(viewers); + fViewers = viewers; + InternalListener listener = new InternalListener(); + fSelectionChangedListeners = new ListenerList<>(); + fPostSelectionChangedListeners = new ListenerList<>(); + fViewerInFocus = viewerInFocus; + + for (StructuredViewer viewer : fViewers) { + viewer.addSelectionChangedListener(listener); + viewer.addPostSelectionChangedListener(new InternalPostSelectionListener()); + Control control = viewer.getControl(); + control.addFocusListener(listener); + } + } + + private void doFocusChanged(Widget control) { + for (StructuredViewer viewer : fViewers) { + if (viewer.getControl() == control) { + propagateFocusChanged(viewer); + return; + } + } + } + + final void doPostSelectionChanged(SelectionChangedEvent event) { + ISelectionProvider provider = event.getSelectionProvider(); + if (provider == fViewerInFocus) { + firePostSelectionChanged(); + } + } + + final void doSelectionChanged(SelectionChangedEvent event) { + ISelectionProvider provider = event.getSelectionProvider(); + if (provider == fViewerInFocus) { + fireSelectionChanged(); + } + } + + final void propagateFocusChanged(StructuredViewer viewer) { + if (viewer != fViewerInFocus) { // OK to compare by identity + fViewerInFocus = viewer; + fireSelectionChanged(); + firePostSelectionChanged(); + } + } + + private void fireSelectionChanged() { + if (fSelectionChangedListeners != null) { + SelectionChangedEvent event = new SelectionChangedEvent(this, getSelection()); + + for (ISelectionChangedListener listener : fSelectionChangedListeners) { + listener.selectionChanged(event); + } + } + } + + private void firePostSelectionChanged() { + if (fPostSelectionChangedListeners != null) { + SelectionChangedEvent event = new SelectionChangedEvent(this, getSelection()); + + for (ISelectionChangedListener listener : fPostSelectionChangedListeners) { + listener.selectionChanged(event); + } + } + } + + @Override + public void addSelectionChangedListener(ISelectionChangedListener listener) { + fSelectionChangedListeners.add(listener); + } + + @Override + public void removeSelectionChangedListener(ISelectionChangedListener listener) { + fSelectionChangedListeners.remove(listener); + } + + @Override + public void addPostSelectionChangedListener(ISelectionChangedListener listener) { + fPostSelectionChangedListeners.add(listener); + } + + @Override + public void removePostSelectionChangedListener(ISelectionChangedListener listener) { + fPostSelectionChangedListeners.remove(listener); + } + + @Override + public ISelection getSelection() { + if (fViewerInFocus != null) { + return fViewerInFocus.getSelection(); + } + return StructuredSelection.EMPTY; + } + + @Override + public void setSelection(ISelection selection) { + if (fViewerInFocus != null) { + fViewerInFocus.setSelection(selection); + } + } + + /** + * Sets current selection + * + * @param selection a selection object + * @param reveal a flag indicating if a reveal is needed + */ + public void setSelection(ISelection selection, boolean reveal) { + if (fViewerInFocus != null) { + fViewerInFocus.setSelection(selection, reveal); + } + } + + /** + * Returns the viewer in focus or null if no viewer has the focus + * + * @return returns the current viewer in focus + */ + public StructuredViewer getViewerInFocus() { + return fViewerInFocus; + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowNextFailureAction.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowNextFailureAction.java new file mode 100644 index 000000000..5e2809ec5 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowNextFailureAction.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2000, 2010 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import org.eclipse.jface.action.Action; + +/** + * Show next failure action + */ +public class ShowNextFailureAction extends Action { + + private TestRunnerViewPart fPart; + + /** + * Constructs a show next failure action object + * + * @param part a test runner view part object + */ + public ShowNextFailureAction(TestRunnerViewPart part) { + super(Messages.ShowNextFailureAction_label); + setDisabledImageDescriptor(Images.getImageDescriptor("dlcl16/select_next.png")); //$NON-NLS-1$ + setHoverImageDescriptor(Images.getImageDescriptor("elcl16/select_next.png")); //$NON-NLS-1$ + setImageDescriptor(Images.getImageDescriptor("elcl16/select_next.png")); //$NON-NLS-1$ + setToolTipText(Messages.ShowNextFailureAction_tooltip); + fPart = part; + } + + @Override + public void run() { + fPart.selectNextFailure(); + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowPreviousFailureAction.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowPreviousFailureAction.java new file mode 100644 index 000000000..f5919f564 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowPreviousFailureAction.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2000, 2010 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import org.eclipse.jface.action.Action; + +/** + * Show previous failure action + */ +public class ShowPreviousFailureAction extends Action { + + private TestRunnerViewPart fPart; + + /** + * Constructs a show previous failure action object + * + * @param part a test runner view part object + */ + public ShowPreviousFailureAction(TestRunnerViewPart part) { + super(Messages.ShowPreviousFailureAction_label); + setDisabledImageDescriptor(Images.getImageDescriptor("dlcl16/select_prev.png")); //$NON-NLS-1$ + setHoverImageDescriptor(Images.getImageDescriptor("elcl16/select_prev.png")); //$NON-NLS-1$ + setImageDescriptor(Images.getImageDescriptor("elcl16/select_prev.png")); //$NON-NLS-1$ + setToolTipText(Messages.ShowPreviousFailureAction_tooltip); + fPart = part; + } + + @Override + public void run() { + fPart.selectPreviousFailure(); + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowStackTraceInConsoleViewAction.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowStackTraceInConsoleViewAction.java new file mode 100644 index 000000000..5270eed0b --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/ShowStackTraceInConsoleViewAction.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2016 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; + +/** + * Action to show the stack trace of a failed test from Unit Test view's failure + * trace in debug's Java stack trace console. + */ +public class ShowStackTraceInConsoleViewAction extends Action { + + private Runnable fDelegate; + + /** + * Constructs a show stacktrace in console view action object + */ + public ShowStackTraceInConsoleViewAction() { + super(Messages.ShowStackTraceInConsoleViewAction_label, IAction.AS_PUSH_BUTTON); + setDescription(Messages.ShowStackTraceInConsoleViewAction_description); + setToolTipText(Messages.ShowStackTraceInConsoleViewAction_tooltip); + + setHoverImageDescriptor(Images.getImageDescriptor("elcl16/open_console.png")); //$NON-NLS-1$ + setImageDescriptor(Images.getImageDescriptor("elcl16/open_console.png")); //$NON-NLS-1$ + setDisabledImageDescriptor(Images.getImageDescriptor("dlcl16/open_console.png")); //$NON-NLS-1$ + + fDelegate = null; + } + + @Override + public void run() { + if (fDelegate != null) { + fDelegate.run(); + } + } + + /** + * Sets an action delegate + * + * @param delegate an action delegate + */ + public void setDelegate(Runnable delegate) { + fDelegate = delegate; + } + + @Override + public boolean isEnabled() { + return super.isEnabled() && fDelegate != null; + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestRunnerViewPart.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestRunnerViewPart.java new file mode 100644 index 000000000..b08266cc0 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestRunnerViewPart.java @@ -0,0 +1,1823 @@ +/******************************************************************************* +L * Copyright (c) 2000, 2018 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.URL; +import java.text.MessageFormat; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.unittest.internal.UnitTestPlugin; +import org.eclipse.unittest.internal.UnitTestPreferencesConstants; +import org.eclipse.unittest.internal.model.ITestRunSessionListener; +import org.eclipse.unittest.internal.model.ITestSessionListener; +import org.eclipse.unittest.internal.model.ProgressState; +import org.eclipse.unittest.internal.model.TestCaseElement; +import org.eclipse.unittest.internal.model.TestElement; +import org.eclipse.unittest.internal.model.TestRunSession; +import org.eclipse.unittest.internal.model.UnitTestModel; +import org.eclipse.unittest.internal.ui.history.History; +import org.eclipse.unittest.internal.ui.history.HistoryHandler; +import org.eclipse.unittest.model.ITestCaseElement; +import org.eclipse.unittest.model.ITestElement; +import org.eclipse.unittest.model.ITestElement.FailureTrace; +import org.eclipse.unittest.model.ITestElement.Result; +import org.eclipse.unittest.model.ITestRunSession; +import org.eclipse.unittest.model.ITestSuiteElement; +import org.eclipse.unittest.ui.ITestViewSupport; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CLabel; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.custom.ViewForm; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.DropTarget; +import org.eclipse.swt.dnd.DropTargetAdapter; +import org.eclipse.swt.dnd.DropTargetEvent; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.URLTransfer; +import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Layout; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.ToolBar; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.IHandler; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.ILock; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.preferences.InstanceScope; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IContributionItem; +import org.eclipse.jface.action.IMenuListener; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.IStatusLineManager; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.resource.ImageDescriptor; + +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IEditorActionBarContributor; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IMemento; +import org.eclipse.ui.IPartListener2; +import org.eclipse.ui.IViewPart; +import org.eclipse.ui.IViewSite; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchPartReference; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.actions.ActionFactory; +import org.eclipse.ui.handlers.IHandlerActivation; +import org.eclipse.ui.handlers.IHandlerService; +import org.eclipse.ui.menus.CommandContributionItem; +import org.eclipse.ui.menus.CommandContributionItemParameter; +import org.eclipse.ui.part.EditorActionBarContributor; +import org.eclipse.ui.part.ViewPart; +import org.eclipse.ui.progress.IWorkbenchSiteProgressService; +import org.eclipse.ui.progress.UIJob; +import org.eclipse.ui.statushandlers.StatusManager; + +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchManager; +import org.eclipse.debug.internal.ui.DebugPluginImages; +import org.eclipse.debug.internal.ui.actions.EditLaunchConfigurationAction; + +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugUIConstants; + +/** + * A ViewPart that shows the results of a test run. + */ +@SuppressWarnings("restriction") +public class TestRunnerViewPart extends ViewPart { + + /** + * An identifier of Test Runner View Part + */ + public static final String NAME = UnitTestPlugin.PLUGIN_ID + ".ResultView"; //$NON-NLS-1$ + + private static final String RERUN_LAST_COMMAND = UnitTestPlugin.PLUGIN_ID + ".UnitTestShortcut.rerunLast"; //$NON-NLS-1$ + private static final String RERUN_FAILED_CASES_COMMAND = UnitTestPlugin.PLUGIN_ID + + ".UnitTestShortcut.rerunFailedCases"; //$NON-NLS-1$ + + static final int REFRESH_INTERVAL = 200; + + /** + * A Test Result layout + */ + public enum TestResultsLayout { + FLAT, HIERARCHICAL + } + + /** + * Whether the output scrolls and reveals tests as they are executed. + */ + protected boolean fAutoScroll = true; + /** + * The current orientation; either <code>VIEW_ORIENTATION_HORIZONTAL</code> + * <code>VIEW_ORIENTATION_VERTICAL</code>, or + * <code>VIEW_ORIENTATION_AUTOMATIC</code>. + */ + private int fOrientation = VIEW_ORIENTATION_AUTOMATIC; + /** + * The current orientation; either <code>VIEW_ORIENTATION_HORIZONTAL</code> + * <code>VIEW_ORIENTATION_VERTICAL</code>. + */ + private int fCurrentOrientation; + /** + * The current layout mode (LAYOUT_FLAT or LAYOUT_HIERARCHICAL). + */ + private TestResultsLayout fLayout = TestResultsLayout.HIERARCHICAL; + + private UnitTestProgressBar fProgressBar; + private ProgressIcons fProgressImages; + protected Image fViewImage; + private CounterPanel fCounterPanel; + protected boolean fShowOnErrorOnly = false; + protected Clipboard fClipboard; + protected volatile String fInfoMessage; + + private FailureTraceUIBlock fFailureTrace; + + private TestViewer fTestViewer; + /** + * Is the UI disposed? + */ + private boolean fIsDisposed = false; + + /** + * Actions + */ + private Action fNextAction; + private Action fPreviousAction; + + private StopAction fStopAction; + private UnitTestCopyAction fCopyAction; + private Action fPasteAction; + + private Action fRerunLastTestAction; + private IHandlerActivation fRerunLastActivation; + private Action fRerunFailedCasesAction; + private IHandlerActivation fRerunFailedFirstActivation; + private EditLaunchConfigurationAction fEditLaunchConfigAction; + + private Action fFailuresOnlyFilterAction; + private Action fIgnoredOnlyFilterAction; + private ScrollLockAction fScrollLockAction; + private ToggleOrientationAction[] fToggleOrientationActions; + private ShowTestHierarchyAction fShowTestHierarchyAction; + private ShowTimeAction fShowTimeAction; + private ActivateOnErrorAction fActivateOnErrorAction; + private IMenuListener fViewMenuListener; + + private TestRunSession fTestRunSession; + private TestSessionListener fTestSessionListener; + +// private RunnerViewHistory fViewHistory; + private TestRunSessionListener fTestRunSessionListener; + + final Image fStackViewIcon; + final Image fTestRunOKIcon; + final Image fTestRunFailIcon; + final Image fTestRunOKDirtyIcon; + final Image fTestRunFailDirtyIcon; + + final Image fTestIcon; + final Image fTestOkIcon; + final Image fTestErrorIcon; + final Image fTestFailIcon; + final Image fTestAssumptionFailureIcon; + final Image fTestRunningIcon; + final Image fTestIgnoredIcon; + + final ImageDescriptor fSuiteIconDescriptor = Images.getImageDescriptor("obj16/tsuite.png"); //$NON-NLS-1$ + final ImageDescriptor fSuiteOkIconDescriptor = Images.getImageDescriptor("obj16/tsuiteok.png"); //$NON-NLS-1$ + final ImageDescriptor fSuiteErrorIconDescriptor = Images.getImageDescriptor("obj16/tsuiteerror.png"); //$NON-NLS-1$ + final ImageDescriptor fSuiteFailIconDescriptor = Images.getImageDescriptor("obj16/tsuitefail.png"); //$NON-NLS-1$ + final ImageDescriptor fSuiteRunningIconDescriptor = Images.getImageDescriptor("obj16/tsuiterun.png"); //$NON-NLS-1$ + + final Image fSuiteIcon; + final Image fSuiteOkIcon; + final Image fSuiteErrorIcon; + final Image fSuiteFailIcon; + final Image fSuiteRunningIcon; + + final List<Image> fImagesToDispose; + + // Persistence tags. + static final String TAG_PAGE = "page"; //$NON-NLS-1$ + static final String TAG_RATIO = "ratio"; //$NON-NLS-1$ + static final String TAG_TRACEFILTER = "tracefilter"; //$NON-NLS-1$ + static final String TAG_ORIENTATION = "orientation"; //$NON-NLS-1$ + static final String TAG_SCROLL = "scroll"; //$NON-NLS-1$ + /** + */ + static final String TAG_LAYOUT = "layout"; //$NON-NLS-1$ + /** + */ + static final String TAG_FAILURES_ONLY = "failuresOnly"; //$NON-NLS-1$ + + /** + */ + static final String TAG_IGNORED_ONLY = "ignoredOnly"; //$NON-NLS-1$ + /** + */ + static final String TAG_SHOW_TIME = "time"; //$NON-NLS-1$ + + /** + */ + static final String PREF_LAST_PATH = "lastImportExportPath"; //$NON-NLS-1$ + + /** + */ + static final String PREF_LAST_URL = "lastImportURL"; //$NON-NLS-1$ + + // orientations + static final int VIEW_ORIENTATION_VERTICAL = 0; + static final int VIEW_ORIENTATION_HORIZONTAL = 1; + static final int VIEW_ORIENTATION_AUTOMATIC = 2; + + private IMemento fMemento; + + Image fOriginalViewImage; + + private SashForm fSashForm; + + private Composite fCounterComposite; + private Composite fParent; + + /** + * A Job that periodically updates view description, counters, and progress bar. + */ + private UpdateUIJob fUpdateJob; + + /** + * A Job that runs as long as a test run is running. It is used to show busyness + * for running jobs in the view (title in italics). + */ + private UnitTestIsRunningJob fUnitTestIsRunningJob; + private ILock fUnitTestIsRunningLock; + public static final Object FAMILY_UNITTEST_RUN = new Object(); + + private IPartListener2 fPartListener = new IPartListener2() { + @Override + public void partActivated(IWorkbenchPartReference ref) { + } + + @Override + public void partBroughtToTop(IWorkbenchPartReference ref) { + } + + @Override + public void partInputChanged(IWorkbenchPartReference ref) { + } + + @Override + public void partClosed(IWorkbenchPartReference ref) { + } + + @Override + public void partDeactivated(IWorkbenchPartReference ref) { + } + + @Override + public void partOpened(IWorkbenchPartReference ref) { + } + + @Override + public void partVisible(IWorkbenchPartReference ref) { + if (getSite().getId().equals(ref.getId())) { + fPartIsVisible = true; + } + } + + @Override + public void partHidden(IWorkbenchPartReference ref) { + if (getSite().getId().equals(ref.getId())) { + fPartIsVisible = false; + } + } + }; + + protected boolean fPartIsVisible = false; + + private static class UnitTesttPasteAction extends Action { + private final Shell fShell; + private Clipboard fClipboard; + + public UnitTesttPasteAction(Shell shell, Clipboard clipboard) { + super(Messages.TestRunnerViewPart_PasteAction_label); + Assert.isNotNull(clipboard); + fShell = shell; + fClipboard = clipboard; + } + + @Override + public void run() { + String urlData = (String) fClipboard.getContents(URLTransfer.getInstance()); + if (urlData == null) { + urlData = (String) fClipboard.getContents(TextTransfer.getInstance()); + } + if (urlData != null && urlData.length() > 0) { + if (isValidUrl(urlData)) { + importTestRunSession(urlData); + return; + } + } + MessageDialog.openInformation(fShell, Messages.TestRunnerViewPart_PasteAction_cannotpaste_title, + Messages.TestRunnerViewPart_PasteAction_cannotpaste_message); + } + + private boolean isValidUrl(String urlData) { + try { + @SuppressWarnings("unused") + URL url = new URL(urlData); + } catch (MalformedURLException e) { + return false; + } + return true; + } + } + + private class TestRunSessionListener implements ITestRunSessionListener { + @Override + public void sessionAdded(final ITestRunSession testRunSession) { + getDisplay().asyncExec(() -> { + if (UnitTestUIPreferencesConstants.getShowInAllViews() + || getSite().getWorkbenchWindow() == PlatformUI.getWorkbench().getActiveWorkbenchWindow()) { + if (fInfoMessage == null) { + String testRunLabel = BasicElementLabels + .getJavaElementName(((TestRunSession) testRunSession).getTestRunName()); + String msg; + if (testRunSession.getLaunch() != null) { + msg = MessageFormat.format(Messages.TestRunnerViewPart_Launching, testRunLabel); + } else { + msg = testRunLabel; + } + registerInfoMessage(msg); + } + + setActiveTestRunSession((TestRunSession) testRunSession); + } + }); + } + + @Override + public void sessionRemoved(final ITestRunSession testRunSession) { + getDisplay().asyncExec(() -> { + if (testRunSession.equals(fTestRunSession)) { + List<TestRunSession> testRunSessions = UnitTestModel.getInstance().getTestRunSessions(); + if (!testRunSessions.isEmpty()) { + setActiveTestRunSession(testRunSessions.get(0)); + } else { + setActiveTestRunSession(null); + } + } + }); + } + } + + private class TestSessionListener implements ITestSessionListener { + @Override + public void sessionStarted() { + fTestViewer.registerViewersRefresh(); + fShowOnErrorOnly = getShowOnErrorOnly(); + + startUpdateJobs(); + + fStopAction.setEnabled(true); + fRerunLastTestAction.setEnabled(true); + fEditLaunchConfigAction.setEnabled(fTestRunSession.getLaunch() != null); + } + + @Override + public void sessionCompleted(Duration duration) { + deregisterTestSessionListener(); + + fTestViewer.registerAutoScrollTarget(null); + + final String msg = MessageFormat.format(Messages.TestRunnerViewPart_message_finish, + Double.valueOf(duration != null ? duration.toNanos() / 1.0e9 : 0)); + getDisplay().asyncExec(() -> registerInfoMessage(msg)); + + postSyncRunnable(() -> { + if (isDisposed()) + return; + fStopAction.setEnabled(lastLaunchStillRunning()); + updateRerunFailedFirstAction(); + processChangesInUI(); + if (hasErrorsOrFailures()) { + selectFirstFailure(); + } + /* + * if (fDirtyListener == null) { fDirtyListener= new DirtyListener(); + * JavaCore.addElementChangedListener(fDirtyListener); } + */ + warnOfContentChange(); + }); + stopUpdateJobs(); + showMessageIfNoTests(); + } + + @Override + public void sessionAborted(Duration duration) { + deregisterTestSessionListener(); + + fTestViewer.registerAutoScrollTarget(null); + + getDisplay().asyncExec(() -> registerInfoMessage(Messages.TestRunnerViewPart_message_stopped)); + handleStopped(); + } + + @Override + public void runningBegins() { + if (!fShowOnErrorOnly) + postShowTestResultsView(); + } + + @Override + public void testStarted(ITestCaseElement testCaseElement) { + fTestViewer.registerAutoScrollTarget(testCaseElement); + fTestViewer.registerViewerUpdate(testCaseElement); + registerInfoMessage(testCaseElement.getDisplayName()); + } + + @Override + public void testFailed(ITestElement testElement, ITestElement.Result status, FailureTrace trace) { + if (isAutoScroll()) { + fTestViewer.registerFailedForAutoScroll(testElement); + } + fTestViewer.registerViewerUpdate(testElement); + + // show the view on the first error only + if (fShowOnErrorOnly && (getErrorsPlusFailures() == 1)) + postShowTestResultsView(); + } + + @Override + public void testEnded(ITestCaseElement testCaseElement) { + fTestViewer.registerViewerUpdate(testCaseElement); + } + + @Override + public void testAdded(ITestElement testElement) { + fTestViewer.registerTestAdded(testElement); + } + } + + private class UpdateUIJob extends UIJob { + private boolean fRunning = true; + + public UpdateUIJob(String name) { + super(name); + setSystem(true); + } + + @Override + public IStatus runInUIThread(IProgressMonitor monitor) { + if (!isDisposed()) { + processChangesInUI(); + } + schedule(REFRESH_INTERVAL); + return Status.OK_STATUS; + } + + public void stop() { + fRunning = false; + } + + @Override + public boolean shouldSchedule() { + return fRunning; + } + } + + private class UnitTestIsRunningJob extends Job { + public UnitTestIsRunningJob(String name) { + super(name); + setSystem(true); + } + + @Override + public IStatus run(IProgressMonitor monitor) { + // wait until the test run terminates + fUnitTestIsRunningLock.acquire(); + return Status.OK_STATUS; + } + + @Override + public boolean belongsTo(Object family) { + return family == TestRunnerViewPart.FAMILY_UNITTEST_RUN; + } + } + + private class StopAction extends Action { + public StopAction() { + setText(Messages.TestRunnerViewPart_stopaction_text); + setToolTipText(Messages.TestRunnerViewPart_stopaction_tooltip); + Images.setLocalImageDescriptors(this, "stop.png"); //$NON-NLS-1$ + } + + @Override + public void run() { + stopTest(); + setEnabled(false); + } + } + + private class RerunLastAction extends Action { + public RerunLastAction() { + setText(Messages.TestRunnerViewPart_rerunaction_label); + setToolTipText(Messages.TestRunnerViewPart_rerunaction_tooltip); + Images.setLocalImageDescriptors(this, "relaunch.png"); //$NON-NLS-1$ + setEnabled(false); + setActionDefinitionId(RERUN_LAST_COMMAND); + } + + @Override + public void run() { + DebugUITools.launch(fTestRunSession.getLaunch().getLaunchConfiguration(), rerunLaunchMode()); + } + } + + private class RerunFailedCasesAction extends Action { + public RerunFailedCasesAction() { + setText(Messages.TestRunnerViewPart_rerunfailuresaction_label); + setToolTipText(Messages.TestRunnerViewPart_rerunfailuresaction_tooltip); + Images.setLocalImageDescriptors(this, "relaunchf.png"); //$NON-NLS-1$ + setEnabled(false); + setActionDefinitionId(RERUN_FAILED_CASES_COMMAND); + } + + @Override + public void run() { + rerunFailedTestCases(); + } + } + + private class ToggleOrientationAction extends Action { + private final int fActionOrientation; + + public ToggleOrientationAction(int orientation) { + super("", AS_RADIO_BUTTON); //$NON-NLS-1$ + switch (orientation) { + case TestRunnerViewPart.VIEW_ORIENTATION_HORIZONTAL: + setText(Messages.TestRunnerViewPart_toggle_horizontal_label); + setImageDescriptor(Images.getImageDescriptor("elcl16/th_horizontal.png")); //$NON-NLS-1$ + break; + case TestRunnerViewPart.VIEW_ORIENTATION_VERTICAL: + setText(Messages.TestRunnerViewPart_toggle_vertical_label); + setImageDescriptor(Images.getImageDescriptor("elcl16/th_vertical.png")); //$NON-NLS-1$ + break; + case TestRunnerViewPart.VIEW_ORIENTATION_AUTOMATIC: + setText(Messages.TestRunnerViewPart_toggle_automatic_label); + setImageDescriptor(Images.getImageDescriptor("elcl16/th_automatic.png")); //$NON-NLS-1$ + break; + default: + break; + } + fActionOrientation = orientation; + PlatformUI.getWorkbench().getHelpSystem().setHelp(this, + IUnitTestHelpContextIds.RESULTS_VIEW_TOGGLE_ORIENTATION_ACTION); + } + + public int getOrientation() { + return fActionOrientation; + } + + @Override + public void run() { + if (isChecked()) { + fOrientation = fActionOrientation; + computeOrientation(); + } + } + } + + private class SortAction extends Action { + private final boolean enableAlphabeticalSort; + + public SortAction(boolean enableAlphabeticalSort) { + super(enableAlphabeticalSort ? Messages.TestRunnerViewPart_sortAlphabetical + : Messages.TestRunnerViewPart_sortRunner, AS_RADIO_BUTTON); + this.enableAlphabeticalSort = enableAlphabeticalSort; + } + + @Override + public void run() { + fTestViewer.setAlphabeticalSort(enableAlphabeticalSort); + } + + @Override + public boolean isChecked() { + return fTestViewer.isAlphabeticalSort() == enableAlphabeticalSort; + } + } + + private class FailuresOnlyFilterAction extends Action { + public FailuresOnlyFilterAction() { + super(Messages.TestRunnerViewPart_show_failures_only, AS_CHECK_BOX); + setToolTipText(Messages.TestRunnerViewPart_show_failures_only); + setImageDescriptor(Images.getImageDescriptor("obj16/failures.png")); //$NON-NLS-1$ + } + + @Override + public void run() { + setShowFailuresOnly(isChecked()); + } + } + + private class IgnoredOnlyFilterAction extends Action { + public IgnoredOnlyFilterAction() { + super(Messages.TestRunnerViewPart_show_ignored_only, AS_CHECK_BOX); + setToolTipText(Messages.TestRunnerViewPart_show_ignored_only); + setImageDescriptor(Images.getImageDescriptor("obj16/testignored.png")); //$NON-NLS-1$ + } + + @Override + public void run() { + setShowIgnoredOnly(isChecked()); + } + } + + private class ShowTimeAction extends Action { + public ShowTimeAction() { + super(Messages.TestRunnerViewPart_show_execution_time, IAction.AS_CHECK_BOX); + } + + @Override + public void run() { + setShowExecutionTime(isChecked()); + } + } + + private class ShowTestHierarchyAction extends Action { + public ShowTestHierarchyAction() { + super(Messages.TestRunnerViewPart_hierarchical_layout, IAction.AS_CHECK_BOX); + setImageDescriptor(Images.getImageDescriptor("elcl16/hierarchicalLayout.png")); //$NON-NLS-1$ + } + + @Override + public void run() { + setFilterAndLayout(fFailuresOnlyFilterAction.isChecked(), fIgnoredOnlyFilterAction.isChecked(), + isChecked() ? TestResultsLayout.HIERARCHICAL : TestResultsLayout.FLAT); + } + } + + private class ActivateOnErrorAction extends Action { + public ActivateOnErrorAction() { + super(Messages.TestRunnerViewPart_activate_on_failure_only, IAction.AS_CHECK_BOX); + // setImageDescriptor(UnitTestPlugin.getImageDescriptor("obj16/failures.png")); + // //$NON-NLS-1$ + update(); + } + + public void update() { + setChecked(getShowOnErrorOnly()); + } + + @Override + public void run() { + boolean checked = isChecked(); + fShowOnErrorOnly = checked; + InstanceScope.INSTANCE.getNode(UnitTestPlugin.PLUGIN_ID) + .putBoolean(UnitTestPreferencesConstants.SHOW_ON_ERROR_ONLY, checked); + } + } + + /** + * Constructs Test Runner View part object + */ + public TestRunnerViewPart() { + fImagesToDispose = new ArrayList<>(); + + fStackViewIcon = createManagedImage("eview16/stackframe.png");//$NON-NLS-1$ + fTestRunOKIcon = createManagedImage("eview16/unitsucc.png"); //$NON-NLS-1$ + fTestRunFailIcon = createManagedImage("eview16/uniterr.png"); //$NON-NLS-1$ + fTestRunOKDirtyIcon = createManagedImage("eview16/unitsuccq.png"); //$NON-NLS-1$ + fTestRunFailDirtyIcon = createManagedImage("eview16/uniterrq.png"); //$NON-NLS-1$ + + fTestIcon = createManagedImage("obj16/test.png"); //$NON-NLS-1$ + fTestOkIcon = createManagedImage("obj16/testok.png"); //$NON-NLS-1$ + fTestErrorIcon = createManagedImage("obj16/testerr.png"); //$NON-NLS-1$ + fTestFailIcon = createManagedImage("obj16/testfail.png"); //$NON-NLS-1$ + fTestRunningIcon = createManagedImage("obj16/testrun.png"); //$NON-NLS-1$ + fTestIgnoredIcon = createManagedImage("obj16/testignored.png"); //$NON-NLS-1$ + fTestAssumptionFailureIcon = createManagedImage("obj16/testassumptionfailed.png"); //$NON-NLS-1$ + + fSuiteIcon = createManagedImage(fSuiteIconDescriptor); + fSuiteOkIcon = createManagedImage(fSuiteOkIconDescriptor); + fSuiteErrorIcon = createManagedImage(fSuiteErrorIconDescriptor); + fSuiteFailIcon = createManagedImage(fSuiteFailIconDescriptor); + fSuiteRunningIcon = createManagedImage(fSuiteRunningIconDescriptor); + } + + private Image createManagedImage(String path) { + return createManagedImage(Images.getImageDescriptor(path)); + } + + private Image createManagedImage(ImageDescriptor descriptor) { + Image image = descriptor.createImage(); + if (image == null) { + image = ImageDescriptor.getMissingImageDescriptor().createImage(); + } + fImagesToDispose.add(image); + return image; + } + + @Override + public void init(IViewSite site, IMemento memento) throws PartInitException { + super.init(site, memento); + fMemento = memento; + IWorkbenchSiteProgressService progressService = getProgressService(); + if (progressService != null) + progressService.showBusyForFamily(TestRunnerViewPart.FAMILY_UNITTEST_RUN); + } + + private IWorkbenchSiteProgressService getProgressService() { + Object siteService = getSite().getAdapter(IWorkbenchSiteProgressService.class); + if (siteService != null) + return (IWorkbenchSiteProgressService) siteService; + return null; + } + + @Override + public void saveState(IMemento memento) { + if (fSashForm == null) { + // part has not been created + if (fMemento != null) // Keep the old state; + memento.putMemento(fMemento); + return; + } + +// int activePage= fTabFolder.getSelectionIndex(); +// memento.putInteger(TAG_PAGE, activePage); + memento.putString(TAG_SCROLL, fScrollLockAction.isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ + int weigths[] = fSashForm.getWeights(); + int ratio = (weigths[0] * 1000) / (weigths[0] + weigths[1]); + memento.putInteger(TAG_RATIO, ratio); + memento.putInteger(TAG_ORIENTATION, fOrientation); + + memento.putString(TAG_FAILURES_ONLY, fFailuresOnlyFilterAction.isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ + memento.putString(TAG_IGNORED_ONLY, fIgnoredOnlyFilterAction.isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ + memento.putString(TAG_LAYOUT, fLayout.name()); + memento.putString(TAG_SHOW_TIME, fShowTimeAction.isChecked() ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + private void restoreLayoutState(IMemento memento) { +// Integer page= memento.getInteger(TAG_PAGE); +// if (page != null) { +// int p= page.intValue(); +// if (p < fTestRunTabs.size()) { // tab count can decrease if a contributing plug-in is removed +// fTabFolder.setSelection(p); +// fActiveRunTab= (TestRunTab)fTestRunTabs.get(p); +// } +// } + Integer ratio = memento.getInteger(TAG_RATIO); + if (ratio != null) + fSashForm.setWeights(ratio.intValue(), 1000 - ratio.intValue()); + Integer orientation = memento.getInteger(TAG_ORIENTATION); + if (orientation != null) + fOrientation = orientation.intValue(); + computeOrientation(); + String scrollLock = memento.getString(TAG_SCROLL); + if (scrollLock != null) { + fScrollLockAction.setChecked(scrollLock.equals("true")); //$NON-NLS-1$ + setAutoScroll(!fScrollLockAction.isChecked()); + } + + String layoutString = memento.getString(TAG_LAYOUT); + TestResultsLayout layout = layoutString == null ? TestResultsLayout.HIERARCHICAL + : TestResultsLayout.valueOf(layoutString); + + String failuresOnly = memento.getString(TAG_FAILURES_ONLY); + boolean showFailuresOnly = false; + if (failuresOnly != null) + showFailuresOnly = failuresOnly.equals("true"); //$NON-NLS-1$ + + String ignoredOnly = memento.getString(TAG_IGNORED_ONLY); + boolean showIgnoredOnly = false; + if (ignoredOnly != null) + showIgnoredOnly = ignoredOnly.equals("true"); //$NON-NLS-1$ + + String time = memento.getString(TAG_SHOW_TIME); + boolean showTime = true; + if (time != null) + showTime = time.equals("true"); //$NON-NLS-1$ + + setFilterAndLayout(showFailuresOnly, showIgnoredOnly, layout); + setShowExecutionTime(showTime); + } + + /** + * Stops the currently running test and shuts down the RemoteTestRunner + */ + public void stopTest() { + if (fTestRunSession != null) { + if (fTestRunSession.isRunning()) { + setContentDescription(Messages.TestRunnerViewPart_message_stopping); + } + fTestRunSession.abortTestRun(); + } + } + + private void startUpdateJobs() { + postSyncProcessChanges(); + + if (fUpdateJob != null) { + return; + } + fUnitTestIsRunningJob = new UnitTestIsRunningJob(Messages.TestRunnerViewPart_wrapperJobName); + fUnitTestIsRunningLock = Job.getJobManager().newLock(); + // acquire lock while a test run is running + // the lock is released when the test run terminates + // the wrapper job will wait on this lock. + fUnitTestIsRunningLock.acquire(); + getProgressService().schedule(fUnitTestIsRunningJob); + + fUpdateJob = new UpdateUIJob(Messages.TestRunnerViewPart_jobName); + fUpdateJob.schedule(REFRESH_INTERVAL); + } + + private void stopUpdateJobs() { + if (fUpdateJob != null) { + fUpdateJob.stop(); + fUpdateJob = null; + } + if (fUnitTestIsRunningJob != null && fUnitTestIsRunningLock != null) { + fUnitTestIsRunningLock.release(); + fUnitTestIsRunningJob = null; + } + postSyncProcessChanges(); + } + + private void processChangesInUI() { + if (fSashForm.isDisposed()) + return; + + doShowInfoMessage(); + refreshCounters(); + + if (!fPartIsVisible) + updateViewTitleProgress(); + else { + updateViewIcon(); + } + updateNextPreviousActions(); + + fTestViewer.processChangesInUI(); + } + + private void updateNextPreviousActions() { + boolean hasErrorsOrFailures = !fIgnoredOnlyFilterAction.isChecked() && hasErrorsOrFailures(); + fNextAction.setEnabled(hasErrorsOrFailures); + fPreviousAction.setEnabled(hasErrorsOrFailures); + } + + private String rerunLaunchMode() { + return fTestRunSession != null && fTestRunSession.getLaunch() != null + ? fTestRunSession.getLaunch().getLaunchMode() + : ILaunchManager.RUN_MODE; + } + + /** + * Re-runs the tests executing the failed tests first + */ + private void rerunFailedTestCases() { + if (lastLaunchStillRunning()) { + // prompt for terminating the existing run + if (MessageDialog.openQuestion(getSite().getShell(), Messages.TestRunnerViewPart_terminate_title, + Messages.TestRunnerViewPart_terminate_message) && fTestRunSession != null) { + fTestRunSession.abortTestRun(); + } + } + List<ITestElement> allFailedTestCases = new ArrayList<>(); + collectFailedTestCases(fTestRunSession, allFailedTestCases); + ILaunchConfiguration tmp = fTestRunSession.getTestViewSupport().getRerunLaunchConfiguration(allFailedTestCases); + if (tmp != null) { + DebugUITools.launch(tmp, rerunLaunchMode()); + } + } + + private void collectFailedTestCases(TestElement testElement, List<ITestElement> allFailedTestCases) { + if (testElement == null) { + return; + } + Result result = testElement.getTestResult(true); + if (result != Result.ERROR && result != Result.FAILURE) { + return; + } + if (testElement instanceof TestCaseElement) { + allFailedTestCases.add(testElement); + } else if (testElement instanceof ITestSuiteElement) { + ((ITestSuiteElement) testElement).getChildren() + .forEach(child -> collectFailedTestCases((TestElement) child, allFailedTestCases)); + } + + } + + /** + * Sets auto-scroll enabled value + * + * @param scroll <code>true</code> in case of auto-scroll enabled, otherwise - + * <code>false</code> + */ + public void setAutoScroll(boolean scroll) { + fAutoScroll = scroll; + } + + /** + * Indicates if autoscroll is enabled + * + * @return <code>true</code> if the output scroll and reveal is needed for the + * tests as they are executed, otherwise returns <code>false</code> + */ + public boolean isAutoScroll() { + return fAutoScroll; + } + + /** + * Selects the next failure in the tests tree + */ + public void selectNextFailure() { + fTestViewer.selectFailure(true); + } + + /** + * Selects the previous failure in the tests tree + */ + public void selectPreviousFailure() { + fTestViewer.selectFailure(false); + } + + /** + * Selects the first failure in the tests tree + */ + protected void selectFirstFailure() { + fTestViewer.selectFirstFailure(); + } + + private boolean hasErrorsOrFailures() { + return getErrorsPlusFailures() > 0; + } + + private int getErrorsPlusFailures() { + if (fTestRunSession == null) + return 0; + else + return fTestRunSession.getCurrentErrorCount() + fTestRunSession.getCurrentFailureCount(); + } + + private void handleStopped() { + postSyncRunnable(() -> { + if (isDisposed()) + return; + resetViewIcon(); + fStopAction.setEnabled(false); + updateRerunFailedFirstAction(); + }); + stopUpdateJobs(); + showMessageIfNoTests(); + } + + private void showMessageIfNoTests() { + if (fTestRunSession != null && fTestRunSession.getFinalTestCaseCount() != null + && fTestRunSession.getFinalTestCaseCount().intValue() == 0) { + Display.getDefault().asyncExec(() -> { + String msg = MessageFormat.format(Messages.TestRunnerViewPart_error_no_tests_found, getDisplayName()); + MessageDialog.openInformation(getSite().getShell(), Messages.TestRunnerViewPart__error_cannotrun, msg); + }); + } + } + + private void resetViewIcon() { + fViewImage = fOriginalViewImage; + firePropertyChange(IWorkbenchPart.PROP_TITLE); + } + + private void updateViewIcon() { + if (fTestRunSession == null || fTestRunSession.isStopped() || fTestRunSession.isRunning() + || fTestRunSession.countStartedTestCases() == 0) + fViewImage = fOriginalViewImage; + else if (hasErrorsOrFailures()) + fViewImage = fTestRunFailIcon; + else + fViewImage = fTestRunOKIcon; + firePropertyChange(IWorkbenchPart.PROP_TITLE); + } + + private void updateViewTitleProgress() { + if (fTestRunSession != null) { + if (fTestRunSession.isRunning()) { + Image progress = fProgressImages.getImage(fTestRunSession.countStartedTestCases(), + fTestRunSession.getFinalTestCaseCount(), + fTestRunSession.getCurrentErrorCount() > 0 || fTestRunSession.getCurrentFailureCount() > 0); + if (progress != fViewImage) { + fViewImage = progress; + firePropertyChange(IWorkbenchPart.PROP_TITLE); + } + } else { + updateViewIcon(); + } + } else { + resetViewIcon(); + } + } + + /** + * Sets an active test run session + * + * @param testRunSession new active test run session + * @return deactivated session, or <code>null</code> iff no session got + * deactivated + */ + public TestRunSession setActiveTestRunSession(TestRunSession testRunSession) { + /* + * - State: fTestRunSession fTestSessionListener Jobs + * fTestViewer.processChangesInUI(); - UI: fCounterPanel fProgressBar + * setContentDescription / fInfoMessage setTitleToolTip view icons statusLine + * fFailureTrace + * + * action enablement + */ + if (fTestRunSession == testRunSession) + return null; + + deregisterTestSessionListener(); + + TestRunSession deactivatedSession = fTestRunSession; + + fTestRunSession = testRunSession; + fTestViewer.registerActiveSession(testRunSession); + History.INSTANCE.watch(testRunSession); + + if (fSashForm.isDisposed()) { + stopUpdateJobs(); + return deactivatedSession; + } + + if (testRunSession == null) { + setTitleToolTip(null); + resetViewIcon(); + clearStatus(); + fFailureTrace.clear(); + + registerInfoMessage(" "); //$NON-NLS-1$ + stopUpdateJobs(); + + fStopAction.setEnabled(false); + fRerunFailedCasesAction.setEnabled(false); + fRerunLastTestAction.setEnabled(false); + + } else { + if (fTestRunSession.isStarting() || fTestRunSession.isRunning()) { + fTestSessionListener = new TestSessionListener(); + fTestRunSession.addTestSessionListener(fTestSessionListener); + } + if (!fTestRunSession.isStarting() && !fShowOnErrorOnly) { + showTestResultsView(); + } + + setTitleToolTip(); + + clearStatus(); + fFailureTrace.clear(); + registerInfoMessage(BasicElementLabels.getJavaElementName(fTestRunSession.getTestRunName())); + + updateRerunFailedFirstAction(); + fRerunLastTestAction.setEnabled(fTestRunSession.getLaunch() != null); + fEditLaunchConfigAction.setEnabled(fTestRunSession.getLaunch() != null); + + fStopAction.setEnabled(fTestRunSession.isRunning()); + if (fTestRunSession.isRunning()) { + startUpdateJobs(); + } else /* old or fresh session: don't want jobs at this stage */ { + stopUpdateJobs(); + } + } + getSite().getShell().getDisplay().asyncExec(this::processChangesInUI); + return deactivatedSession; + } + + private void deregisterTestSessionListener() { + if (fTestRunSession != null && fTestSessionListener != null) { + fTestRunSession.removeTestSessionListener(fTestSessionListener); + fTestSessionListener = null; + } + } + + private void updateRerunFailedFirstAction() { + boolean state = hasErrorsOrFailures() && fTestRunSession.getLaunch().getLaunchConfiguration() != null; + fRerunFailedCasesAction.setEnabled(state); + } + + /** + * Returns the display name of the current test run session + * + * @return the display name of the current test run session, or + * <code>null</code> + */ + public String getDisplayName() { + ITestViewSupport testViewSupport = fTestRunSession.getTestViewSupport(); + return testViewSupport != null ? testViewSupport.getDisplayName() : null; + } + + private void setTitleToolTip() { + String displayStr = getDisplayName(); + + String testRunLabel = BasicElementLabels.getJavaElementName(fTestRunSession.getTestRunName()); + if (displayStr != null) + setTitleToolTip(MessageFormat.format(Messages.TestRunnerViewPart_titleToolTip, testRunLabel, displayStr)); + else + setTitleToolTip(testRunLabel); + } + + @Override + public synchronized void dispose() { + fIsDisposed = true; + if (fTestRunSessionListener != null) + UnitTestModel.getInstance().removeTestRunSessionListener(fTestRunSessionListener); + + IHandlerService handlerService = getSite().getWorkbenchWindow().getService(IHandlerService.class); + handlerService.deactivateHandler(fRerunLastActivation); + handlerService.deactivateHandler(fRerunFailedFirstActivation); + setActiveTestRunSession(null); + + if (fProgressImages != null) + fProgressImages.dispose(); + getViewSite().getPage().removePartListener(fPartListener); + + disposeImages(); + if (fClipboard != null) + fClipboard.dispose(); + if (fViewMenuListener != null) { + getViewSite().getActionBars().getMenuManager().removeMenuListener(fViewMenuListener); + } + /* + * if (fDirtyListener != null) { + * JavaCore.removeElementChangedListener(fDirtyListener); fDirtyListener= null; + * } + */ + if (fFailureTrace != null) { + fFailureTrace.dispose(); + } + } + + private void disposeImages() { + for (Image imageToDispose : fImagesToDispose) { + imageToDispose.dispose(); + } + } + + private void postSyncRunnable(Runnable r) { + if (!isDisposed()) + getDisplay().syncExec(r); + } + + private void refreshCounters() { + // TODO: Inefficient. Either + // - keep a boolean fHasTestRun and update only on changes, or + // - improve components to only redraw on changes (once!). + + int startedCount; + int ignoredCount; + Integer totalCount; + int errorCount; + int failureCount; + int assumptionFailureCount; + boolean hasErrorsOrFailures; + boolean stopped; + + if (fTestRunSession != null) { + startedCount = fTestRunSession.countStartedTestCases(); + ignoredCount = fTestRunSession.getCurrentIgnoredCount(); + totalCount = fTestRunSession.getFinalTestCaseCount(); + errorCount = fTestRunSession.getCurrentErrorCount(); + failureCount = fTestRunSession.getCurrentFailureCount(); + assumptionFailureCount = fTestRunSession.getCurrentAssumptionFailureCount(); + hasErrorsOrFailures = errorCount + failureCount > 0; + stopped = fTestRunSession.isStopped(); + } else { + startedCount = 0; + ignoredCount = 0; + totalCount = null; + errorCount = 0; + failureCount = 0; + assumptionFailureCount = 0; + hasErrorsOrFailures = false; + stopped = false; + } + + fCounterPanel.setTotal(totalCount); + fCounterPanel.setRunValue(startedCount, ignoredCount, assumptionFailureCount); + fCounterPanel.setErrorValue(errorCount); + fCounterPanel.setFailureValue(failureCount); + + int ticksDone; + if (startedCount == 0) { + ticksDone = 0; + } else if (totalCount != null && startedCount == totalCount.intValue() + && fTestRunSession.getProgressState() == ProgressState.COMPLETED) { + ticksDone = totalCount.intValue(); + } else { + ticksDone = startedCount - 1; + } + + fProgressBar.reset(hasErrorsOrFailures, stopped, ticksDone, + totalCount != null ? totalCount.intValue() : ticksDone + 1); + } + + /** + * Queues a runnable that shows a test results view + */ + protected void postShowTestResultsView() { + postSyncRunnable(() -> { + if (isDisposed()) + return; + showTestResultsView(); + }); + } + + /** + * Makes the test results view visible + */ + public void showTestResultsView() { + IWorkbenchWindow window = getSite().getWorkbenchWindow(); + IWorkbenchPage page = window.getActivePage(); + TestRunnerViewPart testRunner = null; + + if (page != null) { + try { // show the result view + testRunner = (TestRunnerViewPart) page.findView(TestRunnerViewPart.NAME); + if (testRunner == null) { + IWorkbenchPart activePart = page.getActivePart(); + testRunner = (TestRunnerViewPart) page.showView(TestRunnerViewPart.NAME, null, + IWorkbenchPage.VIEW_VISIBLE); + // restore focus + page.activate(activePart); + } else { + page.bringToTop(testRunner); + } + } catch (PartInitException pie) { + UnitTestPlugin.log(pie); + } + } + } + + /** + * Shows an info message + */ + protected void doShowInfoMessage() { + if (fInfoMessage != null) { + setContentDescription(fInfoMessage); + fInfoMessage = null; + } + } + + /** + * Registers a test information message + * + * @param message an information message to register + */ + public void registerInfoMessage(String message) { + fInfoMessage = message; + } + + private SashForm createSashForm(Composite parent) { + fSashForm = new SashForm(parent, SWT.VERTICAL); + + ViewForm top = new ViewForm(fSashForm, SWT.NONE); + + Composite empty = new Composite(top, SWT.NONE); + empty.setLayout(new Layout() { + @Override + protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { + return new Point(1, 1); // (0, 0) does not work with super-intelligent ViewForm + } + + @Override + protected void layout(Composite composite, boolean flushCache) { + } + }); + top.setTopLeft(empty); // makes ViewForm draw the horizontal separator line ... + fTestViewer = new TestViewer(top, fClipboard, this); + top.setContent(fTestViewer.getTestViewerControl()); + + ViewForm bottom = new ViewForm(fSashForm, SWT.NONE); + + CLabel label = new CLabel(bottom, SWT.NONE); + label.setText(Messages.TestRunnerViewPart_label_failure); + label.setImage(fStackViewIcon); + bottom.setTopLeft(label); + ToolBar failureToolBar = new ToolBar(bottom, SWT.FLAT | SWT.WRAP); + bottom.setTopCenter(failureToolBar); + fFailureTrace = new FailureTraceUIBlock(bottom, fClipboard, this, failureToolBar); + bottom.setContent(fFailureTrace.getComposite()); + + fSashForm.setWeights(50, 50); + return fSashForm; + } + + private void clearStatus() { + getStatusLine().setMessage(null); + getStatusLine().setErrorMessage(null); + } + + @Override + public void setFocus() { + if (fTestViewer != null) + fTestViewer.getTestViewerControl().setFocus(); + } + + @Override + public void createPartControl(Composite parent) { + fParent = parent; + addResizeListener(parent); + fClipboard = new Clipboard(parent.getDisplay()); + + GridLayout gridLayout = new GridLayout(); + gridLayout.marginWidth = 0; + gridLayout.marginHeight = 0; + parent.setLayout(gridLayout); + +// fViewHistory= new RunnerViewHistory(); + configureToolBar(); + + fCounterComposite = createProgressCountPanel(parent); + fCounterComposite.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL)); + SashForm sashForm = createSashForm(parent); + sashForm.setLayoutData(new GridData(GridData.FILL_BOTH)); + + IActionBars actionBars = getViewSite().getActionBars(); + + fCopyAction = new UnitTestCopyAction(fFailureTrace, fClipboard); + fCopyAction.setActionDefinitionId(ActionFactory.COPY.getCommandId()); + actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), fCopyAction); + + fPasteAction = new UnitTesttPasteAction(parent.getShell(), fClipboard); + fPasteAction.setActionDefinitionId(ActionFactory.PASTE.getCommandId()); + actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(), fPasteAction); + + initPageSwitcher(); + addDropAdapter(parent); + + fOriginalViewImage = getTitleImage(); + fProgressImages = new ProgressIcons(fOriginalViewImage); + PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, IUnitTestHelpContextIds.RESULTS_VIEW); + + getViewSite().getPage().addPartListener(fPartListener); + + setFilterAndLayout(false, false, TestResultsLayout.HIERARCHICAL); + setShowExecutionTime(true); + if (fMemento != null) { + restoreLayoutState(fMemento); + } + fMemento = null; + + fTestRunSessionListener = new TestRunSessionListener(); + UnitTestModel.getInstance().addTestRunSessionListener(fTestRunSessionListener); + UnitTestModel.getInstance().addTestRunSessionListener(History.INSTANCE); + + // always show youngest test run in view. simulate "sessionAdded" event to do + // that + List<TestRunSession> testRunSessions = UnitTestModel.getInstance().getTestRunSessions(); + if (!testRunSessions.isEmpty()) { + fTestRunSessionListener.sessionAdded(testRunSessions.get(0)); + } + } + + private void addDropAdapter(Composite parent) { + DropTarget dropTarget = new DropTarget(parent, + DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK | DND.DROP_DEFAULT); + dropTarget.setTransfer(TextTransfer.getInstance()); + class DropAdapter extends DropTargetAdapter { + @Override + public void dragEnter(DropTargetEvent event) { + event.detail = DND.DROP_COPY; + event.feedback = DND.FEEDBACK_NONE; + } + + @Override + public void dragOver(DropTargetEvent event) { + event.detail = DND.DROP_COPY; + event.feedback = DND.FEEDBACK_NONE; + } + + @Override + public void dragOperationChanged(DropTargetEvent event) { + event.detail = DND.DROP_COPY; + event.feedback = DND.FEEDBACK_NONE; + } + + @Override + public void drop(final DropTargetEvent event) { + if (TextTransfer.getInstance().isSupportedType(event.currentDataType)) { + String url = (String) event.data; + importTestRunSession(url); + } + } + } + dropTarget.addDropListener(new DropAdapter()); + } + + private void initPageSwitcher() { + /* + * @SuppressWarnings("unused") PageSwitcher pageSwitcher= new PageSwitcher(this) + * { + * + * @Override public Object[] getPages() { return + * fViewHistory.getHistoryEntries().toArray(); } + * + * @Override public String getName(Object page) { return + * fViewHistory.getText((TestRunSession) page); } + * + * @Override public ImageDescriptor getImageDescriptor(Object page) { return + * fViewHistory.getImageDescriptor(page); } + * + * @Override public void activatePage(Object page) { + * fViewHistory.setActiveEntry((TestRunSession) page); } + * + * @Override public int getCurrentPageIndex() { return + * fViewHistory.getHistoryEntries().indexOf(fViewHistory.getCurrentEntry()); } + * }; + */ + } + + private void addResizeListener(Composite parent) { + parent.addControlListener(ControlListener.controlResizedAdapter(e -> { + computeOrientation(); + })); + } + + void computeOrientation() { + if (fOrientation != VIEW_ORIENTATION_AUTOMATIC) { + fCurrentOrientation = fOrientation; + setOrientation(fCurrentOrientation); + } else { + Point size = fParent.getSize(); + if (size.x != 0 && size.y != 0) { + if (size.x > size.y) + setOrientation(VIEW_ORIENTATION_HORIZONTAL); + else + setOrientation(VIEW_ORIENTATION_VERTICAL); + } + } + } + + private void configureToolBar() { + IActionBars actionBars = getViewSite().getActionBars(); + IToolBarManager toolBar = actionBars.getToolBarManager(); + IMenuManager viewMenu = actionBars.getMenuManager(); + fNextAction = new ShowNextFailureAction(this); + fNextAction.setEnabled(false); + actionBars.setGlobalActionHandler(ActionFactory.NEXT.getId(), fNextAction); + + fPreviousAction = new ShowPreviousFailureAction(this); + fPreviousAction.setEnabled(false); + actionBars.setGlobalActionHandler(ActionFactory.PREVIOUS.getId(), fPreviousAction); + fStopAction = new StopAction(); + fStopAction.setEnabled(false); + + fRerunLastTestAction = new RerunLastAction(); + IHandlerService handlerService = getSite().getWorkbenchWindow().getService(IHandlerService.class); + IHandler handler = new AbstractHandler() { + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + fRerunLastTestAction.run(); + return null; + } + + @Override + public boolean isEnabled() { + return fRerunLastTestAction.isEnabled(); + } + }; + fRerunLastActivation = handlerService.activateHandler(RERUN_LAST_COMMAND, handler); + + fRerunFailedCasesAction = new RerunFailedCasesAction(); + handler = new AbstractHandler() { + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + fRerunFailedCasesAction.run(); + return null; + } + + @Override + public boolean isEnabled() { + return fRerunFailedCasesAction.isEnabled(); + } + }; + fRerunFailedFirstActivation = handlerService.activateHandler(RERUN_FAILED_CASES_COMMAND, handler); + fEditLaunchConfigAction = new EditLaunchConfigurationAction() { + @Override + protected ILaunchConfiguration getLaunchConfiguration() { + return fTestRunSession != null ? fTestRunSession.getLaunch().getLaunchConfiguration() : null; + } + + @Override + protected String getMode() { + return rerunLaunchMode(); + } + + @Override + protected boolean isTerminated() { + return true; // always allow to re-run + } + }; + fEditLaunchConfigAction.setToolTipText(Messages.TestRunnerViewPart_editLaunchConfiguration); + fEditLaunchConfigAction.setImageDescriptor( + DebugPluginImages.getImageDescriptor(IDebugUIConstants.IMG_OBJS_MODIFICATION_WATCHPOINT)); + fEditLaunchConfigAction.setDisabledImageDescriptor( + DebugPluginImages.getImageDescriptor(IDebugUIConstants.IMG_OBJS_MODIFICATION_WATCHPOINT_DISABLED)); + + fFailuresOnlyFilterAction = new FailuresOnlyFilterAction(); + fIgnoredOnlyFilterAction = new IgnoredOnlyFilterAction(); + + fScrollLockAction = new ScrollLockAction(this); + fScrollLockAction.setChecked(!fAutoScroll); + + fToggleOrientationActions = new ToggleOrientationAction[] { + new ToggleOrientationAction(VIEW_ORIENTATION_VERTICAL), + new ToggleOrientationAction(VIEW_ORIENTATION_HORIZONTAL), + new ToggleOrientationAction(VIEW_ORIENTATION_AUTOMATIC) }; + + toolBar.add(fNextAction); + toolBar.add(fPreviousAction); + toolBar.add(fFailuresOnlyFilterAction); + toolBar.add(fIgnoredOnlyFilterAction); + toolBar.add(fScrollLockAction); + toolBar.add(new Separator()); + toolBar.add(fRerunLastTestAction); + toolBar.add(fRerunFailedCasesAction); + toolBar.add(fStopAction); + toolBar.add(fEditLaunchConfigAction); + toolBar.add(new Separator()); + IContributionItem historyIte = new CommandContributionItem(new CommandContributionItemParameter(getSite(), + HistoryHandler.COMMAND_ID, HistoryHandler.COMMAND_ID, SWT.PUSH)); + toolBar.add(historyIte); + + fShowTestHierarchyAction = new ShowTestHierarchyAction(); + fShowTimeAction = new ShowTimeAction(); + viewMenu.add(fShowTestHierarchyAction); + viewMenu.add(fShowTimeAction); + viewMenu.add(new Separator()); + + MenuManager layoutSubMenu = new MenuManager(Messages.TestRunnerViewPart_layout_menu); + for (ToggleOrientationAction toggleOrientationAction : fToggleOrientationActions) { + layoutSubMenu.add(toggleOrientationAction); + } + viewMenu.add(layoutSubMenu); + MenuManager sortSubmenu = new MenuManager(Messages.TestRunnerViewPart_sort); + sortSubmenu.add(new SortAction(true)); + sortSubmenu.add(new SortAction(false)); + viewMenu.add(sortSubmenu); + viewMenu.add(new Separator()); + + viewMenu.add(fFailuresOnlyFilterAction); + viewMenu.add(fIgnoredOnlyFilterAction); + + fActivateOnErrorAction = new ActivateOnErrorAction(); + viewMenu.add(fActivateOnErrorAction); + fViewMenuListener = manager -> fActivateOnErrorAction.update(); + + viewMenu.addMenuListener(fViewMenuListener); + + actionBars.updateActionBars(); + } + + private IStatusLineManager getStatusLine() { + // we want to show messages globally hence we + // have to go through the active part + IViewSite site = getViewSite(); + IWorkbenchPage page = site.getPage(); + IWorkbenchPart activePart = page.getActivePart(); + + if (activePart instanceof IViewPart) { + IViewPart activeViewPart = (IViewPart) activePart; + IViewSite activeViewSite = activeViewPart.getViewSite(); + return activeViewSite.getActionBars().getStatusLineManager(); + } + + if (activePart instanceof IEditorPart) { + IEditorPart activeEditorPart = (IEditorPart) activePart; + IEditorActionBarContributor contributor = activeEditorPart.getEditorSite().getActionBarContributor(); + if (contributor instanceof EditorActionBarContributor) + return ((EditorActionBarContributor) contributor).getActionBars().getStatusLineManager(); + } + // no active part + return getViewSite().getActionBars().getStatusLineManager(); + } + + /** + * Creates a progress count panel + * + * @param parent a parent composite + * @return a progress count ppanel composite object + */ + protected Composite createProgressCountPanel(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(); + composite.setLayout(layout); + setCounterColumns(layout); + + fCounterPanel = new CounterPanel(composite); + fCounterPanel.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL)); + fProgressBar = new UnitTestProgressBar(composite); + fProgressBar.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL)); + return composite; + } + + /** + * Handles the test element selection + * + * @param test a selected test element + */ + public void handleTestSelected(TestElement test) { + showFailure(test); + fCopyAction.handleTestSelected(test); + } + + private void showFailure(final TestElement test) { + postSyncRunnable(() -> { + if (!isDisposed()) { + fFailureTrace.showFailure(test); + } + }); + } + + /** + * Returns a current test run session object + * + * @return the current test run session, or <code>null</code> + */ + public TestRunSession getCurrentTestRunSession() { + return fTestRunSession; + } + + private boolean isDisposed() { + return fIsDisposed || fCounterPanel.isDisposed(); + } + + private Display getDisplay() { + return getViewSite().getShell().getDisplay(); + } + + @Override + public Image getTitleImage() { + if (fOriginalViewImage == null) { + fOriginalViewImage = super.getTitleImage(); + } + + if (fViewImage == null) { + return super.getTitleImage(); + } + return fViewImage; + } + + void codeHasChanged() { + /* + * if (fDirtyListener != null) { + * JavaCore.removeElementChangedListener(fDirtyListener); fDirtyListener= null; + * } + */ + if (fViewImage == fTestRunOKIcon) + fViewImage = fTestRunOKDirtyIcon; + else if (fViewImage == fTestRunFailIcon) + fViewImage = fTestRunFailDirtyIcon; + + Runnable r = () -> { + if (isDisposed()) + return; + firePropertyChange(IWorkbenchPart.PROP_TITLE); + }; + if (!isDisposed()) + getDisplay().asyncExec(r); + } + + private void postSyncProcessChanges() { + postSyncRunnable(this::processChangesInUI); + } + + /** + * Warns on content change + */ + public void warnOfContentChange() { + IWorkbenchSiteProgressService service = getProgressService(); + if (service != null) + service.warnOfContentChange(); + } + + /** + * Indicates if the last test launch is kept alive + * + * @return <code>true</code> in case of the last test launch is kept alive, + * otherwise returns <code>false</code> + */ + public boolean lastLaunchStillRunning() { + return fTestRunSession != null && !fTestRunSession.getLaunch().isTerminated(); + } + + private void setOrientation(int orientation) { + if ((fSashForm == null) || fSashForm.isDisposed()) + return; + boolean horizontal = orientation == VIEW_ORIENTATION_HORIZONTAL; + fSashForm.setOrientation(horizontal ? SWT.HORIZONTAL : SWT.VERTICAL); + for (ToggleOrientationAction toggleOrientationAction : fToggleOrientationActions) + toggleOrientationAction.setChecked(fOrientation == toggleOrientationAction.getOrientation()); + fCurrentOrientation = orientation; + GridLayout layout = (GridLayout) fCounterComposite.getLayout(); + setCounterColumns(layout); + fParent.layout(); + } + + private void setCounterColumns(GridLayout layout) { + if (fCurrentOrientation == VIEW_ORIENTATION_HORIZONTAL) + layout.numColumns = 2; + else + layout.numColumns = 1; + } + + static boolean getShowOnErrorOnly() { + return Platform.getPreferencesService().getBoolean(UnitTestPlugin.PLUGIN_ID, + UnitTestPreferencesConstants.SHOW_ON_ERROR_ONLY, false, null); + } + + static void importTestRunSession(final String url) { + try { + PlatformUI.getWorkbench().getProgressService() + .busyCursorWhile(monitor -> UnitTestModel.getInstance().importTestRunSession(url, monitor)); + } catch (InterruptedException e) { + // cancelled + } catch (InvocationTargetException e) { + CoreException ce = (CoreException) e.getCause(); + StatusManager.getManager().handle(ce.getStatus(), StatusManager.SHOW | StatusManager.LOG); + } + } + + /** + * Returns the Failure Trace UI Block + * + * @return the current failure trace OI block + */ + public FailureTraceUIBlock getFailureTrace() { + return fFailureTrace; + } + + void setShowFailuresOnly(boolean failuresOnly) { + setFilterAndLayout(failuresOnly, false /* ignoredOnly must be off */, fLayout); + } + + void setShowIgnoredOnly(boolean ignoredOnly) { + setFilterAndLayout(false /* failuresOnly must be off */, ignoredOnly, fLayout); + } + + private void setFilterAndLayout(boolean failuresOnly, boolean ignoredOnly, TestResultsLayout layoutMode) { + fShowTestHierarchyAction.setChecked(layoutMode == TestResultsLayout.HIERARCHICAL); + fLayout = layoutMode; + fFailuresOnlyFilterAction.setChecked(failuresOnly); + fIgnoredOnlyFilterAction.setChecked(ignoredOnly); + fTestViewer.setShowFailuresOrIgnoredOnly(failuresOnly, ignoredOnly, layoutMode); + updateNextPreviousActions(); + } + + private void setShowExecutionTime(boolean showTime) { + fTestViewer.setShowTime(showTime); + fShowTimeAction.setChecked(showTime); + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionLabelProvider.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionLabelProvider.java new file mode 100644 index 000000000..d7a7e98fb --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionLabelProvider.java @@ -0,0 +1,186 @@ +/******************************************************************************* + * Copyright (c) 2000, 2017 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.unittest.internal.ui; + +import java.text.MessageFormat; +import java.time.Duration; + +import org.eclipse.unittest.internal.model.Status; +import org.eclipse.unittest.internal.model.TestCaseElement; +import org.eclipse.unittest.internal.model.TestElement; +import org.eclipse.unittest.internal.ui.TestRunnerViewPart.TestResultsLayout; +import org.eclipse.unittest.model.ITestElement; +import org.eclipse.unittest.model.ITestRunSession; + +import org.eclipse.swt.graphics.Image; + +import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.LabelProviderChangedEvent; +import org.eclipse.jface.viewers.StyledCellLabelProvider; +import org.eclipse.jface.viewers.StyledString; + +/** + * A Test Session label provider implementation. + */ +class TestSessionLabelProvider extends LabelProvider implements IStyledLabelProvider { + + private final TestRunnerViewPart fTestRunnerPart; + private final TestResultsLayout fLayoutMode; + + private boolean fShowTime; + + /** + * Constructs Test Session Provider object. + * + * @param testRunnerPart a test runner view part object + * @param layoutMode a layout mode + */ + public TestSessionLabelProvider(TestRunnerViewPart testRunnerPart, TestResultsLayout layoutMode) { + fTestRunnerPart = testRunnerPart; + fLayoutMode = layoutMode; + fShowTime = true; + } + + @Override + public StyledString getStyledText(Object element) { + if (!(element instanceof ITestElement)) { + return new StyledString(element.toString()); + } + TestElement testElement = (TestElement) element; + StyledString text = new StyledString(testElement.getDisplayName()); + if (fLayoutMode == TestRunnerViewPart.TestResultsLayout.HIERARCHICAL) { + if (testElement.getParentContainer() instanceof ITestRunSession) { + String displayName = fTestRunnerPart.getDisplayName(); + if (displayName != null) { + String decorated = MessageFormat.format(Messages.TestSessionLabelProvider_testName_RunnerVersion, + text.getString(), displayName); + text = StyledCellLabelProvider.styleDecoratedString(decorated, StyledString.QUALIFIER_STYLER, text); + } + } + + } else { + if (element instanceof TestCaseElement) { + String decorated = getTextForFlatLayout((TestCaseElement) testElement, text.getString()); + text = StyledCellLabelProvider.styleDecoratedString(decorated, StyledString.QUALIFIER_STYLER, text); + } + } + return addElapsedTime(text, testElement.getDuration()); + } + + private String getTextForFlatLayout(TestCaseElement testCaseElement, String label) { + String parentName; + String parentDisplayName = testCaseElement.getParent().getDisplayName(); + if (parentDisplayName != null) { + parentName = parentDisplayName; + } else { + parentName = testCaseElement.getTestName(); + } + return MessageFormat.format(Messages.TestSessionLabelProvider_testMethodName_className, label, + BasicElementLabels.getJavaElementName(parentName)); + } + + private StyledString addElapsedTime(StyledString styledString, Duration duration) { + String string = styledString.getString(); + String decorated = addElapsedTime(string, duration); + return StyledCellLabelProvider.styleDecoratedString(decorated, StyledString.COUNTER_STYLER, styledString); + } + + private String addElapsedTime(String string, Duration duration) { + if (!fShowTime || duration == null) { + return string; + } + return MessageFormat.format(Messages.TestSessionLabelProvider_testName_elapsedTimeInSeconds, string, + Double.valueOf(duration.toNanos() / 1.0e9)); + } + + @Override + public String getText(Object element) { + if (!(element instanceof ITestElement)) { + return element.toString(); + } + TestElement testElement = (TestElement) element; + String label = testElement.getDisplayName(); + if (fLayoutMode == TestRunnerViewPart.TestResultsLayout.HIERARCHICAL) { + if (testElement instanceof ITestRunSession || (testElement.getParent() instanceof ITestRunSession + && testElement.getParent().getChildren().size() <= 1)) { + String displayName = fTestRunnerPart.getDisplayName(); + if (displayName != null) { + label = MessageFormat.format(Messages.TestSessionLabelProvider_testName_RunnerVersion, label, + displayName); + } + } + } else { + if (element instanceof TestCaseElement) { + label = getTextForFlatLayout((TestCaseElement) testElement, label); + } + } + return addElapsedTime(label, testElement.getDuration()); + } + + @Override + public Image getImage(Object element) { + if (element instanceof TestElement && ((TestElement) element).isAssumptionFailure()) + return fTestRunnerPart.fTestAssumptionFailureIcon; + + if (element instanceof TestCaseElement) { + TestCaseElement testCaseElement = ((TestCaseElement) element); + if (testCaseElement.isIgnored()) + return fTestRunnerPart.fTestIgnoredIcon; + + Status status = testCaseElement.getStatus(); + if (status.isNotRun()) + return fTestRunnerPart.fTestIcon; + else if (status.isRunning()) + return fTestRunnerPart.fTestRunningIcon; + else if (status.isError()) + return fTestRunnerPart.fTestErrorIcon; + else if (status.isFailure()) + return fTestRunnerPart.fTestFailIcon; + else if (status.isOK()) + return fTestRunnerPart.fTestOkIcon; + else + throw new IllegalStateException(element.toString()); + } else if (element instanceof TestElement) { // suite or session + Status status = ((TestElement) element).getStatus(); + if (status.isNotRun()) + return fTestRunnerPart.fSuiteIcon; + else if (status.isRunning()) + return fTestRunnerPart.fSuiteRunningIcon; + else if (status.isError()) + return fTestRunnerPart.fSuiteErrorIcon; + else if (status.isFailure()) + return fTestRunnerPart.fSuiteFailIcon; + else if (status.isOK()) + return fTestRunnerPart.fSuiteOkIcon; + else + throw new IllegalStateException(element.toString()); + } else { + throw new IllegalArgumentException(String.valueOf(element)); + } + } + + /** + * Makes the label provider to show time on the generated labels + * + * @param showTime <code>true</code> in case a time value is to be shown, + * otherwise - <code>false</code> + */ + public void setShowTime(boolean showTime) { + fShowTime = showTime; + fireLabelProviderChanged(new LabelProviderChangedEvent(this)); + } + +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionTableContentProvider.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionTableContentProvider.java new file mode 100644 index 000000000..93dfb7cee --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionTableContentProvider.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2000, 2010 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.unittest.internal.ui; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.unittest.internal.model.TestCaseElement; +import org.eclipse.unittest.internal.model.TestSuiteElement; +import org.eclipse.unittest.model.ITestElement; +import org.eclipse.unittest.model.ITestRunSession; +import org.eclipse.unittest.model.ITestSuiteElement; + +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** + * A test session table content provider + */ +public class TestSessionTableContentProvider implements IStructuredContentProvider { + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + @Override + public Object[] getElements(Object inputElement) { + ITestRunSession session = (ITestRunSession) inputElement; + List<ITestElement> all = new ArrayList<>(); + addAll(all, session); + return all.toArray(); + } + + private void addAll(List<ITestElement> all, ITestSuiteElement suite) { + for (ITestElement element : suite.getChildren()) { + if (element instanceof TestSuiteElement) { + if (((TestSuiteElement) element).getSuiteStatus().isErrorOrFailure()) + all.add(element); // add failed suite to flat list too + addAll(all, (TestSuiteElement) element); + } else if (element instanceof TestCaseElement) { + all.add(element); + } + } + } + + @Override + public void dispose() { + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionTreeContentProvider.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionTreeContentProvider.java new file mode 100644 index 000000000..239ac6953 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestSessionTreeContentProvider.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.unittest.internal.ui; + +import org.eclipse.unittest.internal.model.TestElement; +import org.eclipse.unittest.internal.model.TestRunSession; +import org.eclipse.unittest.internal.model.TestSuiteElement; + +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** + * A test session tree content provider + */ +public class TestSessionTreeContentProvider implements ITreeContentProvider { + + private static final Object[] NO_CHILDREN = new Object[0]; + + @Override + public void dispose() { + // nothing to dispose + } + + @Override + public Object[] getChildren(Object parentElement) { + if (parentElement instanceof TestSuiteElement) { + return ((TestSuiteElement) parentElement).getChildren().toArray(); + } else { + return NO_CHILDREN; + } + } + + @Override + public Object[] getElements(Object inputElement) { + TestRunnerViewPart part = (TestRunnerViewPart) inputElement; + TestRunSession session = part.getCurrentTestRunSession(); + return new Object[] { session.getChildren().size() == 1 ? session.getChildren().get(0) : session }; + } + + @Override + public Object getParent(Object element) { + return ((TestElement) element).getParent(); + } + + @Override + public boolean hasChildren(Object element) { + if (element instanceof TestSuiteElement) { + return !((TestSuiteElement) element).getChildren().isEmpty(); + } else { + return false; + } + } + + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // nothing + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestViewer.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestViewer.java new file mode 100644 index 000000000..a36384d9a --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TestViewer.java @@ -0,0 +1,871 @@ +/******************************************************************************* + * Copyright (c) 2000, 2018 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.unittest.internal.ui; + +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; + +import org.eclipse.unittest.internal.UnitTestPlugin; +import org.eclipse.unittest.internal.model.Status; +import org.eclipse.unittest.internal.model.TestCaseElement; +import org.eclipse.unittest.internal.model.TestElement; +import org.eclipse.unittest.internal.model.TestRunSession; +import org.eclipse.unittest.internal.model.TestSuiteElement; +import org.eclipse.unittest.internal.ui.TestRunnerViewPart.TestResultsLayout; +import org.eclipse.unittest.model.ITestCaseElement; +import org.eclipse.unittest.model.ITestElement; +import org.eclipse.unittest.model.ITestElement.Result; +import org.eclipse.unittest.model.ITestRunSession; +import org.eclipse.unittest.model.ITestSuiteElement; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.TableItem; + +import org.eclipse.core.runtime.CoreException; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.viewers.AbstractTreeViewer; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.jface.viewers.ViewerFilter; + +import org.eclipse.ui.IWorkbenchActionConstants; +import org.eclipse.ui.part.PageBook; + +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchManager; + +/** + * A Test Viewer implementation + */ +class TestViewer { + + private final class TestSelectionListener implements ISelectionChangedListener { + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) fSelectionProvider.getSelection(); + TestElement testElement = null; + if (selection.size() == 1) { + testElement = (TestElement) selection.getFirstElement(); + } + fTestRunnerPart.handleTestSelected(testElement); + } + } + + private final class TestOpenListener extends SelectionAdapter { + @Override + public void widgetDefaultSelected(SelectionEvent e) { + handleDefaultSelected(); + } + } + + private final class FailuresOnlyFilter extends ViewerFilter { + @Override + public boolean select(Viewer viewer, Object parentElement, Object element) { + return select(((TestElement) element)); + } + + public boolean select(TestElement testElement) { + Status status = testElement.getStatus(); + if (status.isErrorOrFailure()) + return true; + else + return !fTestRunSession.isRunning() && status == Status.RUNNING; // rerunning + } + } + + private final class IgnoredOnlyFilter extends ViewerFilter { + @Override + public boolean select(Viewer viewer, Object parentElement, Object element) { + return select(((TestElement) element)); + } + + public boolean select(TestElement testElement) { + if (hasIgnoredInTestResult(testElement)) + return true; + else + return !fTestRunSession.isRunning() && testElement.getStatus() == Status.RUNNING; // rerunning + } + + /** + * Checks whether a test was skipped i.e. it was ignored (<code>@Ignored</code>) + * or had any assumption failure. + * + * @param testElement the test element (a test suite or a single test case) + * + * @return <code>true</code> if the test element or any of its children has + * {@link Result#IGNORED} test result + */ + private boolean hasIgnoredInTestResult(TestElement testElement) { + if (testElement instanceof TestSuiteElement) { + List<TestElement> children = ((TestSuiteElement) testElement).getChildren(); + for (TestElement child : children) { + boolean hasIgnoredTestResult = hasIgnoredInTestResult(child); + if (hasIgnoredTestResult) { + return true; + } + } + return false; + } + + return testElement.getTestResult(false) == Result.IGNORED; + } + } + + private static class ReverseList<E> extends AbstractList<E> { + private final List<E> fList; + + public ReverseList(List<E> list) { + fList = list; + } + + @Override + public E get(int index) { + return fList.get(fList.size() - index - 1); + } + + @Override + public int size() { + return fList.size(); + } + } + + private class ExpandAllAction extends Action { + public ExpandAllAction() { + setText(Messages.ExpandAllAction_text); + setToolTipText(Messages.ExpandAllAction_tooltip); + } + + @Override + public void run() { + fTreeViewer.expandAll(); + } + } + + private class CollapseAllAction extends Action { + public CollapseAllAction() { + setText(Messages.CollapseAllAction_text); + setToolTipText(Messages.CollapseAllAction_tooltip); + } + + @Override + public void run() { + fTreeViewer.collapseAll(); + } + } + + /** + * Compares two {@link TestElement}s: - {@link TestSuiteElement}s are placed on + * top of {@link TestCaseElement}s - TestElements are alphabetically ordered(by + * their names) + */ + private static final ViewerComparator TEST_ELEMENT_ALPHABETIC_ORDER = new ViewerComparator() { + @Override + public int compare(Viewer viewer, Object e1, Object e2) { + // Show test suites on top of test messages + int weight1 = (e1 instanceof ITestSuiteElement) ? 0 : 1; + int weight2 = (e2 instanceof ITestSuiteElement) ? 0 : 1; + if (weight1 != weight2) { + return weight1 - weight2; + } + // Compare by element names + return ((TestElement) e1).getTestName().compareTo(((TestElement) e2).getTestName()); + } + }; + + private final FailuresOnlyFilter fFailuresOnlyFilter = new FailuresOnlyFilter(); + private final IgnoredOnlyFilter fIgnoredOnlyFilter = new IgnoredOnlyFilter(); + + private final TestRunnerViewPart fTestRunnerPart; + private final Clipboard fClipboard; + + private PageBook fViewerbook; + private TreeViewer fTreeViewer; + private TestSessionTreeContentProvider fTreeContentProvider; + private TestSessionLabelProvider fTreeLabelProvider; + private TableViewer fTableViewer; + private TestSessionLabelProvider fTableLabelProvider; + private SelectionProviderMediator fSelectionProvider; + + private TestResultsLayout fLayoutMode; + private boolean fTreeHasFilter; + private boolean fTableHasFilter; + + private TestRunSession fTestRunSession; + + private boolean fTreeNeedsRefresh; + private boolean fTableNeedsRefresh; + private HashSet<ITestElement> fNeedUpdate; + private ITestCaseElement fAutoScrollTarget; + + private LinkedList<ITestSuiteElement> fAutoClose; + private HashSet<ITestSuiteElement> fAutoExpand; + + /** + * Constructs a Test Viewer object + * + * @param parent a parent composite + * @param clipboard a {@link Clipboard} instance + * @param runner a Test Runner view part + */ + public TestViewer(Composite parent, Clipboard clipboard, TestRunnerViewPart runner) { + fTestRunnerPart = runner; + fClipboard = clipboard; + + fLayoutMode = TestRunnerViewPart.TestResultsLayout.HIERARCHICAL; + + createTestViewers(parent); + + registerViewersRefresh(); + + initContextMenu(); + } + + private void createTestViewers(Composite parent) { + fViewerbook = new PageBook(parent, SWT.NULL); + + fTreeViewer = new TreeViewer(fViewerbook, SWT.V_SCROLL | SWT.SINGLE); + fTreeViewer.setUseHashlookup(true); + fTreeContentProvider = new TestSessionTreeContentProvider(); + fTreeViewer.setContentProvider(fTreeContentProvider); + fTreeLabelProvider = new TestSessionLabelProvider(fTestRunnerPart, + TestRunnerViewPart.TestResultsLayout.HIERARCHICAL); +// fTreeViewer.setLabelProvider(new ColoringLabelProvider(fTreeLabelProvider)); + fTreeViewer.setLabelProvider(fTreeLabelProvider); + + fTableViewer = new TableViewer(fViewerbook, SWT.V_SCROLL | SWT.H_SCROLL | SWT.SINGLE); + fTableViewer.setUseHashlookup(true); + TestSessionTableContentProvider fTableContentProvider = new TestSessionTableContentProvider(); + fTableViewer.setContentProvider(fTableContentProvider); + fTableLabelProvider = new TestSessionLabelProvider(fTestRunnerPart, TestRunnerViewPart.TestResultsLayout.FLAT); +// fTableViewer.setLabelProvider(new ColoringLabelProvider(fTableLabelProvider)); + fTableViewer.setLabelProvider(fTableLabelProvider); + + fSelectionProvider = new SelectionProviderMediator(new StructuredViewer[] { fTreeViewer, fTableViewer }, + fTreeViewer); + fSelectionProvider.addSelectionChangedListener(new TestSelectionListener()); + TestOpenListener testOpenListener = new TestOpenListener(); + fTreeViewer.getTree().addSelectionListener(testOpenListener); + fTableViewer.getTable().addSelectionListener(testOpenListener); + + fTestRunnerPart.getSite().setSelectionProvider(fSelectionProvider); + + fViewerbook.showPage(fTreeViewer.getTree()); + } + + private void initContextMenu() { + MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$ + menuMgr.setRemoveAllWhenShown(true); + menuMgr.addMenuListener(this::handleMenuAboutToShow); + fTestRunnerPart.getSite().registerContextMenu(menuMgr, fSelectionProvider); + Menu menu = menuMgr.createContextMenu(fViewerbook); + fTreeViewer.getTree().setMenu(menu); + fTableViewer.getTable().setMenu(menu); + } + + void handleMenuAboutToShow(IMenuManager manager) { + IStructuredSelection selection = (IStructuredSelection) fSelectionProvider.getSelection(); + if (!selection.isEmpty()) { + TestElement testElement = (TestElement) selection.getFirstElement(); + + if (testElement instanceof TestSuiteElement) { + TestSuiteElement testSuiteElement = (TestSuiteElement) testElement; + IAction openTestAction = testSuiteElement.getTestRunSession().getTestViewSupport() + .getOpenTestAction(fTestRunnerPart.getSite().getShell(), testSuiteElement); + if (openTestAction != null) { + manager.add(openTestAction); + } + manager.add(new Separator()); + if (!fTestRunnerPart.lastLaunchStillRunning()) { + addRerunActions(manager, testSuiteElement); + } + } else { + TestCaseElement testCaseElement = (TestCaseElement) testElement; + IAction openTestAction = testElement.getTestRunSession().getTestViewSupport() + .getOpenTestAction(fTestRunnerPart.getSite().getShell(), testCaseElement); + if (openTestAction != null) { + manager.add(openTestAction); + } + manager.add(new Separator()); + addRerunActions(manager, testCaseElement); + } + if (fLayoutMode == TestRunnerViewPart.TestResultsLayout.HIERARCHICAL) { + manager.add(new Separator()); + manager.add(new ExpandAllAction()); + manager.add(new CollapseAllAction()); + } + + } + if (fTestRunSession != null + && fTestRunSession.getCurrentFailureCount() + fTestRunSession.getCurrentErrorCount() > 0) { + if (fLayoutMode != TestRunnerViewPart.TestResultsLayout.HIERARCHICAL) { + manager.add(new Separator()); + } + manager.add(new CopyFailureListAction(fTestRunnerPart, fClipboard)); + } + manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); + manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS + "-end")); //$NON-NLS-1$ + } + + private void addRerunActions(IMenuManager manager, TestElement testCaseElement) { + ILaunchConfiguration rerunLaunchConfiguration = testCaseElement.getTestRunSession().getTestViewSupport() + .getRerunLaunchConfiguration(Collections.singletonList(testCaseElement)); + if (rerunLaunchConfiguration == null) { + return; + } + if (fTestRunnerPart.lastLaunchStillRunning()) { + manager.add(new RerunAction(rerunLaunchConfiguration, ILaunchManager.RUN_MODE)); + } else { + try { + rerunLaunchConfiguration.getType().getSupportedModeCombinations().stream() // + .filter(modes -> modes.size() == 1) // + .flatMap(Collection::stream) // + .forEach(mode -> manager.add(new RerunAction(rerunLaunchConfiguration, mode))); + } catch (CoreException e) { + UnitTestPlugin.log(e); + } + } + } + + /** + * Returns the tree viewer control + * + * @return tree viewer control object + */ + public Control getTestViewerControl() { + return fViewerbook; + } + + /** + * Registers a given active test session + * + * @param testRunSession a test session object + */ + public synchronized void registerActiveSession(TestRunSession testRunSession) { + fTestRunSession = testRunSession; + registerAutoScrollTarget(null); + registerViewersRefresh(); + } + + void handleDefaultSelected() { + IStructuredSelection selection = (IStructuredSelection) fSelectionProvider.getSelection(); + if (selection.size() != 1) + return; + + TestElement testElement = (TestElement) selection.getFirstElement(); + IAction action; + if (testElement instanceof ITestSuiteElement) { + action = testElement.getTestRunSession().getTestViewSupport() + .getOpenTestAction(fTestRunnerPart.getSite().getShell(), (ITestSuiteElement) testElement); + } else if (testElement instanceof ITestCaseElement) { + action = testElement.getTestRunSession().getTestViewSupport() + .getOpenTestAction(fTestRunnerPart.getSite().getShell(), (ITestCaseElement) testElement); + } else { + throw new IllegalStateException(String.valueOf(testElement)); + } + + if (action != null && action.isEnabled()) + action.run(); + } + + /** + * Tunes the label providers to show time on the generated labels + * + * @param showTime <code>true</code> in case a time value is to be shown, + * otherwise - <code>false</code> + */ + public synchronized void setShowTime(boolean showTime) { + try { + fViewerbook.setRedraw(false); + fTreeLabelProvider.setShowTime(showTime); + fTableLabelProvider.setShowTime(showTime); + } finally { + fViewerbook.setRedraw(true); + } + } + + /** + * It makes sense to display either failed or ignored tests, not both together. + * + * @param failuresOnly whether to show only failed tests + * @param ignoredOnly whether to show only skipped tests + * @param layoutMode the layout mode + */ + public synchronized void setShowFailuresOrIgnoredOnly(boolean failuresOnly, boolean ignoredOnly, + TestResultsLayout layoutMode) { + /* + * Management of fTreeViewer and fTableViewer + * ****************************************** - invisible viewer is updated on + * registerViewerUpdate unless its f*NeedsRefresh is true - invisible viewer is + * not refreshed upfront - on layout change, new viewer is refreshed if + * necessary - filter only applies to "current" layout mode / viewer + */ + try { + fViewerbook.setRedraw(false); + + IStructuredSelection selection = null; + boolean switchLayout = layoutMode != fLayoutMode; + if (switchLayout) { + selection = (IStructuredSelection) fSelectionProvider.getSelection(); + if (layoutMode == TestRunnerViewPart.TestResultsLayout.HIERARCHICAL) { + if (fTreeNeedsRefresh) { + clearUpdateAndExpansion(); + } + } else { + if (fTableNeedsRefresh) { + clearUpdateAndExpansion(); + } + } + fLayoutMode = layoutMode; + fViewerbook.showPage(getActiveViewer().getControl()); + } + // avoid realizing all TableItems, especially in flat mode! + StructuredViewer viewer = getActiveViewer(); + if (failuresOnly || ignoredOnly) { + if (getActiveViewerHasFilter()) { + // For simplicity clear both filters (only one of them is used) + viewer.removeFilter(fFailuresOnlyFilter); + viewer.removeFilter(fIgnoredOnlyFilter); + } + setActiveViewerHasFilter(true); + viewer.setInput(null); + // Set either the failures or the skipped tests filter + ViewerFilter filter = fFailuresOnlyFilter; + if (ignoredOnly == true) { + filter = fIgnoredOnlyFilter; + } + viewer.addFilter(filter); + setActiveViewerNeedsRefresh(true); + + } else { + if (getActiveViewerHasFilter()) { + setActiveViewerNeedsRefresh(true); + setActiveViewerHasFilter(false); + viewer.setInput(null); + viewer.removeFilter(fIgnoredOnlyFilter); + viewer.removeFilter(fFailuresOnlyFilter); + } + } + processChangesInUI(); + + if (selection != null) { + // workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=125708 + // (ITreeSelection not adapted if TreePaths changed): + StructuredSelection flatSelection = new StructuredSelection(selection.toList()); + fSelectionProvider.setSelection(flatSelection, true); + } + + } finally { + fViewerbook.setRedraw(true); + } + } + + private boolean getActiveViewerHasFilter() { + if (fLayoutMode == TestRunnerViewPart.TestResultsLayout.HIERARCHICAL) + return fTreeHasFilter; + else + return fTableHasFilter; + } + + private void setActiveViewerHasFilter(boolean filter) { + if (fLayoutMode == TestRunnerViewPart.TestResultsLayout.HIERARCHICAL) + fTreeHasFilter = filter; + else + fTableHasFilter = filter; + } + + private StructuredViewer getActiveViewer() { + if (fLayoutMode == TestRunnerViewPart.TestResultsLayout.HIERARCHICAL) + return fTreeViewer; + else + return fTableViewer; + } + + private boolean getActiveViewerNeedsRefresh() { + if (fLayoutMode == TestRunnerViewPart.TestResultsLayout.HIERARCHICAL) + return fTreeNeedsRefresh; + else + return fTableNeedsRefresh; + } + + private void setActiveViewerNeedsRefresh(boolean needsRefresh) { + if (fLayoutMode == TestRunnerViewPart.TestResultsLayout.HIERARCHICAL) + fTreeNeedsRefresh = needsRefresh; + else + fTableNeedsRefresh = needsRefresh; + } + + /** + * To be called periodically by the TestRunnerViewPart (in the UI thread). + */ + public void processChangesInUI() { + if (fTestRunSession == null) { + registerViewersRefresh(); + fTreeNeedsRefresh = false; + fTableNeedsRefresh = false; + fTreeViewer.setInput(null); + fTableViewer.setInput(null); + return; + } + + StructuredViewer viewer = getActiveViewer(); + if (getActiveViewerNeedsRefresh()) { + clearUpdateAndExpansion(); + setActiveViewerNeedsRefresh(false); + viewer.setInput(fTestRunnerPart); + } else { + Object[] toUpdate; + synchronized (this) { + toUpdate = fNeedUpdate.toArray(); + fNeedUpdate.clear(); + } + if (!fTreeNeedsRefresh && toUpdate.length > 0) { + if (fTreeHasFilter) + for (Object element : toUpdate) + updateElementInTree((TestElement) element); + else { + HashSet<Object> toUpdateWithParents = new HashSet<>(); + toUpdateWithParents.addAll(Arrays.asList(toUpdate)); + for (Object element : toUpdate) { + ITestElement parent = ((ITestElement) element).getParent(); + while (parent != null) { + toUpdateWithParents.add(parent); + parent = parent.getParent(); + } + } + fTreeViewer.update(toUpdateWithParents.toArray(), null); + } + } + if (!fTableNeedsRefresh && toUpdate.length > 0) { + if (fTableHasFilter) + for (Object element : toUpdate) + updateElementInTable((TestElement) element); + else + fTableViewer.update(toUpdate, null); + } + } + autoScrollInUI(); + } + + private void updateElementInTree(final TestElement testElement) { + if (isShown(testElement)) { + updateShownElementInTree(testElement); + } else { + TestElement current = testElement; + do { + if (fTreeViewer.testFindItem(current) != null) + fTreeViewer.remove(current); + current = current.getParent(); + } while (!(current instanceof ITestRunSession) && !isShown(current)); + + while (current != null && !(current instanceof ITestRunSession)) { + fTreeViewer.update(current, null); + current = current.getParent(); + } + } + } + + private void updateShownElementInTree(ITestElement testElement) { + if (testElement == null || testElement instanceof ITestRunSession) // paranoia null check + return; + + ITestSuiteElement parent = testElement.getParent(); + updateShownElementInTree(parent); // make sure parent is shown and up-to-date + + if (fTreeViewer.testFindItem(testElement) == null) { + fTreeViewer.add(parent, testElement); // if not yet in tree: add + } else { + fTreeViewer.update(testElement, null); // if in tree: update + } + } + + private void updateElementInTable(TestElement element) { + if (isShown(element)) { + if (fTableViewer.testFindItem(element) == null) { + TestElement previous = getNextFailure(element, false); + int insertionIndex = -1; + if (previous != null) { + TableItem item = (TableItem) fTableViewer.testFindItem(previous); + if (item != null) + insertionIndex = fTableViewer.getTable().indexOf(item); + } + fTableViewer.insert(element, insertionIndex); + } else { + fTableViewer.update(element, null); + } + } else { + fTableViewer.remove(element); + } + } + + private boolean isShown(TestElement current) { + return fFailuresOnlyFilter.select(current); + } + + private void autoScrollInUI() { + if (!fTestRunnerPart.isAutoScroll()) { + clearAutoExpand(); + fAutoClose.clear(); + return; + } + + if (fLayoutMode == TestRunnerViewPart.TestResultsLayout.FLAT) { + if (fAutoScrollTarget != null) + fTableViewer.reveal(fAutoScrollTarget); + return; + } + + synchronized (this) { + for (ITestSuiteElement suite : fAutoExpand) { + fTreeViewer.setExpandedState(suite, true); + } + clearAutoExpand(); + } + + ITestCaseElement current = fAutoScrollTarget; + fAutoScrollTarget = null; + + ITestSuiteElement parent = current == null ? null : (ITestSuiteElement) fTreeContentProvider.getParent(current); + if (fAutoClose.isEmpty() || !fAutoClose.getLast().equals(parent)) { + // we're in a new branch, so let's close old OK branches: + for (ListIterator<ITestSuiteElement> iter = fAutoClose.listIterator(fAutoClose.size()); iter + .hasPrevious();) { + ITestSuiteElement previousAutoOpened = iter.previous(); + if (previousAutoOpened.equals(parent)) + break; + + if (((TestElement) previousAutoOpened).getStatus() == Status.OK) { + // auto-opened the element, and all children are OK -> auto close + iter.remove(); + fTreeViewer.collapseToLevel(previousAutoOpened, AbstractTreeViewer.ALL_LEVELS); + } + } + + while (parent != null && !fTestRunSession.equals(parent) && fTreeViewer.getExpandedState(parent) == false) { + fAutoClose.add(parent); // add to auto-opened elements -> close later if STATUS_OK + parent = (ITestSuiteElement) fTreeContentProvider.getParent(parent); + } + } + if (current != null) + fTreeViewer.reveal(current); + } + + /** + * Selects the next failure test element + */ + public void selectFirstFailure() { + ITestElement firstFailure = getNextChildFailure(fTestRunSession, true); + if (firstFailure != null) + getActiveViewer().setSelection(new StructuredSelection(firstFailure), true); + } + + /** + * Selects a next failure test element + * + * @param showNext <code>true</code> if a next failed element is to be shown, + * otherwise - <code>false</code> + */ + public void selectFailure(boolean showNext) { + IStructuredSelection selection = (IStructuredSelection) getActiveViewer().getSelection(); + TestElement selected = (TestElement) selection.getFirstElement(); + ITestElement next; + + if (selected == null) { + next = getNextChildFailure(fTestRunSession, showNext); + } else { + next = getNextFailure(selected, showNext); + } + + if (next != null) + getActiveViewer().setSelection(new StructuredSelection(next), true); + } + + private TestElement getNextFailure(TestElement selected, boolean showNext) { + if (selected instanceof TestSuiteElement) { + TestElement nextChild = getNextChildFailure((TestSuiteElement) selected, showNext); + if (nextChild != null) + return nextChild; + } + return getNextFailureSibling(selected, showNext); + } + + private TestElement getNextFailureSibling(TestElement current, boolean showNext) { + TestSuiteElement parent = current.getParent(); + if (parent == null) + return null; + + List<TestElement> siblings = getSortedChildren(parent); + if (!showNext) + siblings = new ReverseList<>(siblings); + + int nextIndex = siblings.indexOf(current) + 1; + for (int i = nextIndex; i < siblings.size(); i++) { + TestElement sibling = siblings.get(i); + if (sibling.getStatus().isErrorOrFailure()) { + if (sibling instanceof ITestCaseElement) { + return sibling; + } else { + TestSuiteElement testSuiteElement = (TestSuiteElement) sibling; + if (testSuiteElement.getChildren().isEmpty()) { + return testSuiteElement; + } + return getNextChildFailure(testSuiteElement, showNext); + } + } + } + return getNextFailureSibling(parent, showNext); + } + + private TestElement getNextChildFailure(TestSuiteElement root, boolean showNext) { + List<TestElement> children = getSortedChildren(root); + if (!showNext) + children = new ReverseList<>(children); + for (TestElement child : children) { + if (child.getStatus().isErrorOrFailure()) { + if (child instanceof ITestCaseElement) { + return child; + } else { + TestSuiteElement testSuiteElement = (TestSuiteElement) child; + if (testSuiteElement.getChildren().isEmpty()) { + return testSuiteElement; + } + return getNextChildFailure(testSuiteElement, showNext); + } + } + } + return null; + } + + private List<TestElement> getSortedChildren(TestSuiteElement parent) { + List<TestElement> siblings = new ArrayList<>(parent.getChildren()); + ViewerComparator comparator = fTreeViewer.getComparator(); + if (comparator != null) { + siblings.sort((e1, e2) -> comparator.compare(fTreeViewer, e1, e2)); + } + return siblings; + } + + /** + * Initializes a viewers refresh + */ + public synchronized void registerViewersRefresh() { + fTreeNeedsRefresh = true; + fTableNeedsRefresh = true; + clearUpdateAndExpansion(); + } + + private void clearUpdateAndExpansion() { + fNeedUpdate = new LinkedHashSet<>(); + fAutoClose = new LinkedList<>(); + fAutoExpand = new HashSet<>(); + } + + /** + * Registers a test element + * + * @param testElement the added test + */ + public synchronized void registerTestAdded(ITestElement testElement) { + // TODO: performance: would only need to refresh parent of added element + fTreeNeedsRefresh = true; + fTableNeedsRefresh = true; + } + + /** + * Initializes an update for a test element + * + * @param testElement a test element that needs to be updated + */ + public synchronized void registerViewerUpdate(final ITestElement testElement) { + fNeedUpdate.add(testElement); + } + + private synchronized void clearAutoExpand() { + fAutoExpand.clear(); + } + + /** + * Registers an auto-scroll target test case element + * + * @param testCaseElement a test case element + */ + public void registerAutoScrollTarget(ITestCaseElement testCaseElement) { + fAutoScrollTarget = testCaseElement; + } + + /** + * Registers a failed test element for an auto-scroll + * + * @param testElement a failed test element + */ + public synchronized void registerFailedForAutoScroll(ITestElement testElement) { + ITestSuiteElement parent = (TestSuiteElement) fTreeContentProvider.getParent(testElement); + if (parent != null) + fAutoExpand.add(parent); + } + + /** + * Expands the test element tree first level + */ + public void expandFirstLevel() { + fTreeViewer.expandToLevel(2); + } + + /** + * Sets up an alphabetical sort flag + * + * @param enableAlphabeticalSort <code>true</code> if an alphabetical sort is + * enabled, otherwise <code>false</code> + */ + public void setAlphabeticalSort(boolean enableAlphabeticalSort) { + fTreeViewer.setComparator(enableAlphabeticalSort ? TEST_ELEMENT_ALPHABETIC_ORDER : null); + fTreeViewer.refresh(); + } + + /** + * Indicates if an alphabetical sort is enabled + * + * @return <code>true</code> if an alphabetical sort is enabled, otherwise + * <code>false</code> + */ + public boolean isAlphabeticalSort() { + return fTreeViewer.getComparator() == TEST_ELEMENT_ALPHABETIC_ORDER; + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TextualTrace.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TextualTrace.java new file mode 100644 index 000000000..2d428b1da --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/TextualTrace.java @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (c) 2005, 2017 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +/** + * + */ +package org.eclipse.unittest.internal.ui; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.Collection; + +import org.eclipse.core.text.StringMatcher; + +/** + * A Textual Trace + */ +public class TextualTrace { + /** + * An exception line type + */ + public static final int LINE_TYPE_EXCEPTION = 1; + + /** + * An normal line type + */ + public static final int LINE_TYPE_NORMAL = 0; + + /** + * An stackframe line type + */ + public static final int LINE_TYPE_STACKFRAME = 2; + + private final String fTrace; + + /** + * Constructs a Textual Trace object + * + * @param trace a trace line + * @param filterPatterns a collection of filter string matchers + */ + public TextualTrace(String trace, Collection<StringMatcher> filterPatterns) { + super(); + fTrace = filterStack(trace, filterPatterns); + } + + /** + * Displays a trace line on a specified display + * + * @param display a target display + * @param maxLabelLength a maximum number of characters to be displayed + */ + public void display(ITraceDisplay display, int maxLabelLength) { + StringReader stringReader = new StringReader(fTrace); + BufferedReader bufferedReader = new BufferedReader(stringReader); + String line; + + try { + // first line contains the thrown exception + line = readLine(bufferedReader); + if (line == null) + return; + + displayWrappedLine(display, maxLabelLength, line, LINE_TYPE_EXCEPTION); + + // the stack frames of the trace + while ((line = readLine(bufferedReader)) != null) { + int type = isAStackFrame(line) ? LINE_TYPE_STACKFRAME : LINE_TYPE_NORMAL; + displayWrappedLine(display, maxLabelLength, line, type); + } + } catch (IOException e) { + display.addTraceLine(LINE_TYPE_NORMAL, fTrace); + } + } + + private void displayWrappedLine(ITraceDisplay display, int maxLabelLength, String line, int type) { + final int labelLength = line.length(); + if (labelLength < maxLabelLength) { + display.addTraceLine(type, line); + } else { + display.addTraceLine(type, line.substring(0, maxLabelLength)); + int offset = maxLabelLength; + while (offset < labelLength) { + int nextOffset = Math.min(labelLength, offset + maxLabelLength); + display.addTraceLine(LINE_TYPE_NORMAL, line.substring(offset, nextOffset)); + offset = nextOffset; + } + } + } + + private boolean filterLine(Collection<StringMatcher> patterns, String line) { + for (StringMatcher pattern : patterns) { + if (pattern.match(line)) { + return true; + } + } + return false; + } + + private String filterStack(String stackTrace, Collection<StringMatcher> filterPatterns) { + if (filterPatterns == null || filterPatterns.isEmpty() || stackTrace == null) + return stackTrace; + + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter); + StringReader stringReader = new StringReader(stackTrace); + BufferedReader bufferedReader = new BufferedReader(stringReader); + + String line; + boolean firstLine = true; + try { + while ((line = bufferedReader.readLine()) != null) { + if (firstLine || !filterLine(filterPatterns, line)) + printWriter.println(line); + firstLine = false; + } + } catch (IOException e) { + return stackTrace; // return the stack unfiltered + } + return stringWriter.toString(); + } + + private boolean isAStackFrame(String itemLabel) { + // heuristic for detecting a stack frame - works for JDK + return itemLabel.startsWith(" at "); //$NON-NLS-1$ + } + + private String readLine(BufferedReader bufferedReader) throws IOException { + String readLine = bufferedReader.readLine(); + return readLine == null ? null : readLine.replace('\t', ' '); + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UITestRunListener.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UITestRunListener.java new file mode 100644 index 000000000..26d55526c --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UITestRunListener.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import org.eclipse.unittest.internal.UnitTestPlugin; +import org.eclipse.unittest.internal.launcher.TestRunListener; +import org.eclipse.unittest.model.ITestRunSession; + +import org.eclipse.swt.widgets.Display; + +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; + +/** + * This test run listener is the entry point that makes sure the + * org.eclipse.unittest plug-in gets loaded when a UnitTest launch configuration + * is launched. + */ +public class UITestRunListener extends TestRunListener { + @Override + public void sessionLaunched(ITestRunSession session) { + getDisplay().asyncExec(this::showTestRunnerViewPartInActivePage); + } + + /** + * Creates a Test Runner View Part if it's not yet created and makes it visible + * in active page + * + * @return a {@link TestRunnerViewPart} instance + */ + private TestRunnerViewPart showTestRunnerViewPartInActivePage() { + try { + // Have to force the creation of view part contents + // otherwise the UI will not be updated + IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); + if (page == null) + return null; + TestRunnerViewPart view = (TestRunnerViewPart) page.findView(TestRunnerViewPart.NAME); + if (view == null) { + // create and show the result view if it isn't created yet. + return (TestRunnerViewPart) page.showView(TestRunnerViewPart.NAME, null, IWorkbenchPage.VIEW_VISIBLE); + } else { + page.activate(view); + return view; + } + } catch (PartInitException pie) { + UnitTestPlugin.log(pie); + return null; + } + } + + private static Display getDisplay() { + Display display = Display.getCurrent(); + if (display == null) { + display = Display.getDefault(); + } + return display; + } + +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestCopyAction.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestCopyAction.java new file mode 100644 index 000000000..7acc70088 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestCopyAction.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2000, 2010 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringReader; +import java.io.StringWriter; + +import org.eclipse.unittest.model.ITestElement; + +import org.eclipse.swt.SWTError; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.DND; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; + +import org.eclipse.core.runtime.Assert; + +import org.eclipse.jface.dialogs.MessageDialog; + +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.actions.SelectionListenerAction; + +/** + * Copies a test failure stack trace to the clipboard. + */ +public class UnitTestCopyAction extends SelectionListenerAction { + private FailureTraceUIBlock fView; + + private final Clipboard fClipboard; + + private ITestElement fTestElement; + + /** + * Constructs a Unit Test Copy action + * + * @param view a {@link FailureTraceUIBlock} object + * @param clipboard a {@link Clipboard} object + */ + public UnitTestCopyAction(FailureTraceUIBlock view, Clipboard clipboard) { + super(Messages.CopyTrace_action_label); + Assert.isNotNull(clipboard); + PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IUnitTestHelpContextIds.COPYTRACE_ACTION); + fView = view; + fClipboard = clipboard; + } + + @Override + public void run() { + String trace = fView.getTrace(); + String source = null; + if (trace != null) { + source = convertLineTerminators(trace); + } else if (fTestElement != null) { + source = fTestElement.getTestName(); + } + if (source == null || source.length() == 0) + return; + + TextTransfer plainTextTransfer = TextTransfer.getInstance(); + try { + fClipboard.setContents(new String[] { convertLineTerminators(source) }, + new Transfer[] { plainTextTransfer }); + } catch (SWTError e) { + if (e.code != DND.ERROR_CANNOT_SET_CLIPBOARD) + throw e; + if (MessageDialog.openQuestion(fView.getComposite().getShell(), Messages.CopyTraceAction_problem, + Messages.CopyTraceAction_clipboard_busy)) + run(); + } + } + + /** + * Handles a test selection + * + * @param test an {@link ITestElement} object + */ + public void handleTestSelected(ITestElement test) { + fTestElement = test; + } + + private String convertLineTerminators(String in) { + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter); + StringReader stringReader = new StringReader(in); + BufferedReader bufferedReader = new BufferedReader(stringReader); + String line; + try { + while ((line = bufferedReader.readLine()) != null) { + printWriter.println(line); + } + } catch (IOException e) { + return in; // return the trace unfiltered + } + return stringWriter.toString(); + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestProgressBar.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestProgressBar.java new file mode 100644 index 000000000..c377589dd --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestProgressBar.java @@ -0,0 +1,216 @@ +/******************************************************************************* + * Copyright (c) 2000, 2010 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlAdapter; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; + +/** + * A progress bar with a red/green indication for success or failure. + */ +public class UnitTestProgressBar extends Canvas { + private static final int DEFAULT_WIDTH = 160; + private static final int DEFAULT_HEIGHT = 18; + + private int fCurrentTickCount = 0; + private int fMaxTickCount = 0; + private int fColorBarWidth = 0; + private Color fOKColor; + private Color fFailureColor; + private Color fStoppedColor; + private boolean fError; + private boolean fStopped = false; + + /** + * Constructs a Unit Test Progress Bar object + * + * @param parent a parent composite + */ + public UnitTestProgressBar(Composite parent) { + super(parent, SWT.NONE); + + addControlListener(new ControlAdapter() { + @Override + public void controlResized(ControlEvent e) { + fColorBarWidth = scale(fCurrentTickCount); + redraw(); + } + }); + addPaintListener(this::paint); + addDisposeListener(event -> { + fFailureColor.dispose(); + fOKColor.dispose(); + fStoppedColor.dispose(); + }); + Display display = parent.getDisplay(); + fFailureColor = new Color(display, 159, 63, 63); + fOKColor = new Color(display, 95, 191, 95); + fStoppedColor = new Color(display, 120, 120, 120); + } + + /** + * Sets a maximum ticks count + * + * @param max a value of maximum ticks count + */ + public void setMaximum(int max) { + fMaxTickCount = max; + } + + /** + * Resets the progress bar + */ + public void reset() { + fError = false; + fStopped = false; + fCurrentTickCount = 0; + fMaxTickCount = 0; + fColorBarWidth = 0; + redraw(); + } + + /** + * Resets the progress bar using new initial values + * + * @param hasErrors <code>true</code> if a test has errors, otherwise + * <code>false</code> + * @param stopped <code>true</code> if a test is stopped, otherwise + * <code>false</code> + * @param ticksDone a number of ticks done + * @param maximum a maximum ticks count + */ + public void reset(boolean hasErrors, boolean stopped, int ticksDone, int maximum) { + boolean noChange = fError == hasErrors && fStopped == stopped && fCurrentTickCount == ticksDone + && fMaxTickCount == maximum; + fError = hasErrors; + fStopped = stopped; + fCurrentTickCount = ticksDone; + fMaxTickCount = maximum; + fColorBarWidth = scale(ticksDone); + if (!noChange) + redraw(); + } + + private void paintStep(int startX, int endX) { + GC gc = new GC(this); + setStatusColor(gc); + Rectangle rect = getClientArea(); + startX = Math.max(1, startX); + gc.fillRectangle(startX, 1, endX - startX, rect.height - 2); + gc.dispose(); + } + + private void setStatusColor(GC gc) { + if (fStopped) + gc.setBackground(fStoppedColor); + else if (fError) + gc.setBackground(fFailureColor); + else + gc.setBackground(fOKColor); + } + + /** + * Sets a stopped flag on the progress bar + */ + public void stopped() { + fStopped = true; + redraw(); + } + + private int scale(int value) { + if (fMaxTickCount > 0) { + Rectangle r = getClientArea(); + if (r.width != 0) + return Math.max(0, value * (r.width - 2) / fMaxTickCount); + } + return value; + } + + private void drawBevelRect(GC gc, int x, int y, int w, int h, Color topleft, Color bottomright) { + gc.setForeground(topleft); + gc.drawLine(x, y, x + w - 1, y); + gc.drawLine(x, y, x, y + h - 1); + + gc.setForeground(bottomright); + gc.drawLine(x + w, y, x + w, y + h); + gc.drawLine(x, y + h, x + w, y + h); + } + + private void paint(PaintEvent event) { + GC gc = event.gc; + Display disp = getDisplay(); + + Rectangle rect = getClientArea(); + gc.fillRectangle(rect); + drawBevelRect(gc, rect.x, rect.y, rect.width - 1, rect.height - 1, + disp.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW), + disp.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW)); + + setStatusColor(gc); + fColorBarWidth = Math.min(rect.width - 2, fColorBarWidth); + gc.fillRectangle(1, 1, fColorBarWidth, rect.height - 2); + } + + @Override + public Point computeSize(int wHint, int hHint, boolean changed) { + checkWidget(); + Point size = new Point(DEFAULT_WIDTH, DEFAULT_HEIGHT); + if (wHint != SWT.DEFAULT) + size.x = wHint; + if (hHint != SWT.DEFAULT) + size.y = hHint; + return size; + } + + /** + * Steps the progress according to failures count + * + * @param failures a failures count + */ + public void step(int failures) { + fCurrentTickCount++; + int x = fColorBarWidth; + + fColorBarWidth = scale(fCurrentTickCount); + + if (!fError && failures > 0) { + fError = true; + x = 1; + } + if (fCurrentTickCount == fMaxTickCount) + fColorBarWidth = getClientArea().width - 1; + paintStep(x, fColorBarWidth); + } + + /** + * Refreshes the progress bar + * + * @param hasErrors <code>true</code> if a test has errors, otherwise + * <code>false</code> + */ + public void refresh(boolean hasErrors) { + fError = hasErrors; + redraw(); + } + +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestUIPreferencesConstants.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestUIPreferencesConstants.java new file mode 100644 index 000000000..f1396ea24 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/UnitTestUIPreferencesConstants.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2010, 2017 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.internal.ui; + +import org.osgi.service.prefs.BackingStoreException; + +import org.eclipse.unittest.internal.UnitTestPlugin; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.core.runtime.preferences.InstanceScope; + +/** + * Defines constants which are used to refer to values in the plugin's + * preference store. + */ +public class UnitTestUIPreferencesConstants { + /** + * Boolean preference controlling whether newly launched Unit tests should be + * shown in all Unit Test views (in all windows). + */ + public static final String SHOW_IN_ALL_VIEWS = UnitTestPlugin.PLUGIN_ID + ".show_in_all_views"; //$NON-NLS-1$ + + /** + * A default value for SHOW_IN_ALL_VIEWS preference + */ + public static final boolean SHOW_IN_ALL_VIEWS_DEFAULT = false; // would need a PreferenceInitializer if this was + // changed to true! + + private UnitTestUIPreferencesConstants() { + // no instance + } + + /** + * Returns a value of SHOW_IN_ALL_VIEWS preference + * + * @return boolean value of SHOW_IN_ALL_VIEWS preference + */ + public static boolean getShowInAllViews() { + return Platform.getPreferencesService().getBoolean(UnitTestPlugin.PLUGIN_ID, SHOW_IN_ALL_VIEWS, + SHOW_IN_ALL_VIEWS_DEFAULT, null); + } + + /** + * Sets a value of SHOW_IN_ALL_VIEWS preference + * + * @param show <code>true</code> if the results view is to be shown in all + * views, otherwise <code>false</code> + */ + public static void setShowInAllViews(boolean show) { + IEclipsePreferences preferences = InstanceScope.INSTANCE.getNode(UnitTestPlugin.PLUGIN_ID); + preferences.putBoolean(SHOW_IN_ALL_VIEWS, show); + try { + preferences.flush(); + } catch (BackingStoreException e) { + UnitTestPlugin.log(e); + } + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/History.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/History.java new file mode 100644 index 000000000..ea41f16db --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/History.java @@ -0,0 +1,143 @@ +/******************************************************************************* + * Copyright (c) 2020 Red Hat Inc. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.unittest.internal.ui.history; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.eclipse.unittest.internal.UnitTestPlugin; +import org.eclipse.unittest.internal.model.ITestRunSessionListener; +import org.eclipse.unittest.internal.model.TestRunSession; +import org.eclipse.unittest.model.ITestRunSession; + +import org.eclipse.core.runtime.CoreException; + +/** + * A test run sessions history object + */ +public class History implements ITestRunSessionListener { + + private static final String HISTORY_DIR_NAME = "history"; //$NON-NLS-1$ + + /** + * An instance of test run sessions history object + */ + public static final History INSTANCE = new History(); + + private History() { + } + + private boolean wasRead = false; + private List<HistoryItem> items = new ArrayList<>(); + + /** + * Creates and returns a directory to store the History information + * + * @return the file corresponding to History directory + * @throws IllegalStateException in case of failed to create or find an existing + * directory + */ + public File getDirectory() throws IllegalStateException { + File historyDir = UnitTestPlugin.getDefault().getStateLocation().append(HISTORY_DIR_NAME).toFile(); + if (!historyDir.isDirectory()) { + historyDir.mkdir(); + } + return historyDir; + } + + /** + * Returns a list of history items + * + * @return a list of history items + */ + public List<HistoryItem> getHistory() { + if (!wasRead) { + Arrays.stream(getDirectory().listFiles()).map(HistoryItem::new).forEach(items::add); + wasRead = true; + } + return Collections.unmodifiableList(items); + } + + /** + * Clears the history + */ + public void clear() { + for (HistoryItem item : items) { + try { + item.removeSwapFile(); + } catch (IOException e) { + UnitTestPlugin.log(e); + } + } + items.clear(); + } + + @Override + public void sessionAdded(ITestRunSession testRunSession) { + items.add(new HistoryItem((TestRunSession) testRunSession)); + } + + @Override + public void sessionRemoved(ITestRunSession testRunSession) { + items.stream().filter(item -> item.getCurrentTestRunSession().filter(testRunSession::equals).isPresent()) + .forEach(toRemove -> { + try { + toRemove.removeSwapFile(); + } catch (IOException e) { + UnitTestPlugin.log(e); + } + }); + } + + /** + * Saves a test run session into history + * + * @param testRunSession a test run session object + */ + public void watch(TestRunSession testRunSession) { + for (HistoryItem item : items) { + if (testRunSession == null || item.getCurrentTestRunSession().filter(testRunSession::equals).isEmpty()) { + try { + item.swapOut(); + } catch (CoreException e) { + UnitTestPlugin.log(e); + } + } + } + } + + /** + * Removes a history item + * + * @param selected a history item to remove + */ + public void remove(HistoryItem selected) { + this.items.remove(selected); + try { + selected.removeSwapFile(); + } catch (IOException e) { + UnitTestPlugin.log(e); + } + } + + /** + * adds a history item + * + * @param historyItem history item to add + */ + public void add(HistoryItem historyItem) { + items.add(historyItem); + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryDialog.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryDialog.java new file mode 100644 index 000000000..1bf1a87af --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryDialog.java @@ -0,0 +1,270 @@ +/******************************************************************************* + * Copyright (c) 2020 Red Hat Inc. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.unittest.internal.ui.history; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Collections; +import java.util.Comparator; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Stream; + +import javax.xml.transform.TransformerFactoryConfigurationError; + +import org.eclipse.unittest.internal.UnitTestPlugin; +import org.eclipse.unittest.internal.model.TestRunSession; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.DirectoryDialog; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.core.runtime.CoreException; + +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.viewers.ArrayContentProvider; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; + +import org.eclipse.ui.dialogs.SelectionDialog; + +/** + * A History item selection dialog + */ +public class HistoryDialog extends SelectionDialog { + + private static final Comparator<HistoryItem> COMPARING_START_DATE = Comparator.comparing(HistoryItem::getStartDate) + .reversed(); + private Set<TestRunSession> fCurrentlyVisible; + private Button fRemoveButton; + private Button fExportButton; + private TableViewer fTable; + + /** + * Constructs a history item selection dialog object + * + * @param shell a shell object + * @param visibleSessions a set of visible {@link TestRunSession} objects + */ + public HistoryDialog(Shell shell, Set<TestRunSession> visibleSessions) { + super(shell); + fCurrentlyVisible = visibleSessions; + setResult(Collections.emptyList()); + } + + @Override + protected Control createDialogArea(Composite parent) { + setTitle(Messages.HistoryDialog_title); + getShell().setText(Messages.HistoryDialog_title); + Composite res = new Composite(parent, SWT.NONE); + res.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + res.setLayout(new GridLayout(2, false)); + this.fTable = createTable(res); + fTable.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + createButtons(res); + return fTable.getControl(); + } + + private void createButtons(Composite res) { + Composite buttons = new Composite(res, SWT.NONE); + buttons.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, false, false)); + RowLayout rowLayout = new RowLayout(SWT.VERTICAL); + rowLayout.fill = true; + buttons.setLayout(rowLayout); + fRemoveButton = new Button(buttons, SWT.PUSH); + fRemoveButton.setText(Messages.HistoryDialog_remove); + fRemoveButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + for (Object selected : getResult()) { + History.INSTANCE.remove((HistoryItem) selected); + } + fTable.refresh(); + })); + Button importButton = new Button(buttons, SWT.PUSH); + importButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + FileDialog fileDialog = new FileDialog(getShell()); + fileDialog.setFilterExtensions(new String[] { "*.xml" }); //$NON-NLS-1$ + fileDialog.setText(Messages.HistoryDialog_selectImport); + String path = fileDialog.open(); + if (path == null) { + return; + } + Path sourcePath = Path.of(path); + Path targetPath = Path.of(History.INSTANCE.getDirectory().getAbsolutePath(), + sourcePath.getFileName().toString()); + try { + Files.copy(sourcePath, targetPath); + History.INSTANCE.add(new HistoryItem(targetPath.toFile())); + } catch (IOException e1) { + UnitTestPlugin.log(e1); + } + fTable.refresh(); + })); + importButton.setText(Messages.HistoryDialog_import); + fExportButton = new Button(buttons, SWT.PUSH); + fExportButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> { + DirectoryDialog directoryDialog = new DirectoryDialog(getShell()); + directoryDialog.setText(Messages.HistoryDialog_selectExport); + String path = directoryDialog.open(); + if (path == null) { + return; + } + File directory = new File(path); + for (Object object : getResult()) { + HistoryItem historyItem = (HistoryItem) object; + try { + historyItem.storeSessionToFile(new File(directory, historyItem.getFile().getName())); + } catch (TransformerFactoryConfigurationError | CoreException e1) { + UnitTestPlugin.log(e1); + } + } + fTable.refresh(); + })); + fExportButton.setText(Messages.HistoryDialog_export); + updateButtons(); + } + + private TableViewer createTable(Composite parent) { + TableViewer table = new TableViewer(parent); + table.setContentProvider(new ArrayContentProvider()); + table.setComparator(new ViewerComparator() { + @Override + public int compare(Viewer viewer, Object e1, Object e2) { + HistoryItem item1 = (HistoryItem) e1; + HistoryItem item2 = (HistoryItem) e2; + return COMPARING_START_DATE.compare(item1, item2); + } + }); + int fontSize = table.getTable().getFont().getFontData()[0].getHeight(); + table.getTable().setHeaderVisible(true); + TableViewerColumn visibleColumn = new TableViewerColumn(table, SWT.DEFAULT); + visibleColumn.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + return ((HistoryItem) element).getCurrentTestRunSession().filter(fCurrentlyVisible::contains) + .map(any -> "👁️").orElse(""); //$NON-NLS-1$ //$NON-NLS-2$ + } + }); + visibleColumn.getColumn().setWidth(2 * fontSize); + TableViewerColumn nameColumn = new TableViewerColumn(table, SWT.DEFAULT); + nameColumn.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + return ((HistoryItem) element).getName(); + } + }); + nameColumn.getColumn().setWidth(20 * fontSize); + nameColumn.getColumn().setText(Messages.HistoryDialog_name); + TableViewerColumn dateColumn = new TableViewerColumn(table, SWT.DEFAULT); + dateColumn.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + Instant startDate = ((HistoryItem) element).getStartDate(); + return startDate != null + ? startDate.atZone(ZoneId.systemDefault()).format(DateTimeFormatter.RFC_1123_DATE_TIME) + : ""; //$NON-NLS-1$ + } + }); + dateColumn.getColumn().setWidth(25 * fontSize); + dateColumn.getColumn().setText(Messages.HistoryDialog_date); + TableViewerColumn progressColumn = new TableViewerColumn(table, SWT.DEFAULT); + progressColumn.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + return ((HistoryItem) element).getCurrentTestRunSession().filter(TestRunSession::isRunning) + .map(any -> "🏃").orElse(""); //$NON-NLS-1$ //$NON-NLS-2$ + } + }); + progressColumn.getColumn().setWidth(2 * fontSize); + progressColumn.getColumn().setText(Messages.HistoryDialog_progress); + TableViewerColumn successColumn = new TableViewerColumn(table, SWT.DEFAULT); + successColumn.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + int failures = ((HistoryItem) element).getFailureCount(); + if (failures == 0) { + return "✅"; //$NON-NLS-1$ + } + return "❌ " + failures + Messages.HistoryDialog_failures; //$NON-NLS-1$ + } + }); + successColumn.getColumn().setWidth(15 * fontSize); + successColumn.getColumn().setText(Messages.HistoryDialog_result); + TableViewerColumn sizeColumn = new TableViewerColumn(table, SWT.DEFAULT); + sizeColumn.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + Long size = ((HistoryItem) element).getSizeOnDisk(); + if (size != null) { + return size.toString() + " B"; //$NON-NLS-1$ + } + return Character.toString('?'); + } + }); + sizeColumn.getColumn().setText(Messages.HistoryDialog_size); + sizeColumn.getColumn().setWidth(10 * fontSize); + table.setInput(History.INSTANCE.getHistory()); + table.setSelection(new StructuredSelection(getInitialElementSelections().toArray())); + table.addSelectionChangedListener( + event -> setSelectionResult(((IStructuredSelection) event.getSelection()).toArray())); + return table; + } + + @Override + protected Button createButton(Composite parent, int id, String label, boolean defaultButton) { + return super.createButton(parent, id, id == IDialogConstants.OK_ID ? IDialogConstants.OPEN_LABEL : label, + defaultButton); + } + + @Override + protected void createButtonsForButtonBar(Composite parent) { + super.createButtonsForButtonBar(parent); + updateButtons(); + } + + @Override + protected void setSelectionResult(Object[] newResult) { + super.setSelectionResult(newResult); + updateButtons(); + } + + private void updateButtons() { + Object[] selection = getResult(); + boolean singleItemSelection = selection.length == 1; + Stream.of(getButton(IDialogConstants.OK_ID), fRemoveButton, fExportButton) // + .filter(Objects::nonNull) // + .forEach(button -> button.setEnabled(singleItemSelection)); + if (singleItemSelection) { + HistoryItem item = (HistoryItem) selection[0]; + boolean isRunning = item.getCurrentTestRunSession().filter(TestRunSession::isRunning).isPresent(); + fRemoveButton.setEnabled(!isRunning); + fExportButton.setEnabled(!isRunning); + } + + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryHandler.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryHandler.java new file mode 100644 index 000000000..ea36c41de --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryHandler.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2020 Red Hat Inc. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.unittest.internal.ui.history; + +import java.util.Arrays; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.unittest.internal.UnitTestPlugin; +import org.eclipse.unittest.internal.model.TestRunSession; +import org.eclipse.unittest.internal.ui.TestRunnerViewPart; + +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; + +import org.eclipse.core.runtime.CoreException; + +import org.eclipse.jface.dialogs.IDialogConstants; + +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.dialogs.SelectionDialog; +import org.eclipse.ui.handlers.HandlerUtil; + +/** + * A handler for Show History command + */ +public class HistoryHandler extends AbstractHandler { + + /** + * An identifier of Show History command + */ + public static final String COMMAND_ID = UnitTestPlugin.PLUGIN_ID + ".history"; //$NON-NLS-1$ + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + Shell shell = HandlerUtil.getActiveShell(event); + IWorkbenchPage page = HandlerUtil.getActivePart(event).getSite().getPage(); + Set<TestRunSession> visibleSessions = Arrays.stream(page.getViewReferences()) // + .map(ref -> ref.getPart(false)) // + .filter(TestRunnerViewPart.class::isInstance) // + .map(TestRunnerViewPart.class::cast) // + .map(TestRunnerViewPart::getCurrentTestRunSession) // + .filter(Objects::nonNull) // + .collect(Collectors.toSet()); + SelectionDialog historyDialog = new HistoryDialog(shell, visibleSessions); + historyDialog.setBlockOnOpen(true); + if (historyDialog.open() == IDialogConstants.OK_ID) { + HistoryItem item = (HistoryItem) historyDialog.getResult()[0]; + try { + TestRunnerViewPart part = findCurrentPartOrOpenNew(HandlerUtil.getActivePart(event)); + part.setActiveTestRunSession(item.reloadTestRunSession()); + } catch (CoreException e) { + UnitTestPlugin.log(e); + } + } + return null; + } + + private TestRunnerViewPart findCurrentPartOrOpenNew(IWorkbenchPart part) throws PartInitException { + if (part instanceof TestRunnerViewPart) { + return (TestRunnerViewPart) part; + } + return (TestRunnerViewPart) part.getSite().getPage().showView(TestRunnerViewPart.NAME); + } + +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryItem.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryItem.java new file mode 100644 index 000000000..ec541db62 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/HistoryItem.java @@ -0,0 +1,324 @@ +/******************************************************************************* + * Copyright (c) 2020 Red Hat Inc. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.unittest.internal.ui.history; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.time.Duration; +import java.time.Instant; +import java.util.Date; +import java.util.Optional; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.stream.StreamResult; + +import org.xml.sax.InputSource; + +import org.eclipse.unittest.internal.UnitTestPlugin; +import org.eclipse.unittest.internal.junitXmlReport.HistoryEntryHandler; +import org.eclipse.unittest.internal.junitXmlReport.TestRunHandler; +import org.eclipse.unittest.internal.junitXmlReport.TestRunSessionSerializer; +import org.eclipse.unittest.internal.model.ITestSessionListener; +import org.eclipse.unittest.internal.model.ModelMessages; +import org.eclipse.unittest.internal.model.TestRunSession; +import org.eclipse.unittest.internal.ui.BasicElementLabels; +import org.eclipse.unittest.model.ITestCaseElement; +import org.eclipse.unittest.model.ITestElement; +import org.eclipse.unittest.model.ITestElement.FailureTrace; +import org.eclipse.unittest.model.ITestElement.Result; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; + +/** + * A history item object + */ +public class HistoryItem { + + private File historyFile; + + private TestRunSession session; + + private String name; + + private Instant startTime; + + private int failuresAndErrors; + + /** + * Constructs a history item object for a {@link TestRunSession} + * + * @param session a {@link TestRunSession} object + */ + public HistoryItem(TestRunSession session) { + this.session = session; + this.name = session.getTestRunName(); + this.startTime = session.getStartTime(); + this.failuresAndErrors = session.getCurrentErrorCount() + session.getCurrentFailureCount(); + session.addTestSessionListener(new ITestSessionListener() { + @Override + public void testStarted(ITestCaseElement testCaseElement) { + // nothing + } + + @Override + public void testFailed(ITestElement testElement, Result status, FailureTrace trace) { + // nothing + } + + @Override + public void testEnded(ITestCaseElement testCaseElement) { + // nothing + } + + @Override + public void testAdded(ITestElement testElement) { + // nothing + } + + @Override + public void sessionStarted() { + getFile(); // Force creating a History File + } + + @Override + public void sessionCompleted(Duration duration) { + try { + storeSessionToFile(getFile()); + } catch (CoreException e) { + UnitTestPlugin.log(e); + } + } + + @Override + public void sessionAborted(Duration duration) { + sessionCompleted(duration); + } + + @Override + public void runningBegins() { + // nothing + } + }); + } + + /** + * Constructs a history item object from a file + * + * @param file a history item file + */ + public HistoryItem(File file) { + this.historyFile = file; + try { + SAXParserFactory parserFactory = SAXParserFactory.newInstance(); + SAXParser parser = parserFactory.newSAXParser(); + HistoryEntryHandler handler = new HistoryEntryHandler(); + parser.parse(getFile(), handler); + this.name = handler.getName(); + this.startTime = handler.getStartTime(); + this.failuresAndErrors = handler.getFailuresAndErrors(); + } catch (Exception e) { + UnitTestPlugin.log(e); + } + } + + /** + * Reloads a {@link TestRunSession} object + * + * @return a {@link TestRunSession} object instance + * @throws CoreException in case of a problem during the object reading + */ + public TestRunSession reloadTestRunSession() throws CoreException { + if (this.session == null && getFile() != null) { + try { + SAXParserFactory parserFactory = SAXParserFactory.newInstance(); + SAXParser parser = parserFactory.newSAXParser(); + TestRunHandler handler = new TestRunHandler(new NullProgressMonitor()); + parser.parse(getFile(), handler); + this.session = handler.getTestRunSession(); + } catch (Exception e) { + throwImportError(getFile(), e); + } + } + return this.session; + } + + /** + * Returns current {@link TestRunSession} object + * + * @return a {@link TestRunSession} object + */ + public Optional<TestRunSession> getCurrentTestRunSession() { + return Optional.ofNullable(this.session); + } + + /** + * Removes a swap file for a history item + * + * @throws IOException in case of I/O failure + */ + public void removeSwapFile() throws IOException { + if (historyFile != null && historyFile.exists()) { + Files.delete(historyFile.toPath()); + } + } + + /** + * Saves a history item into a file + * + * @param target a target file + * @throws TransformerFactoryConfigurationError in case of transformation + * operation failure + * @throws CoreException in case of storing operation + * failure + */ + void storeSessionToFile(File target) throws TransformerFactoryConfigurationError, CoreException { + if (this.session == null) { + return; + } + try (FileOutputStream out = new FileOutputStream(target)) { + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + InputSource inputSource = new InputSource(); + SAXSource source = new SAXSource(new TestRunSessionSerializer(this.session), inputSource); + StreamResult result = new StreamResult(out); + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$ + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ + /* + * Bug in Xalan: Only indents if proprietary property + * org.apache.xalan.templates.OutputProperties.S_KEY_INDENT_AMOUNT is set. + * + * Bug in Xalan as shipped with J2SE 5.0: Does not read the indent-amount + * property at all >:-(. + */ + try { + transformer.setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "2"); //$NON-NLS-1$ //$NON-NLS-2$ + } catch (IllegalArgumentException e) { + // no indentation today... + } + transformer.transform(source, result); + } catch (Exception e) { + throwExportError(target, e); + } + } + + /** + * Returns the history item swap file + * + * @return a history item file + */ + public File getFile() { + if (this.historyFile == null) { + File historyDir = History.INSTANCE.getDirectory(); + String isoTime = new SimpleDateFormat("yyyyMMdd-HHmmss.SSS") //$NON-NLS-1$ + .format(new Date(getStartDate().toEpochMilli())); + String swapFileName = session.getTestRunName() + '@' + isoTime + ".xml"; //$NON-NLS-1$ + this.historyFile = new File(historyDir, swapFileName); + } + + return this.historyFile; + } + + /** + * Stores test session into a swap file + * + * @throws CoreException in case of a problem + */ + public void swapOut() throws CoreException { + if (session != null && session.isStopped()) { + storeSessionToFile(getFile()); + session = null; + } + } + + /** + * Returns a test session name. + * + * If a test session name is <code>null</code> returns a name of file + * + * @return a test session name + */ + public String getName() { + if (session != null) { + return session.getTestRunName(); + } + if (name != null) { + return name; + } + return getFile().getName(); + } + + /** + * Returns a test session start date/time. + * + * If date/time of a test session cannot be obtained returns a time of last swap + * file modification, or "now" + * + * + * @return an {@link Instant} object indicating a test session start date/time + */ + public Instant getStartDate() { + if (session != null) { + startTime = session.getStartTime(); + } + if (startTime != null) { + return startTime; + } + return Instant.now(); + } + + /** + * Returns a failure count for a test session + * + * @return a failure count + */ + public int getFailureCount() { + if (session != null) { + return session.getCurrentErrorCount() + session.getCurrentFailureCount(); + } + return failuresAndErrors; + } + + private static void throwExportError(File file, Exception e) throws CoreException { + throw new CoreException( + new org.eclipse.core.runtime.Status(IStatus.ERROR, UnitTestPlugin.PLUGIN_ID, MessageFormat.format( + ModelMessages.UnitTestModel_could_not_write, BasicElementLabels.getPathLabel(file)), e)); + } + + private static void throwImportError(File file, Exception e) throws CoreException { + throw new CoreException(new org.eclipse.core.runtime.Status(IStatus.ERROR, UnitTestPlugin.PLUGIN_ID, + MessageFormat.format(ModelMessages.UnitTestModel_could_not_read, BasicElementLabels.getPathLabel(file)), + e)); + } + + /** + * Returns a size of a swap file + * + * @return a size of a swap file + */ + public Long getSizeOnDisk() { + File file = getFile(); + if (file != null && file.isFile()) { + return Long.valueOf(file.length()); + } + return null; + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/Messages.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/Messages.java new file mode 100644 index 000000000..73eca9510 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/Messages.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2020 Red Hat Inc. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.unittest.internal.ui.history; + +import org.eclipse.osgi.util.NLS; + +/** + * History messages + */ +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.unittest.internal.ui.history.messages"; //$NON-NLS-1$ + public static String HistoryDialog_date; + public static String HistoryDialog_export; + public static String HistoryDialog_failures; + public static String HistoryDialog_import; + public static String HistoryDialog_name; + public static String HistoryDialog_progress; + public static String HistoryDialog_remove; + public static String HistoryDialog_result; + public static String HistoryDialog_selectExport; + public static String HistoryDialog_selectImport; + public static String HistoryDialog_title; + public static String HistoryDialog_size; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/messages.properties b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/messages.properties new file mode 100644 index 000000000..599521b40 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/internal/ui/history/messages.properties @@ -0,0 +1,22 @@ +############################################################################### +# Copyright (c) 2020 Red Hat, Inc. and others. +# +# This program and the accompanying materials +# are made available under the terms of the Eclipse Public License 2.0 +# which accompanies this distribution, and is available at +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +############################################################################### +HistoryDialog_date=Date +HistoryDialog_export=\uD83D\uDCE4 &Export... +HistoryDialog_failures=\ Failures +HistoryDialog_import=\uD83D\uDCE5 &Import... +HistoryDialog_name=Name +HistoryDialog_progress=Progress +HistoryDialog_remove=\uD83D\uDDD1\uFE0F &Remove +HistoryDialog_result=Result +HistoryDialog_selectExport=Select a directory to export test session report +HistoryDialog_selectImport=Select test report file to import +HistoryDialog_title=Select a test session to display +HistoryDialog_size=Size diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/launcher/ITestRunnerClient.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/launcher/ITestRunnerClient.java new file mode 100644 index 000000000..61a62c7e3 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/launcher/ITestRunnerClient.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2020 Red Hat Inc. and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.launcher; + +import org.eclipse.unittest.internal.model.TestRunSession; +import org.eclipse.unittest.internal.ui.UITestRunListener; + +import org.eclipse.debug.core.ILaunch; + +/** + * An interface to be implemented by a Test Runner Client. Its implementation + * should takes care of placing the right listeners to a given + * {@link TestRunSession} (usually received in the constructor) and to react to + * the various test engine events (can be some notifications via some network, + * reading standard output, etc. depending on design of a specified test runner) + * by sending notifications to the {@link UITestRunListener}s. + */ +public interface ITestRunnerClient { + + /** + * Starts monitoring test execution. + * + * @see #stopMonitoring() + */ + void startMonitoring(); + + /** + * Requests to stop the tests execution. Usually requested by user; so it should + * stop the test runner client (usually calling {@link #stopMonitoring()} and + * also related test specific closable objects like an underlying + * {@link ILaunch} (unless launch is configured to be kept alive). + */ + void stopTest(); + + /** + * Stops monitoring and disconnects this test runner client; this is typically + * happening when a test run session is marked as terminated. + */ + void stopMonitoring(); + +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestCaseElement.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestCaseElement.java new file mode 100644 index 000000000..4ab28b76d --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestCaseElement.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.model; + +/** + * Represents a test case element. + * <p> + * This interface is not intended to be implemented by clients. + * </p> + * + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + */ +public interface ITestCaseElement extends ITestElement { + + /** + * Indicates if the test case was ignored + * + * @return true in case of the test case was ignored, otherwise false + */ + boolean isIgnored(); + + /** + * Indicates if the test case is dynamic + * + * @return true in case of dynamic test case element, otherwise false + */ + boolean isDynamicTest(); +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestElement.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestElement.java new file mode 100644 index 000000000..c59c8b935 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestElement.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (c) 2000, 2010 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.model; + +import java.time.Duration; +import java.util.Objects; + +import org.eclipse.unittest.internal.model.ProgressState; + +/** + * Common protocol for test elements. This set consists of + * {@link ITestCaseElement}, {@link ITestSuiteElement} and + * {@link ITestRunSession} + * + * <p> + * This interface is not intended to be implemented by clients. + * </p> + * + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + */ +public interface ITestElement { + + /** + * Result states of a test. + */ + public enum Result { + UNDEFINED("Undefined"), //$NON-NLS-1$ + OK("OK"), //$NON-NLS-1$ + ERROR("Error"), //$NON-NLS-1$ + FAILURE("Failure"), //$NON-NLS-1$ + IGNORED("Ignored"); //$NON-NLS-1$ + + private String fName; + + private Result(String name) { + fName = name; + } + + @Override + public String toString() { + return fName; + } + } + + /** + * A failure trace of a test. + * + * This class is not intended to be instantiated or extended by clients. + */ + public static final class FailureTrace { + private final String fActual; + private final String fExpected; + private final String fTrace; + + public FailureTrace(String trace, String expected, String actual) { + fActual = actual; + fExpected = expected; + fTrace = trace; + } + + /** + * Returns the failure stack trace. + * + * @return the failure stack trace + */ + public String getTrace() { + return fTrace; + } + + /** + * Returns the expected result or <code>null</code> if the trace is not a + * comparison failure. + * + * @return the expected result or <code>null</code> if the trace is not a + * comparison failure. + */ + public String getExpected() { + return fExpected; + } + + /** + * Returns the actual result or <code>null</code> if the trace is not a + * comparison failure. + * + * @return the actual result or <code>null</code> if the trace is not a + * comparison failure. + */ + public String getActual() { + return fActual; + } + + /** + * Returns <code>true</code> in case of comparison failure. + * + * @return <code>true</code> in case of comparison failure, otherwise returns + * <code>false</code> + */ + public boolean isComparisonFailure() { + return (fExpected != null || fActual != null) && !Objects.equals(fActual, fExpected); + } + } + + /** + * Returns an identifier of the test element + * + * @return a test element identifier + */ + String getId(); + + /** + * Returns some runner-specific data, such as complete test description or other + * data allowing further operations not covered by the generic test model. + * + * @return some runner-specific data, such as complete test description or other + * data allowing further operations not covered by the generic test + * model. + */ + String getData(); + + /** + * Returns the test run session. + * + * @return the parent test run session. + */ + ITestRunSession getTestRunSession(); + + /** + * Returns the estimated total time elapsed while executing this test element. + * The total time for a test suite includes the time used for all tests in that + * suite. The total time for a test session includes the time used for all tests + * in that session. + * <p> + * <strong>Note:</strong> The elapsed time is only valid for + * {@link ProgressState#COMPLETED} test elements. + * </p> + * + * @return total execution duration for the test element, or <code>null</code> + * if the state of the element is not {@link ProgressState#COMPLETED} + */ + Duration getDuration(); + + /** + * Returns the failure trace of this test element or <code>null</code> if the + * test has not resulted in an error or failure. + * + * @return the failure trace of this test or <code>null</code>. + */ + FailureTrace getFailureTrace(); + + /** + * Returns parent test suite element of this test element + * + * @return a parent test suite element + */ + ITestSuiteElement getParent(); + + /** + * Returns the name of the test element + * + * @return a name of test element + */ + String getTestName(); + + /** + * Returns the display name of the test. Can be <code>null</code>. In that case, + * use {@link ITestElement#getTestName() getTestName()}. + * + * @return the test display name, can be <code>null</code> + */ + String getDisplayName(); + +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestRunSession.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestRunSession.java new file mode 100644 index 000000000..3c47ad701 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestRunSession.java @@ -0,0 +1,140 @@ +/******************************************************************************* + * Copyright (c) 2000, 2009 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.model; + +import java.time.Duration; + +import org.eclipse.debug.core.ILaunch; + +/** + * Represents a test run session. + * <p> + * This interface is not intended to be implemented by clients. + * </p> + * + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + */ +public interface ITestRunSession extends ITestSuiteElement { + + /** + * Returns the {@link ILaunch} from which this test run session has been + * started, or <code>null</code> if not available. + * + * @return the {@link ILaunch} object instance, or <code>null</code> is not + * available. + */ + ILaunch getLaunch(); + + /** + * Returns a test element by its identifier + * + * @param id a test element identifier + * @return a {@link ITestElement} found or <code>null</code> + */ + ITestElement getTestElement(String id); + + /** + * Creates a new Test Case and adds it to the model + * + * @param testId a unique id for the test + * @param testName the name of the test + * @param parent the parent, can be <code>null</code> + * @param displayName the display name of the test + * @param data runner specific data + * @return the new test case element + */ + ITestCaseElement newTestCase(String testId, String testName, ITestSuiteElement parent, String displayName, + String data); + + /** + * Creates a new Test Suite and adds it to the model + * + * @param testId a unique id for the test + * @param testName the name of the test + * @param testCount the number of tests this suite will run, <code>null</code> + * if unknown. + * @param parent the parent + * @param displayName the display name of the test + * @param data runner specific data + * @return the new test case element + */ + ITestSuiteElement newTestSuite(String testId, String testName, Integer testCount, ITestSuiteElement parent, + String displayName, String data); + + /** + * Notifies on a test run ended normally. Individual test success don't matter. + * If the test session failed to complete for some reason, use + * {@link #notifyTestSessionAborted(Duration, Exception)}. + * + * @param duration the total elapsed time of the test run, can be + * <code>null</code>. + * @see #notifyTestSessionAborted(Duration, Exception) notifyTestRunAborted to + * use for abnormal termination of the test session. + */ + void notifyTestSessionCompleted(final Duration duration); + + /** + * Notifies on a test run aborted, abnormally. + * + * @param duration duration of the test run session until abortion, can be + * <code>null</code>. + * @param cause the cause of the abortion, can be shown in log or to user, + * can be <code>null</code>. + * @see #notifyTestSessionCompleted(Duration) notifyTestRunAborted to use for + * normal completion. + */ + void notifyTestSessionAborted(final Duration duration, final Exception cause); + + /** + * Notifies on an individual test ended. + * + * @param test a unique Id identifying the test + * @param isIgnored <code>true</code> indicates that the specified test was + * ignored, otherwise - <code>false</code> + */ + void notifyTestEnded(ITestElement test, boolean isIgnored); + + /** + * Notifies on an individual test started. + * + * @param test the test + */ + void notifyTestStarted(ITestElement test); + + /** + * Notifies on a test run started. + * + * @param count the number of individual tests that will be run, + * <code>null</code> if unknown + */ + void notifyTestSessionStarted(final Integer count); + + /** + * Notifies on an individual test failed with a stack trace. + * + * @param test the test + * @param status the outcome of the test; one of + * {@link org.eclipse.unittest.model.ITestElement.Result#ERROR} + * or + * {@link org.eclipse.unittest.model.ITestElement.Result#FAILURE}. + * An exception is thrown otherwise + * @param isAssumptionFailed indicates that an assumption is failed + * @param failureTrace The failure trace + * @throws IllegalArgumentException if status doesn't indicate ERROR or FAILURE. + */ + void notifyTestFailed(ITestElement test, Result status, boolean isAssumptionFailed, FailureTrace failureTrace) + throws IllegalArgumentException; + +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestSuiteElement.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestSuiteElement.java new file mode 100644 index 000000000..ef544add2 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/ITestSuiteElement.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2000, 2011 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.model; + +import java.util.List; + +/** + * Represents a test suite element. + * <p> + * This interface is not intended to be implemented by clients. + * </p> + * + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + */ +public interface ITestSuiteElement extends ITestElement { + + /** + * Returns all tests (and test suites) contained in the suite. + * + * @return returns all tests (and test suites) contained in the suite. + */ + List<? extends ITestElement> getChildren(); +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/package.html b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/package.html new file mode 100644 index 000000000..0115c4c90 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/model/package.html @@ -0,0 +1,15 @@ +<!doctype html public "-//w3c//dtd html 4.0 transitional//en"> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <meta name="Author" content="IBM"> + <meta name="GENERATOR" content="Mozilla/4.75 [en] (Windows NT 5.0; U) [Netscape]"> + <title>Package-level Javadoc</title> +</head> +<body> +Application programming interfaces representing test elements as shown in the Unit Test view. +<h2> +Package Specification</h2> +APIs to access the test elements of the Unit Test view +</body> +</html> diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/ui/ConfigureViewerSupport.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/ui/ConfigureViewerSupport.java new file mode 100644 index 000000000..db4a2c113 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/ui/ConfigureViewerSupport.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2020 Red Hat Inc. and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.ui; + +import java.util.function.Function; + +import org.eclipse.unittest.internal.launcher.UnitTestLaunchConfigurationConstants; + +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; + +/** + * Configures a Launch configuration Working Copy with an identifier of Test + * View Support extension + */ +public final class ConfigureViewerSupport + implements Function<ILaunchConfigurationWorkingCopy, ILaunchConfigurationWorkingCopy> { + private final String identifier; + + public ConfigureViewerSupport(String testViewSupportExtensionId) { + this.identifier = testViewSupportExtensionId; + } + + @Override + public ILaunchConfigurationWorkingCopy apply(ILaunchConfigurationWorkingCopy configuration) { + if (configuration != null && identifier != null) { + configuration.setAttribute(UnitTestLaunchConfigurationConstants.ATTR_UNIT_TEST_VIEW_SUPPORT, identifier); + } + return configuration; + } +} diff --git a/org.eclipse.unittest.ui/src/org/eclipse/unittest/ui/ITestViewSupport.java b/org.eclipse.unittest.ui/src/org/eclipse/unittest/ui/ITestViewSupport.java new file mode 100644 index 000000000..a03075e02 --- /dev/null +++ b/org.eclipse.unittest.ui/src/org/eclipse/unittest/ui/ITestViewSupport.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2020 Red Hat Inc. and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.unittest.ui; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.unittest.launcher.ITestRunnerClient; +import org.eclipse.unittest.model.ITestCaseElement; +import org.eclipse.unittest.model.ITestElement; +import org.eclipse.unittest.model.ITestRunSession; +import org.eclipse.unittest.model.ITestSuiteElement; + +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.core.text.StringMatcher; + +import org.eclipse.jface.action.IAction; + +import org.eclipse.debug.core.ILaunchConfiguration; + +/** + * Interface to be implemented by a Test View Support to be returned by + * org.org.eclipse.unittest.unittestViewSupport extension. + */ +public interface ITestViewSupport { + /** + * Returns a Test Runner Client. + * + * @param session the test session. ⚠️ The session may not be fully initialized + * at that point, however {@link ITestRunSession#getLaunch()} is + * supposed to return the proper launch. + * + * @return returns a Test Runner Client + */ + ITestRunnerClient newTestRunnerClient(ITestRunSession session); + + /** + * Returns filter patterns to exclude lines from stack trace or an error message + * + * @return filter patterns, matching lines will be hidden in the UI + */ + Collection<StringMatcher> getTraceExclusionFilterPatterns(); + + /** + * Returns an action to open a specified test elements + * + * @param shell a parent {@link Shell} instance + * @param testCase a test case element + * @return an action to open a specified test case element, or <code>null</code> + */ + IAction getOpenTestAction(Shell shell, ITestCaseElement testCase); + + /** + * Returns an action to open a specified test suite element + * + * @param shell a parent {@link Shell} instance + * @param testSuite a test suite element + * @return an action to open a specified test suite element, or + * <code>null</code> + */ + IAction getOpenTestAction(Shell shell, ITestSuiteElement testSuite); + + /** + * Returns an action to open a failure trace element + * + * @param shell a parent {@link Shell} instance + * @param failure a test element that is failed + * @param traceLine a stack trace or an error message text + * @return an action to open a failure trace element, or <code>null</null> + */ + IAction createOpenEditorAction(Shell shell, ITestElement failure, String traceLine); + + /** + * Returns an action to copy an existing stack trace/error message into a + * console view + * + * @param failedTest the failed test + * @return an {@link Runnable} if it can be created, otherwise - + * <code>null</code> + */ + Runnable createShowStackTraceInConsoleViewActionDelegate(ITestElement failedTest); + + /** + * Returns a Rerun launch configuration for the given element + * + * @param testElements the tests to rerun + * @return a {@link ILaunchConfiguration}, derived from current test session and + * selected element. + */ + ILaunchConfiguration getRerunLaunchConfiguration(List<ITestElement> testElements); + + /** + * Returns a Test View Support display name + * + * @return returns a display name + */ + String getDisplayName(); +} @@ -62,5 +62,6 @@ <module>org.eclipse.debug.ui</module> <module>org.eclipse.ui.console</module> <module>org.eclipse.ui.externaltools</module> + <module>org.eclipse.unittest.ui</module> </modules> </project> |