cumulusci-plus 5.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cumulusci-plus might be problematic. Click here for more details.
- cumulusci/__about__.py +1 -0
- cumulusci/__init__.py +22 -0
- cumulusci/__main__.py +3 -0
- cumulusci/cli/__init__.py +0 -0
- cumulusci/cli/cci.py +244 -0
- cumulusci/cli/error.py +125 -0
- cumulusci/cli/flow.py +185 -0
- cumulusci/cli/logger.py +72 -0
- cumulusci/cli/org.py +692 -0
- cumulusci/cli/plan.py +181 -0
- cumulusci/cli/project.py +391 -0
- cumulusci/cli/robot.py +116 -0
- cumulusci/cli/runtime.py +190 -0
- cumulusci/cli/service.py +521 -0
- cumulusci/cli/task.py +295 -0
- cumulusci/cli/tests/__init__.py +0 -0
- cumulusci/cli/tests/test_cci.py +545 -0
- cumulusci/cli/tests/test_error.py +170 -0
- cumulusci/cli/tests/test_flow.py +276 -0
- cumulusci/cli/tests/test_logger.py +25 -0
- cumulusci/cli/tests/test_org.py +1438 -0
- cumulusci/cli/tests/test_plan.py +245 -0
- cumulusci/cli/tests/test_project.py +235 -0
- cumulusci/cli/tests/test_robot.py +177 -0
- cumulusci/cli/tests/test_runtime.py +197 -0
- cumulusci/cli/tests/test_service.py +853 -0
- cumulusci/cli/tests/test_task.py +266 -0
- cumulusci/cli/tests/test_ui.py +310 -0
- cumulusci/cli/tests/test_utils.py +122 -0
- cumulusci/cli/tests/utils.py +52 -0
- cumulusci/cli/ui.py +234 -0
- cumulusci/cli/utils.py +150 -0
- cumulusci/conftest.py +181 -0
- cumulusci/core/__init__.py +0 -0
- cumulusci/core/config/BaseConfig.py +5 -0
- cumulusci/core/config/BaseTaskFlowConfig.py +5 -0
- cumulusci/core/config/OrgConfig.py +5 -0
- cumulusci/core/config/ScratchOrgConfig.py +5 -0
- cumulusci/core/config/__init__.py +125 -0
- cumulusci/core/config/base_config.py +111 -0
- cumulusci/core/config/base_task_flow_config.py +82 -0
- cumulusci/core/config/marketing_cloud_service_config.py +83 -0
- cumulusci/core/config/oauth2_service_config.py +17 -0
- cumulusci/core/config/org_config.py +604 -0
- cumulusci/core/config/project_config.py +782 -0
- cumulusci/core/config/scratch_org_config.py +251 -0
- cumulusci/core/config/sfdx_org_config.py +220 -0
- cumulusci/core/config/tests/_test_config_backwards_compatibility.py +33 -0
- cumulusci/core/config/tests/test_config.py +1895 -0
- cumulusci/core/config/tests/test_config_expensive.py +839 -0
- cumulusci/core/config/tests/test_config_util.py +91 -0
- cumulusci/core/config/universal_config.py +88 -0
- cumulusci/core/config/util.py +18 -0
- cumulusci/core/datasets.py +303 -0
- cumulusci/core/debug.py +33 -0
- cumulusci/core/dependencies/__init__.py +55 -0
- cumulusci/core/dependencies/base.py +561 -0
- cumulusci/core/dependencies/dependencies.py +273 -0
- cumulusci/core/dependencies/github.py +177 -0
- cumulusci/core/dependencies/github_resolvers.py +244 -0
- cumulusci/core/dependencies/resolvers.py +580 -0
- cumulusci/core/dependencies/tests/__init__.py +0 -0
- cumulusci/core/dependencies/tests/conftest.py +385 -0
- cumulusci/core/dependencies/tests/test_dependencies.py +950 -0
- cumulusci/core/dependencies/tests/test_github.py +83 -0
- cumulusci/core/dependencies/tests/test_resolvers.py +1027 -0
- cumulusci/core/dependencies/utils.py +13 -0
- cumulusci/core/enums.py +11 -0
- cumulusci/core/exceptions.py +311 -0
- cumulusci/core/flowrunner.py +888 -0
- cumulusci/core/github.py +665 -0
- cumulusci/core/keychain/__init__.py +24 -0
- cumulusci/core/keychain/base_project_keychain.py +441 -0
- cumulusci/core/keychain/encrypted_file_project_keychain.py +945 -0
- cumulusci/core/keychain/environment_project_keychain.py +7 -0
- cumulusci/core/keychain/serialization.py +152 -0
- cumulusci/core/keychain/subprocess_keychain.py +24 -0
- cumulusci/core/keychain/tests/conftest.py +50 -0
- cumulusci/core/keychain/tests/test_base_project_keychain.py +299 -0
- cumulusci/core/keychain/tests/test_encrypted_file_project_keychain.py +1228 -0
- cumulusci/core/metadeploy/__init__.py +0 -0
- cumulusci/core/metadeploy/api.py +88 -0
- cumulusci/core/metadeploy/plans.py +25 -0
- cumulusci/core/metadeploy/tests/test_api.py +276 -0
- cumulusci/core/runtime.py +115 -0
- cumulusci/core/sfdx.py +162 -0
- cumulusci/core/source/__init__.py +16 -0
- cumulusci/core/source/github.py +50 -0
- cumulusci/core/source/local_folder.py +35 -0
- cumulusci/core/source_transforms/__init__.py +0 -0
- cumulusci/core/source_transforms/tests/test_transforms.py +1091 -0
- cumulusci/core/source_transforms/transforms.py +532 -0
- cumulusci/core/tasks.py +404 -0
- cumulusci/core/template_utils.py +59 -0
- cumulusci/core/tests/__init__.py +0 -0
- cumulusci/core/tests/cassettes/TestDatasetsE2E.test_datasets_e2e.yaml +215 -0
- cumulusci/core/tests/cassettes/TestDatasetsE2E.test_datasets_extract_standard_objects.yaml +199 -0
- cumulusci/core/tests/cassettes/TestDatasetsE2E.test_datasets_read_explicit_extract_declaration.yaml +3 -0
- cumulusci/core/tests/fake_remote_repo/cumulusci.yml +32 -0
- cumulusci/core/tests/fake_remote_repo/tasks/directory/example_2.py +6 -0
- cumulusci/core/tests/fake_remote_repo/tasks/example.py +43 -0
- cumulusci/core/tests/fake_remote_repo_2/cumulusci.yml +11 -0
- cumulusci/core/tests/fake_remote_repo_2/tasks/example_3.py +6 -0
- cumulusci/core/tests/test_datasets_e2e.py +386 -0
- cumulusci/core/tests/test_exceptions.py +11 -0
- cumulusci/core/tests/test_flowrunner.py +836 -0
- cumulusci/core/tests/test_github.py +942 -0
- cumulusci/core/tests/test_sfdx.py +138 -0
- cumulusci/core/tests/test_source.py +678 -0
- cumulusci/core/tests/test_tasks.py +262 -0
- cumulusci/core/tests/test_utils.py +141 -0
- cumulusci/core/tests/test_utils_merge_config.py +276 -0
- cumulusci/core/tests/test_versions.py +76 -0
- cumulusci/core/tests/untrusted_repo_child/cumulusci.yml +7 -0
- cumulusci/core/tests/untrusted_repo_child/tasks/untrusted_child.py +6 -0
- cumulusci/core/tests/untrusted_repo_parent/cumulusci.yml +26 -0
- cumulusci/core/tests/untrusted_repo_parent/tasks/untrusted_parent.py +6 -0
- cumulusci/core/tests/utils.py +116 -0
- cumulusci/core/tests/yaml/global.yaml +0 -0
- cumulusci/core/utils.py +402 -0
- cumulusci/core/versions.py +149 -0
- cumulusci/cumulusci.yml +1621 -0
- cumulusci/files/admin_profile.xml +20 -0
- cumulusci/files/delete_excludes.txt +424 -0
- cumulusci/files/templates/project/README.md +12 -0
- cumulusci/files/templates/project/cumulusci.yml +63 -0
- cumulusci/files/templates/project/dot-gitignore +60 -0
- cumulusci/files/templates/project/mapping.yml +45 -0
- cumulusci/files/templates/project/scratch_def.json +25 -0
- cumulusci/oauth/__init__.py +0 -0
- cumulusci/oauth/client.py +400 -0
- cumulusci/oauth/exceptions.py +9 -0
- cumulusci/oauth/salesforce.py +95 -0
- cumulusci/oauth/tests/__init__.py +0 -0
- cumulusci/oauth/tests/cassettes/test_get_device_code.yaml +22 -0
- cumulusci/oauth/tests/cassettes/test_get_device_oauth_token.yaml +74 -0
- cumulusci/oauth/tests/test_client.py +308 -0
- cumulusci/oauth/tests/test_salesforce.py +46 -0
- cumulusci/plugins/__init__.py +3 -0
- cumulusci/plugins/plugin_base.py +93 -0
- cumulusci/plugins/plugin_loader.py +59 -0
- cumulusci/robotframework/CumulusCI.py +340 -0
- cumulusci/robotframework/CumulusCI.robot +7 -0
- cumulusci/robotframework/Performance.py +165 -0
- cumulusci/robotframework/Salesforce.py +936 -0
- cumulusci/robotframework/Salesforce.robot +192 -0
- cumulusci/robotframework/SalesforceAPI.py +416 -0
- cumulusci/robotframework/SalesforcePlaywright.py +220 -0
- cumulusci/robotframework/SalesforcePlaywright.robot +40 -0
- cumulusci/robotframework/__init__.py +2 -0
- cumulusci/robotframework/base_library.py +39 -0
- cumulusci/robotframework/faker_mixin.py +89 -0
- cumulusci/robotframework/form_handlers.py +222 -0
- cumulusci/robotframework/javascript/cci_init.js +34 -0
- cumulusci/robotframework/javascript/cumulusci.js +4 -0
- cumulusci/robotframework/locator_manager.py +197 -0
- cumulusci/robotframework/locators_56.py +88 -0
- cumulusci/robotframework/locators_57.py +5 -0
- cumulusci/robotframework/pageobjects/BasePageObjects.py +433 -0
- cumulusci/robotframework/pageobjects/ObjectManagerPageObject.py +246 -0
- cumulusci/robotframework/pageobjects/PageObjectLibrary.py +45 -0
- cumulusci/robotframework/pageobjects/PageObjects.py +351 -0
- cumulusci/robotframework/pageobjects/__init__.py +12 -0
- cumulusci/robotframework/pageobjects/baseobjects.py +120 -0
- cumulusci/robotframework/perftests/short/collection_perf.robot +105 -0
- cumulusci/robotframework/tests/CustomObjectTestPage.py +10 -0
- cumulusci/robotframework/tests/FooTestPage.py +8 -0
- cumulusci/robotframework/tests/cumulusci/base.robot +40 -0
- cumulusci/robotframework/tests/cumulusci/bulkdata.robot +38 -0
- cumulusci/robotframework/tests/cumulusci/communities.robot +57 -0
- cumulusci/robotframework/tests/cumulusci/datagen.robot +84 -0
- cumulusci/robotframework/tests/salesforce/TestLibraryA.py +24 -0
- cumulusci/robotframework/tests/salesforce/TestLibraryB.py +20 -0
- cumulusci/robotframework/tests/salesforce/TestListener.py +93 -0
- cumulusci/robotframework/tests/salesforce/api.robot +178 -0
- cumulusci/robotframework/tests/salesforce/browsers.robot +143 -0
- cumulusci/robotframework/tests/salesforce/classic.robot +51 -0
- cumulusci/robotframework/tests/salesforce/create_contact.robot +59 -0
- cumulusci/robotframework/tests/salesforce/faker.robot +68 -0
- cumulusci/robotframework/tests/salesforce/forms.robot +172 -0
- cumulusci/robotframework/tests/salesforce/label_locator.robot +244 -0
- cumulusci/robotframework/tests/salesforce/labels.html +33 -0
- cumulusci/robotframework/tests/salesforce/locators.robot +149 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/base_pageobjects.robot +100 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/example_page_object.py +25 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/listing_page.robot +115 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/objectmanager.robot +74 -0
- cumulusci/robotframework/tests/salesforce/pageobjects/pageobjects.robot +171 -0
- cumulusci/robotframework/tests/salesforce/performance.robot +109 -0
- cumulusci/robotframework/tests/salesforce/playwright/javascript_keywords.robot +33 -0
- cumulusci/robotframework/tests/salesforce/playwright/open_test_browser.robot +48 -0
- cumulusci/robotframework/tests/salesforce/playwright/playwright.robot +24 -0
- cumulusci/robotframework/tests/salesforce/playwright/ui.robot +32 -0
- cumulusci/robotframework/tests/salesforce/populate.robot +89 -0
- cumulusci/robotframework/tests/salesforce/test_testlistener.py +37 -0
- cumulusci/robotframework/tests/salesforce/ui.robot +361 -0
- cumulusci/robotframework/tests/test_cumulusci_library.py +304 -0
- cumulusci/robotframework/tests/test_locator_manager.py +158 -0
- cumulusci/robotframework/tests/test_pageobjects.py +291 -0
- cumulusci/robotframework/tests/test_performance.py +38 -0
- cumulusci/robotframework/tests/test_salesforce.py +79 -0
- cumulusci/robotframework/tests/test_salesforce_locators.py +73 -0
- cumulusci/robotframework/tests/test_template_util.py +53 -0
- cumulusci/robotframework/tests/test_utils.py +106 -0
- cumulusci/robotframework/utils.py +283 -0
- cumulusci/salesforce_api/__init__.py +0 -0
- cumulusci/salesforce_api/exceptions.py +23 -0
- cumulusci/salesforce_api/filterable_objects.py +96 -0
- cumulusci/salesforce_api/mc_soap_envelopes.py +89 -0
- cumulusci/salesforce_api/metadata.py +721 -0
- cumulusci/salesforce_api/org_schema.py +571 -0
- cumulusci/salesforce_api/org_schema_models.py +226 -0
- cumulusci/salesforce_api/package_install.py +265 -0
- cumulusci/salesforce_api/package_zip.py +301 -0
- cumulusci/salesforce_api/rest_deploy.py +148 -0
- cumulusci/salesforce_api/retrieve_profile_api.py +301 -0
- cumulusci/salesforce_api/soap_envelopes.py +177 -0
- cumulusci/salesforce_api/tests/__init__.py +0 -0
- cumulusci/salesforce_api/tests/metadata_test_strings.py +24 -0
- cumulusci/salesforce_api/tests/test_metadata.py +1015 -0
- cumulusci/salesforce_api/tests/test_package_install.py +219 -0
- cumulusci/salesforce_api/tests/test_package_zip.py +380 -0
- cumulusci/salesforce_api/tests/test_rest_deploy.py +264 -0
- cumulusci/salesforce_api/tests/test_retrieve_profile_api.py +337 -0
- cumulusci/salesforce_api/tests/test_utils.py +124 -0
- cumulusci/salesforce_api/utils.py +51 -0
- cumulusci/schema/cumulusci.jsonschema.json +782 -0
- cumulusci/tasks/__init__.py +0 -0
- cumulusci/tasks/apex/__init__.py +0 -0
- cumulusci/tasks/apex/anon.py +157 -0
- cumulusci/tasks/apex/batch.py +180 -0
- cumulusci/tasks/apex/testrunner.py +835 -0
- cumulusci/tasks/apex/tests/cassettes/ManualEditTestApexIntegrationTests.test_run_tests__integration_test.yaml +703 -0
- cumulusci/tasks/apex/tests/test_apex_tasks.py +1558 -0
- cumulusci/tasks/base_source_control_task.py +17 -0
- cumulusci/tasks/bulkdata/__init__.py +15 -0
- cumulusci/tasks/bulkdata/base_generate_data_task.py +96 -0
- cumulusci/tasks/bulkdata/dates.py +97 -0
- cumulusci/tasks/bulkdata/delete.py +156 -0
- cumulusci/tasks/bulkdata/extract.py +441 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/calculate_dependencies.py +117 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/extract_yml.py +123 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/hardcoded_default_declarations.py +49 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/synthesize_extract_declarations.py +283 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/tests/test_extract_yml.py +142 -0
- cumulusci/tasks/bulkdata/extract_dataset_utils/tests/test_synthesize_extract_declarations.py +575 -0
- cumulusci/tasks/bulkdata/factory_utils.py +134 -0
- cumulusci/tasks/bulkdata/generate.py +4 -0
- cumulusci/tasks/bulkdata/generate_and_load_data.py +232 -0
- cumulusci/tasks/bulkdata/generate_and_load_data_from_yaml.py +19 -0
- cumulusci/tasks/bulkdata/generate_from_yaml.py +183 -0
- cumulusci/tasks/bulkdata/generate_mapping.py +434 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/dependency_map.py +169 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/extract_mapping_file_generator.py +45 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/generate_mapping_from_declarations.py +121 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/load_mapping_file_generator.py +127 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/mapping_generator_post_processes.py +53 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/mapping_transforms.py +139 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_generate_extract_mapping_from_declarations.py +135 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_generate_load_mapping_from_declarations.py +330 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_mapping_generator_post_processes.py +60 -0
- cumulusci/tasks/bulkdata/generate_mapping_utils/tests/test_mapping_transforms.py +188 -0
- cumulusci/tasks/bulkdata/load.py +1196 -0
- cumulusci/tasks/bulkdata/mapping_parser.py +811 -0
- cumulusci/tasks/bulkdata/query_transformers.py +264 -0
- cumulusci/tasks/bulkdata/select_utils.py +792 -0
- cumulusci/tasks/bulkdata/snowfakery.py +753 -0
- cumulusci/tasks/bulkdata/snowfakery_utils/queue_manager.py +478 -0
- cumulusci/tasks/bulkdata/snowfakery_utils/snowfakery_run_until.py +141 -0
- cumulusci/tasks/bulkdata/snowfakery_utils/snowfakery_working_directory.py +53 -0
- cumulusci/tasks/bulkdata/snowfakery_utils/subtask_configurator.py +64 -0
- cumulusci/tasks/bulkdata/step.py +1242 -0
- cumulusci/tasks/bulkdata/tests/__init__.py +0 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_random_strategy.yaml +147 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_annoy_strategy.yaml +123 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_select_and_insert_strategy.yaml +313 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_select_and_insert_strategy_bulk.yaml +550 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_similarity_strategy.yaml +175 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSelect.test_select_standard_strategy.yaml +147 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_run_until_records_in_org__multiple_needed.yaml +69 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_run_until_records_in_org__none_needed.yaml +22 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_run_until_records_in_org__one_needed.yaml +24 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestSnowfakery.test_snowfakery_query_salesforce.yaml +25 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpdatesIntegrationTests.test_updates_task.yaml +80 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_simple_upsert__rest.yaml +270 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert__rest.yaml +267 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert_complex_external_id_field__rest.yaml +369 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert_complex_external_id_field_rest__duplicate_error.yaml +204 -0
- cumulusci/tasks/bulkdata/tests/cassettes/TestUpsert.test_upsert_complex_fields__bulk.yaml +675 -0
- cumulusci/tasks/bulkdata/tests/dummy_data_factory.py +36 -0
- cumulusci/tasks/bulkdata/tests/integration_test_utils.py +49 -0
- cumulusci/tasks/bulkdata/tests/mapping-oid.yml +87 -0
- cumulusci/tasks/bulkdata/tests/mapping_after.yml +38 -0
- cumulusci/tasks/bulkdata/tests/mapping_poly.yml +34 -0
- cumulusci/tasks/bulkdata/tests/mapping_poly_incomplete.yml +20 -0
- cumulusci/tasks/bulkdata/tests/mapping_poly_wrong.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_select.yml +20 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_invalid_strategy.yml +20 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_invalid_threshold__invalid_number.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_invalid_threshold__invalid_strategy.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_invalid_threshold__non_float.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_missing_priority_fields.yml +22 -0
- cumulusci/tasks/bulkdata/tests/mapping_select_no_priority_fields.yml +18 -0
- cumulusci/tasks/bulkdata/tests/mapping_simple.yml +27 -0
- cumulusci/tasks/bulkdata/tests/mapping_v1.yml +28 -0
- cumulusci/tasks/bulkdata/tests/mapping_v2.yml +21 -0
- cumulusci/tasks/bulkdata/tests/mapping_v3.yml +32 -0
- cumulusci/tasks/bulkdata/tests/mapping_vanilla_sf.yml +69 -0
- cumulusci/tasks/bulkdata/tests/mock_data_factory_without_mapping.py +12 -0
- cumulusci/tasks/bulkdata/tests/person_accounts.yml +23 -0
- cumulusci/tasks/bulkdata/tests/person_accounts_minimal.yml +15 -0
- cumulusci/tasks/bulkdata/tests/recordtypes.yml +8 -0
- cumulusci/tasks/bulkdata/tests/recordtypes_2.yml +6 -0
- cumulusci/tasks/bulkdata/tests/recordtypes_with_ispersontype.yml +8 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/child/child2.yml +3 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/child.yml +4 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/gen_npsp_standard_objects.recipe.yml +89 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/include_parent.yml +3 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/npsp_standard_objects_macros.yml +34 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/options.recipe.yml +6 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/query_snowfakery.recipe.yml +16 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/sf_standard_object_macros.yml +83 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery.load.yml +2 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery.recipe.yml +13 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_2.load.yml +5 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_channels.load.yml +13 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_channels.recipe.yml +12 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/simple_snowfakery_channels_2.load.yml +13 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/unique_values.recipe.yml +4 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/upsert.recipe.yml +23 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/upsert_2.recipe.yml +29 -0
- cumulusci/tasks/bulkdata/tests/snowfakery/upsert_before.yml +10 -0
- cumulusci/tasks/bulkdata/tests/test_base_generate_data_tasks.py +61 -0
- cumulusci/tasks/bulkdata/tests/test_dates.py +99 -0
- cumulusci/tasks/bulkdata/tests/test_delete.py +404 -0
- cumulusci/tasks/bulkdata/tests/test_extract.py +1311 -0
- cumulusci/tasks/bulkdata/tests/test_factory_utils.py +55 -0
- cumulusci/tasks/bulkdata/tests/test_generate_and_load.py +252 -0
- cumulusci/tasks/bulkdata/tests/test_generate_from_snowfakery_task.py +343 -0
- cumulusci/tasks/bulkdata/tests/test_generatemapping.py +1039 -0
- cumulusci/tasks/bulkdata/tests/test_load.py +3175 -0
- cumulusci/tasks/bulkdata/tests/test_mapping_parser.py +1658 -0
- cumulusci/tasks/bulkdata/tests/test_query_db__joins_self_lookups.yml +12 -0
- cumulusci/tasks/bulkdata/tests/test_query_db_joins_lookups.yml +26 -0
- cumulusci/tasks/bulkdata/tests/test_query_db_joins_lookups_select.yml +48 -0
- cumulusci/tasks/bulkdata/tests/test_select.py +171 -0
- cumulusci/tasks/bulkdata/tests/test_select_utils.py +1057 -0
- cumulusci/tasks/bulkdata/tests/test_snowfakery.py +1153 -0
- cumulusci/tasks/bulkdata/tests/test_step.py +3957 -0
- cumulusci/tasks/bulkdata/tests/test_updates.py +513 -0
- cumulusci/tasks/bulkdata/tests/test_upsert.py +1015 -0
- cumulusci/tasks/bulkdata/tests/test_utils.py +158 -0
- cumulusci/tasks/bulkdata/tests/testdata.db +0 -0
- cumulusci/tasks/bulkdata/tests/update_describe.py +50 -0
- cumulusci/tasks/bulkdata/tests/update_person_accounts.yml +23 -0
- cumulusci/tasks/bulkdata/tests/utils.py +114 -0
- cumulusci/tasks/bulkdata/update_data.py +260 -0
- cumulusci/tasks/bulkdata/upsert_utils.py +130 -0
- cumulusci/tasks/bulkdata/utils.py +249 -0
- cumulusci/tasks/command.py +178 -0
- cumulusci/tasks/connectedapp.py +186 -0
- cumulusci/tasks/create_package_version.py +778 -0
- cumulusci/tasks/datadictionary.py +745 -0
- cumulusci/tasks/dx_convert_from.py +26 -0
- cumulusci/tasks/github/__init__.py +17 -0
- cumulusci/tasks/github/base.py +16 -0
- cumulusci/tasks/github/commit_status.py +13 -0
- cumulusci/tasks/github/merge.py +11 -0
- cumulusci/tasks/github/publish.py +11 -0
- cumulusci/tasks/github/pull_request.py +11 -0
- cumulusci/tasks/github/release.py +11 -0
- cumulusci/tasks/github/release_report.py +11 -0
- cumulusci/tasks/github/tag.py +11 -0
- cumulusci/tasks/github/tests/__init__.py +0 -0
- cumulusci/tasks/github/tests/test_util.py +202 -0
- cumulusci/tasks/github/tests/test_vcs_migration.py +44 -0
- cumulusci/tasks/github/tests/util_github_api.py +666 -0
- cumulusci/tasks/github/util.py +252 -0
- cumulusci/tasks/marketing_cloud/__init__.py +0 -0
- cumulusci/tasks/marketing_cloud/api.py +188 -0
- cumulusci/tasks/marketing_cloud/base.py +38 -0
- cumulusci/tasks/marketing_cloud/deploy.py +345 -0
- cumulusci/tasks/marketing_cloud/get_user_info.py +40 -0
- cumulusci/tasks/marketing_cloud/mc_constants.py +1 -0
- cumulusci/tasks/marketing_cloud/tests/__init__.py +0 -0
- cumulusci/tasks/marketing_cloud/tests/conftest.py +46 -0
- cumulusci/tasks/marketing_cloud/tests/expected-payload.json +110 -0
- cumulusci/tasks/marketing_cloud/tests/test_api.py +97 -0
- cumulusci/tasks/marketing_cloud/tests/test_api_soap_envelopes.py +145 -0
- cumulusci/tasks/marketing_cloud/tests/test_base.py +14 -0
- cumulusci/tasks/marketing_cloud/tests/test_deploy.py +400 -0
- cumulusci/tasks/marketing_cloud/tests/test_get_user_info.py +141 -0
- cumulusci/tasks/marketing_cloud/tests/validation-response.json +39 -0
- cumulusci/tasks/metadata/__init__.py +0 -0
- cumulusci/tasks/metadata/ee_src.py +94 -0
- cumulusci/tasks/metadata/managed_src.py +100 -0
- cumulusci/tasks/metadata/metadata_map.yml +868 -0
- cumulusci/tasks/metadata/modify.py +99 -0
- cumulusci/tasks/metadata/package.py +684 -0
- cumulusci/tasks/metadata/tests/__init__.py +0 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/.hidden/.keep +0 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/destructiveChanges.xml +9 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/package.xml +9 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/package_install_uninstall.xml +11 -0
- cumulusci/tasks/metadata/tests/package_metadata/namespaced_report_folder/reports/namespace__TestFolder/TestReport.report +3 -0
- cumulusci/tasks/metadata/tests/sample_package.xml +9 -0
- cumulusci/tasks/metadata/tests/test_ee_src.py +112 -0
- cumulusci/tasks/metadata/tests/test_managed_src.py +111 -0
- cumulusci/tasks/metadata/tests/test_modify.py +123 -0
- cumulusci/tasks/metadata/tests/test_package.py +476 -0
- cumulusci/tasks/metadata_etl/__init__.py +29 -0
- cumulusci/tasks/metadata_etl/base.py +436 -0
- cumulusci/tasks/metadata_etl/duplicate_rules.py +24 -0
- cumulusci/tasks/metadata_etl/field_sets.py +70 -0
- cumulusci/tasks/metadata_etl/help_text.py +92 -0
- cumulusci/tasks/metadata_etl/layouts.py +550 -0
- cumulusci/tasks/metadata_etl/objects.py +68 -0
- cumulusci/tasks/metadata_etl/permissions.py +167 -0
- cumulusci/tasks/metadata_etl/picklists.py +221 -0
- cumulusci/tasks/metadata_etl/remote_site_settings.py +99 -0
- cumulusci/tasks/metadata_etl/sharing.py +138 -0
- cumulusci/tasks/metadata_etl/tests/test_base.py +512 -0
- cumulusci/tasks/metadata_etl/tests/test_duplicate_rules.py +22 -0
- cumulusci/tasks/metadata_etl/tests/test_field_sets.py +156 -0
- cumulusci/tasks/metadata_etl/tests/test_help_text.py +387 -0
- cumulusci/tasks/metadata_etl/tests/test_ip_ranges.py +85 -0
- cumulusci/tasks/metadata_etl/tests/test_layouts.py +858 -0
- cumulusci/tasks/metadata_etl/tests/test_objects.py +236 -0
- cumulusci/tasks/metadata_etl/tests/test_permissions.py +223 -0
- cumulusci/tasks/metadata_etl/tests/test_picklists.py +547 -0
- cumulusci/tasks/metadata_etl/tests/test_remote_site_settings.py +46 -0
- cumulusci/tasks/metadata_etl/tests/test_sharing.py +333 -0
- cumulusci/tasks/metadata_etl/tests/test_value_sets.py +298 -0
- cumulusci/tasks/metadata_etl/value_sets.py +106 -0
- cumulusci/tasks/metadeploy.py +393 -0
- cumulusci/tasks/metaxml.py +88 -0
- cumulusci/tasks/preflight/__init__.py +0 -0
- cumulusci/tasks/preflight/dataset_load.py +49 -0
- cumulusci/tasks/preflight/licenses.py +86 -0
- cumulusci/tasks/preflight/packages.py +14 -0
- cumulusci/tasks/preflight/permsets.py +23 -0
- cumulusci/tasks/preflight/recordtypes.py +16 -0
- cumulusci/tasks/preflight/retrieve_tasks.py +30 -0
- cumulusci/tasks/preflight/settings.py +77 -0
- cumulusci/tasks/preflight/sobjects.py +202 -0
- cumulusci/tasks/preflight/tests/test_dataset_load.py +85 -0
- cumulusci/tasks/preflight/tests/test_licenses.py +174 -0
- cumulusci/tasks/preflight/tests/test_packages.py +14 -0
- cumulusci/tasks/preflight/tests/test_permset_preflights.py +51 -0
- cumulusci/tasks/preflight/tests/test_recordtypes.py +30 -0
- cumulusci/tasks/preflight/tests/test_retrieve_tasks.py +62 -0
- cumulusci/tasks/preflight/tests/test_settings.py +130 -0
- cumulusci/tasks/preflight/tests/test_sobjects.py +231 -0
- cumulusci/tasks/push/README.md +59 -0
- cumulusci/tasks/push/__init__.py +0 -0
- cumulusci/tasks/push/push_api.py +659 -0
- cumulusci/tasks/push/pushfails.py +136 -0
- cumulusci/tasks/push/tasks.py +476 -0
- cumulusci/tasks/push/tests/conftest.py +263 -0
- cumulusci/tasks/push/tests/test_push_api.py +951 -0
- cumulusci/tasks/push/tests/test_push_tasks.py +659 -0
- cumulusci/tasks/release_notes/README.md +63 -0
- cumulusci/tasks/release_notes/__init__.py +0 -0
- cumulusci/tasks/release_notes/exceptions.py +5 -0
- cumulusci/tasks/release_notes/generator.py +137 -0
- cumulusci/tasks/release_notes/parser.py +232 -0
- cumulusci/tasks/release_notes/provider.py +44 -0
- cumulusci/tasks/release_notes/task.py +300 -0
- cumulusci/tasks/release_notes/tests/__init__.py +0 -0
- cumulusci/tasks/release_notes/tests/change_notes/full/example1.md +17 -0
- cumulusci/tasks/release_notes/tests/change_notes/multi/1.txt +1 -0
- cumulusci/tasks/release_notes/tests/change_notes/multi/2.txt +1 -0
- cumulusci/tasks/release_notes/tests/change_notes/multi/3.txt +1 -0
- cumulusci/tasks/release_notes/tests/change_notes/single/1.txt +1 -0
- cumulusci/tasks/release_notes/tests/test_generator.py +582 -0
- cumulusci/tasks/release_notes/tests/test_parser.py +867 -0
- cumulusci/tasks/release_notes/tests/test_provider.py +512 -0
- cumulusci/tasks/release_notes/tests/test_task.py +461 -0
- cumulusci/tasks/release_notes/tests/utils.py +153 -0
- cumulusci/tasks/robotframework/__init__.py +3 -0
- cumulusci/tasks/robotframework/debugger/DebugListener.py +100 -0
- cumulusci/tasks/robotframework/debugger/__init__.py +10 -0
- cumulusci/tasks/robotframework/debugger/model.py +87 -0
- cumulusci/tasks/robotframework/debugger/ui.py +259 -0
- cumulusci/tasks/robotframework/libdoc.py +269 -0
- cumulusci/tasks/robotframework/robotframework.py +392 -0
- cumulusci/tasks/robotframework/stylesheet.css +130 -0
- cumulusci/tasks/robotframework/template.html +109 -0
- cumulusci/tasks/robotframework/tests/TestLibrary.py +18 -0
- cumulusci/tasks/robotframework/tests/TestPageObjects.py +31 -0
- cumulusci/tasks/robotframework/tests/TestResource.robot +8 -0
- cumulusci/tasks/robotframework/tests/failing_tests.robot +16 -0
- cumulusci/tasks/robotframework/tests/performance.robot +23 -0
- cumulusci/tasks/robotframework/tests/test_browser_proxies.py +137 -0
- cumulusci/tasks/robotframework/tests/test_debugger.py +360 -0
- cumulusci/tasks/robotframework/tests/test_robot_parallel.py +141 -0
- cumulusci/tasks/robotframework/tests/test_robotframework.py +860 -0
- cumulusci/tasks/salesforce/BaseRetrieveMetadata.py +58 -0
- cumulusci/tasks/salesforce/BaseSalesforceApiTask.py +45 -0
- cumulusci/tasks/salesforce/BaseSalesforceMetadataApiTask.py +18 -0
- cumulusci/tasks/salesforce/BaseSalesforceTask.py +4 -0
- cumulusci/tasks/salesforce/BaseUninstallMetadata.py +41 -0
- cumulusci/tasks/salesforce/CreateCommunity.py +124 -0
- cumulusci/tasks/salesforce/CreatePackage.py +29 -0
- cumulusci/tasks/salesforce/Deploy.py +240 -0
- cumulusci/tasks/salesforce/DeployBundles.py +88 -0
- cumulusci/tasks/salesforce/DescribeMetadataTypes.py +26 -0
- cumulusci/tasks/salesforce/EnsureRecordTypes.py +202 -0
- cumulusci/tasks/salesforce/GetInstalledPackages.py +8 -0
- cumulusci/tasks/salesforce/ListCommunities.py +40 -0
- cumulusci/tasks/salesforce/ListCommunityTemplates.py +19 -0
- cumulusci/tasks/salesforce/PublishCommunity.py +62 -0
- cumulusci/tasks/salesforce/RetrievePackaged.py +41 -0
- cumulusci/tasks/salesforce/RetrieveReportsAndDashboards.py +82 -0
- cumulusci/tasks/salesforce/RetrieveUnpackaged.py +36 -0
- cumulusci/tasks/salesforce/SOQLQuery.py +39 -0
- cumulusci/tasks/salesforce/UninstallLocal.py +15 -0
- cumulusci/tasks/salesforce/UninstallLocalBundles.py +28 -0
- cumulusci/tasks/salesforce/UninstallLocalNamespacedBundles.py +58 -0
- cumulusci/tasks/salesforce/UninstallPackage.py +32 -0
- cumulusci/tasks/salesforce/UninstallPackaged.py +56 -0
- cumulusci/tasks/salesforce/UpdateAdminProfile.py +8 -0
- cumulusci/tasks/salesforce/__init__.py +79 -0
- cumulusci/tasks/salesforce/activate_flow.py +74 -0
- cumulusci/tasks/salesforce/check_components.py +324 -0
- cumulusci/tasks/salesforce/composite.py +142 -0
- cumulusci/tasks/salesforce/create_permission_sets.py +35 -0
- cumulusci/tasks/salesforce/custom_settings.py +134 -0
- cumulusci/tasks/salesforce/custom_settings_wait.py +132 -0
- cumulusci/tasks/salesforce/enable_prediction.py +107 -0
- cumulusci/tasks/salesforce/insert_record.py +40 -0
- cumulusci/tasks/salesforce/install_package_version.py +242 -0
- cumulusci/tasks/salesforce/license_preflights.py +8 -0
- cumulusci/tasks/salesforce/network_member_group.py +178 -0
- cumulusci/tasks/salesforce/nonsourcetracking.py +228 -0
- cumulusci/tasks/salesforce/org_settings.py +193 -0
- cumulusci/tasks/salesforce/package_upload.py +328 -0
- cumulusci/tasks/salesforce/profiles.py +74 -0
- cumulusci/tasks/salesforce/promote_package_version.py +376 -0
- cumulusci/tasks/salesforce/retrieve_profile.py +195 -0
- cumulusci/tasks/salesforce/salesforce_files.py +244 -0
- cumulusci/tasks/salesforce/sourcetracking.py +507 -0
- cumulusci/tasks/salesforce/tests/__init__.py +3 -0
- cumulusci/tasks/salesforce/tests/test_CreateCommunity.py +278 -0
- cumulusci/tasks/salesforce/tests/test_CreatePackage.py +22 -0
- cumulusci/tasks/salesforce/tests/test_Deploy.py +470 -0
- cumulusci/tasks/salesforce/tests/test_DeployBundles.py +76 -0
- cumulusci/tasks/salesforce/tests/test_EnsureRecordTypes.py +345 -0
- cumulusci/tasks/salesforce/tests/test_ListCommunities.py +84 -0
- cumulusci/tasks/salesforce/tests/test_ListCommunityTemplates.py +49 -0
- cumulusci/tasks/salesforce/tests/test_PackageUpload.py +547 -0
- cumulusci/tasks/salesforce/tests/test_ProfileGrantAllAccess.py +699 -0
- cumulusci/tasks/salesforce/tests/test_PublishCommunity.py +181 -0
- cumulusci/tasks/salesforce/tests/test_RetrievePackaged.py +24 -0
- cumulusci/tasks/salesforce/tests/test_RetrieveReportsAndDashboards.py +56 -0
- cumulusci/tasks/salesforce/tests/test_RetrieveUnpackaged.py +21 -0
- cumulusci/tasks/salesforce/tests/test_SOQLQuery.py +30 -0
- cumulusci/tasks/salesforce/tests/test_UninstallLocal.py +15 -0
- cumulusci/tasks/salesforce/tests/test_UninstallLocalBundles.py +19 -0
- cumulusci/tasks/salesforce/tests/test_UninstallLocalNamespacedBundles.py +22 -0
- cumulusci/tasks/salesforce/tests/test_UninstallPackage.py +19 -0
- cumulusci/tasks/salesforce/tests/test_UninstallPackaged.py +66 -0
- cumulusci/tasks/salesforce/tests/test_UninstallPackagedIncremental.py +127 -0
- cumulusci/tasks/salesforce/tests/test_activate_flow.py +132 -0
- cumulusci/tasks/salesforce/tests/test_base_tasks.py +110 -0
- cumulusci/tasks/salesforce/tests/test_check_components.py +445 -0
- cumulusci/tasks/salesforce/tests/test_composite.py +250 -0
- cumulusci/tasks/salesforce/tests/test_create_permission_sets.py +41 -0
- cumulusci/tasks/salesforce/tests/test_custom_settings.py +227 -0
- cumulusci/tasks/salesforce/tests/test_custom_settings_wait.py +174 -0
- cumulusci/tasks/salesforce/tests/test_describemetadatatypes.py +18 -0
- cumulusci/tasks/salesforce/tests/test_enable_prediction.py +240 -0
- cumulusci/tasks/salesforce/tests/test_insert_record.py +110 -0
- cumulusci/tasks/salesforce/tests/test_install_package_version.py +464 -0
- cumulusci/tasks/salesforce/tests/test_network_member_group.py +444 -0
- cumulusci/tasks/salesforce/tests/test_nonsourcetracking.py +235 -0
- cumulusci/tasks/salesforce/tests/test_org_settings.py +407 -0
- cumulusci/tasks/salesforce/tests/test_profiles.py +202 -0
- cumulusci/tasks/salesforce/tests/test_retrieve_profile.py +287 -0
- cumulusci/tasks/salesforce/tests/test_salesforce_files.py +228 -0
- cumulusci/tasks/salesforce/tests/test_sourcetracking.py +350 -0
- cumulusci/tasks/salesforce/tests/test_trigger_handlers.py +300 -0
- cumulusci/tasks/salesforce/tests/test_update_dependencies.py +509 -0
- cumulusci/tasks/salesforce/tests/util.py +79 -0
- cumulusci/tasks/salesforce/trigger_handlers.py +119 -0
- cumulusci/tasks/salesforce/uninstall_packaged_incremental.py +136 -0
- cumulusci/tasks/salesforce/update_dependencies.py +290 -0
- cumulusci/tasks/salesforce/update_profile.py +339 -0
- cumulusci/tasks/salesforce/users/permsets.py +227 -0
- cumulusci/tasks/salesforce/users/photos.py +162 -0
- cumulusci/tasks/salesforce/users/tests/photo.mock.txt +1 -0
- cumulusci/tasks/salesforce/users/tests/test_permsets.py +950 -0
- cumulusci/tasks/salesforce/users/tests/test_photos.py +373 -0
- cumulusci/tasks/sample_data/capture_sample_data.py +77 -0
- cumulusci/tasks/sample_data/load_sample_data.py +85 -0
- cumulusci/tasks/sample_data/test_capture_sample_data.py +117 -0
- cumulusci/tasks/sample_data/test_load_sample_data.py +121 -0
- cumulusci/tasks/sfdx.py +83 -0
- cumulusci/tasks/tests/__init__.py +1 -0
- cumulusci/tasks/tests/conftest.py +30 -0
- cumulusci/tasks/tests/test_command.py +129 -0
- cumulusci/tasks/tests/test_connectedapp.py +236 -0
- cumulusci/tasks/tests/test_create_package_version.py +847 -0
- cumulusci/tasks/tests/test_datadictionary.py +1575 -0
- cumulusci/tasks/tests/test_dx_convert_from.py +60 -0
- cumulusci/tasks/tests/test_metadeploy.py +624 -0
- cumulusci/tasks/tests/test_metaxml.py +99 -0
- cumulusci/tasks/tests/test_promote_package_version.py +488 -0
- cumulusci/tasks/tests/test_pushfails.py +96 -0
- cumulusci/tasks/tests/test_salesforce.py +72 -0
- cumulusci/tasks/tests/test_sfdx.py +105 -0
- cumulusci/tasks/tests/test_util.py +207 -0
- cumulusci/tasks/util.py +261 -0
- cumulusci/tasks/vcs/__init__.py +19 -0
- cumulusci/tasks/vcs/commit_status.py +58 -0
- cumulusci/tasks/vcs/create_commit_status.py +37 -0
- cumulusci/tasks/vcs/download_extract.py +199 -0
- cumulusci/tasks/vcs/merge.py +298 -0
- cumulusci/tasks/vcs/publish.py +207 -0
- cumulusci/tasks/vcs/pull_request.py +9 -0
- cumulusci/tasks/vcs/release.py +134 -0
- cumulusci/tasks/vcs/release_report.py +105 -0
- cumulusci/tasks/vcs/tag.py +31 -0
- cumulusci/tasks/vcs/tests/github/test_commit_status.py +196 -0
- cumulusci/tasks/vcs/tests/github/test_download_extract.py +896 -0
- cumulusci/tasks/vcs/tests/github/test_merge.py +1118 -0
- cumulusci/tasks/vcs/tests/github/test_publish.py +823 -0
- cumulusci/tasks/vcs/tests/github/test_pull_request.py +29 -0
- cumulusci/tasks/vcs/tests/github/test_release.py +390 -0
- cumulusci/tasks/vcs/tests/github/test_release_report.py +109 -0
- cumulusci/tasks/vcs/tests/github/test_tag.py +90 -0
- cumulusci/tasks/vlocity/exceptions.py +2 -0
- cumulusci/tasks/vlocity/tests/test_vlocity.py +283 -0
- cumulusci/tasks/vlocity/vlocity.py +342 -0
- cumulusci/tests/__init__.py +1 -0
- cumulusci/tests/cassettes/GET_sobjects_Account_PersonAccount_describe.yaml +18 -0
- cumulusci/tests/cassettes/TestIntegrationInfrastructure.test_integration_tests.yaml +19 -0
- cumulusci/tests/pytest_plugins/pytest_sf_orgconnect.py +307 -0
- cumulusci/tests/pytest_plugins/pytest_sf_vcr.py +275 -0
- cumulusci/tests/pytest_plugins/pytest_sf_vcr_serializer.py +160 -0
- cumulusci/tests/pytest_plugins/pytest_typeguard.py +5 -0
- cumulusci/tests/pytest_plugins/test_vcr_string_compressor.py +49 -0
- cumulusci/tests/pytest_plugins/vcr_string_compressor.py +97 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Account_describe.yaml +18 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Case_describe.yaml +18 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Contact_describe.yaml +4838 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Custom__c_describe.yaml +242 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Event_describe.yaml +19 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Global_describe.yaml +1338 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Lead_describe.yaml +18 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_OpportunityContactRole_describe.yaml +34 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Opportunity_describe.yaml +1261 -0
- cumulusci/tests/shared_cassettes/GET_sobjects_Organization.yaml +49 -0
- cumulusci/tests/shared_cassettes/vcr_string_templates/batchInfoList_xml.tpl +15 -0
- cumulusci/tests/shared_cassettes/vcr_string_templates/batchInfo_xml.tpl +13 -0
- cumulusci/tests/shared_cassettes/vcr_string_templates/jobInfo_insert_xml.tpl +24 -0
- cumulusci/tests/shared_cassettes/vcr_string_templates/jobInfo_upsert_xml.tpl +25 -0
- cumulusci/tests/test_entry_points.py +20 -0
- cumulusci/tests/test_integration_infrastructure.py +131 -0
- cumulusci/tests/test_main.py +9 -0
- cumulusci/tests/test_schema.py +32 -0
- cumulusci/tests/test_utils.py +657 -0
- cumulusci/tests/test_vcr_serializer.py +134 -0
- cumulusci/tests/uncompressed_cassette.yaml +83 -0
- cumulusci/tests/util.py +344 -0
- cumulusci/utils/__init__.py +731 -0
- cumulusci/utils/classutils.py +9 -0
- cumulusci/utils/collections.py +32 -0
- cumulusci/utils/deprecation.py +11 -0
- cumulusci/utils/encryption.py +31 -0
- cumulusci/utils/fileutils.py +295 -0
- cumulusci/utils/git.py +142 -0
- cumulusci/utils/http/multi_request.py +214 -0
- cumulusci/utils/http/requests_utils.py +103 -0
- cumulusci/utils/http/tests/cassettes/ManualEditTestCompositeParallelSalesforce.test_http_headers.yaml +32 -0
- cumulusci/utils/http/tests/cassettes/TestCompositeParallelSalesforce.test_composite_parallel_salesforce.yaml +65 -0
- cumulusci/utils/http/tests/cassettes/TestCompositeParallelSalesforce.test_errors.yaml +24 -0
- cumulusci/utils/http/tests/cassettes/TestCompositeParallelSalesforce.test_reference_ids.yaml +49 -0
- cumulusci/utils/http/tests/test_multi_request.py +255 -0
- cumulusci/utils/iterators.py +21 -0
- cumulusci/utils/logging.py +128 -0
- cumulusci/utils/metaprogramming.py +10 -0
- cumulusci/utils/options.py +138 -0
- cumulusci/utils/parallel/queries_in_parallel/run_queries_in_parallel.py +29 -0
- cumulusci/utils/parallel/queries_in_parallel/tests/test_run_queries_in_parallel.py +50 -0
- cumulusci/utils/parallel/task_worker_queues/parallel_worker.py +238 -0
- cumulusci/utils/parallel/task_worker_queues/parallel_worker_queue.py +243 -0
- cumulusci/utils/parallel/task_worker_queues/tests/test_parallel_worker.py +353 -0
- cumulusci/utils/salesforce/count_sobjects.py +46 -0
- cumulusci/utils/salesforce/soql.py +17 -0
- cumulusci/utils/salesforce/tests/cassettes/ManualEdit_TestCountSObjects.test_count_sobjects__network_errors.yaml +23 -0
- cumulusci/utils/salesforce/tests/cassettes/TestCountSObjects.test_count_sobjects__errors.yaml +33 -0
- cumulusci/utils/salesforce/tests/cassettes/TestCountSObjects.test_count_sobjects_simple.yaml +29 -0
- cumulusci/utils/salesforce/tests/test_count_sobjects.py +29 -0
- cumulusci/utils/salesforce/tests/test_soql.py +30 -0
- cumulusci/utils/tests/cassettes/ManualEditTestDescribeOrg.test_minimal_schema.yaml +36 -0
- cumulusci/utils/tests/cassettes/ManualEdit_test_describe_to_sql.yaml +191 -0
- cumulusci/utils/tests/test_fileutils.py +284 -0
- cumulusci/utils/tests/test_git.py +85 -0
- cumulusci/utils/tests/test_logging.py +70 -0
- cumulusci/utils/tests/test_option_parsing.py +188 -0
- cumulusci/utils/tests/test_org_schema.py +691 -0
- cumulusci/utils/tests/test_org_schema_models.py +79 -0
- cumulusci/utils/tests/test_waiting.py +25 -0
- cumulusci/utils/version_strings.py +391 -0
- cumulusci/utils/waiting.py +42 -0
- cumulusci/utils/xml/__init__.py +91 -0
- cumulusci/utils/xml/metadata_tree.py +299 -0
- cumulusci/utils/xml/robot_xml.py +114 -0
- cumulusci/utils/xml/salesforce_encoding.py +100 -0
- cumulusci/utils/xml/test/test_metadata_tree.py +251 -0
- cumulusci/utils/xml/test/test_salesforce_encoding.py +173 -0
- cumulusci/utils/yaml/cumulusci_yml.py +401 -0
- cumulusci/utils/yaml/model_parser.py +156 -0
- cumulusci/utils/yaml/safer_loader.py +74 -0
- cumulusci/utils/yaml/tests/bad_cci.yml +5 -0
- cumulusci/utils/yaml/tests/cassettes/TestCumulusciYml.test_validate_url__with_errors.yaml +20 -0
- cumulusci/utils/yaml/tests/test_cumulusci_yml.py +286 -0
- cumulusci/utils/yaml/tests/test_model_parser.py +175 -0
- cumulusci/utils/yaml/tests/test_safer_loader.py +88 -0
- cumulusci/utils/ziputils.py +61 -0
- cumulusci/vcs/base.py +143 -0
- cumulusci/vcs/bootstrap.py +272 -0
- cumulusci/vcs/github/__init__.py +24 -0
- cumulusci/vcs/github/adapter.py +689 -0
- cumulusci/vcs/github/release_notes/generator.py +219 -0
- cumulusci/vcs/github/release_notes/parser.py +151 -0
- cumulusci/vcs/github/release_notes/provider.py +143 -0
- cumulusci/vcs/github/service.py +569 -0
- cumulusci/vcs/github/tests/test_adapter.py +138 -0
- cumulusci/vcs/github/tests/test_service.py +408 -0
- cumulusci/vcs/models.py +586 -0
- cumulusci/vcs/tests/conftest.py +41 -0
- cumulusci/vcs/tests/dummy_service.py +241 -0
- cumulusci/vcs/tests/test_vcs_base.py +687 -0
- cumulusci/vcs/tests/test_vcs_bootstrap.py +727 -0
- cumulusci/vcs/utils/__init__.py +31 -0
- cumulusci/vcs/vcs_source.py +287 -0
- cumulusci_plus-5.0.0.dist-info/METADATA +145 -0
- cumulusci_plus-5.0.0.dist-info/RECORD +744 -0
- cumulusci_plus-5.0.0.dist-info/WHEEL +4 -0
- cumulusci_plus-5.0.0.dist-info/entry_points.txt +3 -0
- cumulusci_plus-5.0.0.dist-info/licenses/AUTHORS.rst +41 -0
- cumulusci_plus-5.0.0.dist-info/licenses/LICENSE +30 -0
|
@@ -0,0 +1,835 @@
|
|
|
1
|
+
""" CumulusCI Tasks for running Apex Tests """
|
|
2
|
+
|
|
3
|
+
import html
|
|
4
|
+
import io
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
from cumulusci.core.exceptions import (
|
|
9
|
+
ApexTestException,
|
|
10
|
+
CumulusCIException,
|
|
11
|
+
TaskOptionsError,
|
|
12
|
+
)
|
|
13
|
+
from cumulusci.core.utils import decode_to_unicode, process_bool_arg, process_list_arg, determine_managed_mode
|
|
14
|
+
from cumulusci.tasks.salesforce import BaseSalesforceApiTask
|
|
15
|
+
from cumulusci.utils.http.requests_utils import safe_json_from_response
|
|
16
|
+
|
|
17
|
+
APEX_LIMITS = {
|
|
18
|
+
"Soql": {
|
|
19
|
+
"Label": "TESTING_LIMITS: Number of SOQL queries",
|
|
20
|
+
"SYNC": 100,
|
|
21
|
+
"ASYNC": 200,
|
|
22
|
+
},
|
|
23
|
+
"Email": {
|
|
24
|
+
"Label": "TESTING_LIMITS: Number of Email Invocations",
|
|
25
|
+
"SYNC": 10,
|
|
26
|
+
"ASYNC": 10,
|
|
27
|
+
},
|
|
28
|
+
"AsyncCalls": {
|
|
29
|
+
"Label": "TESTING_LIMITS: Number of future calls",
|
|
30
|
+
"SYNC": 50,
|
|
31
|
+
"ASYNC": 50,
|
|
32
|
+
},
|
|
33
|
+
"DmlRows": {
|
|
34
|
+
"Label": "TESTING_LIMITS: Number of DML rows",
|
|
35
|
+
"SYNC": 10000,
|
|
36
|
+
"ASYNC": 10000,
|
|
37
|
+
},
|
|
38
|
+
"Cpu": {"Label": "TESTING_LIMITS: Maximum CPU time", "SYNC": 10000, "ASYNC": 60000},
|
|
39
|
+
"QueryRows": {
|
|
40
|
+
"Label": "TESTING_LIMITS: Number of query rows",
|
|
41
|
+
"SYNC": 50000,
|
|
42
|
+
"ASYNC": 50000,
|
|
43
|
+
},
|
|
44
|
+
"Dml": {
|
|
45
|
+
"Label": "TESTING_LIMITS: Number of DML statements",
|
|
46
|
+
"SYNC": 150,
|
|
47
|
+
"ASYNC": 150,
|
|
48
|
+
},
|
|
49
|
+
"MobilePush": {
|
|
50
|
+
"Label": "TESTING_LIMITS: Number of Mobile Apex push calls",
|
|
51
|
+
"SYNC": 10,
|
|
52
|
+
"ASYNC": 10,
|
|
53
|
+
},
|
|
54
|
+
"Sosl": {
|
|
55
|
+
"Label": "TESTING_LIMITS: Number of SOSL queries",
|
|
56
|
+
"SYNC": 20,
|
|
57
|
+
"ASYNC": 20,
|
|
58
|
+
},
|
|
59
|
+
"Callouts": {
|
|
60
|
+
"Label": "TESTING_LIMITS: Number of callouts",
|
|
61
|
+
"SYNC": 100,
|
|
62
|
+
"ASYNC": 100,
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
TEST_RESULT_QUERY = """
|
|
68
|
+
SELECT Id,ApexClassId,TestTimestamp,
|
|
69
|
+
Message,MethodName,Outcome,
|
|
70
|
+
RunTime,StackTrace,
|
|
71
|
+
(SELECT
|
|
72
|
+
Id,Callouts,AsyncCalls,DmlRows,Email,
|
|
73
|
+
LimitContext,LimitExceptions,MobilePush,
|
|
74
|
+
QueryRows,Sosl,Cpu,Dml,Soql
|
|
75
|
+
FROM ApexTestResults)
|
|
76
|
+
FROM ApexTestResult
|
|
77
|
+
WHERE AsyncApexJobId='{}'
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class RunApexTests(BaseSalesforceApiTask):
|
|
82
|
+
"""Task to run Apex tests with the Tooling API and report results.
|
|
83
|
+
|
|
84
|
+
This task optionally supports retrying unit tests that fail due to
|
|
85
|
+
transitory issues or concurrency-related row locks. To enable retries,
|
|
86
|
+
add ones or more regular expressions to the list option `retry_failures`.
|
|
87
|
+
|
|
88
|
+
When a test run fails, if all of the failures' error messages or stack traces
|
|
89
|
+
match one of these regular expressions, each failed test will be retried by
|
|
90
|
+
itself. This is often useful when running Apex tests in parallel; row locks
|
|
91
|
+
may automatically be retried. Note that retries are supported whether or not
|
|
92
|
+
the org has parallel Apex testing enabled.
|
|
93
|
+
|
|
94
|
+
The ``retry_always`` option modifies this behavior: if a test run fails and
|
|
95
|
+
any (not all) of the failures match the specified regular expressions,
|
|
96
|
+
all of the failed tests will be retried in serial. This is helpful when
|
|
97
|
+
underlying row locking errors are masked by custom exceptions.
|
|
98
|
+
|
|
99
|
+
A useful base configuration for projects wishing to use retries is:
|
|
100
|
+
|
|
101
|
+
.. code-block:: yaml
|
|
102
|
+
|
|
103
|
+
retry_failures:
|
|
104
|
+
- "unable to obtain exclusive access to this record"
|
|
105
|
+
- "UNABLE_TO_LOCK_ROW"
|
|
106
|
+
- "connection was cancelled here"
|
|
107
|
+
retry_always: True
|
|
108
|
+
|
|
109
|
+
Some projects' unit tests produce so many concurrency errors that
|
|
110
|
+
it's faster to execute the entire run in serial mode than to use retries.
|
|
111
|
+
Serial and parallel mode are configured in the scratch org definition file."""
|
|
112
|
+
|
|
113
|
+
api_version = "38.0"
|
|
114
|
+
name = "RunApexTests"
|
|
115
|
+
task_options = {
|
|
116
|
+
"test_name_match": {
|
|
117
|
+
"description": (
|
|
118
|
+
"Pattern to find Apex test classes to run "
|
|
119
|
+
'("%" is wildcard). Defaults to '
|
|
120
|
+
"project__test__name_match from project config. "
|
|
121
|
+
"Comma-separated list for multiple patterns."
|
|
122
|
+
),
|
|
123
|
+
},
|
|
124
|
+
"test_name_exclude": {
|
|
125
|
+
"description": (
|
|
126
|
+
"Query to find Apex test classes to exclude "
|
|
127
|
+
'("%" is wildcard). Defaults to '
|
|
128
|
+
"project__test__name_exclude from project config. "
|
|
129
|
+
"Comma-separated list for multiple patterns."
|
|
130
|
+
)
|
|
131
|
+
},
|
|
132
|
+
"namespace": {
|
|
133
|
+
"description": (
|
|
134
|
+
"Salesforce project namespace. Defaults to "
|
|
135
|
+
+ "project__package__namespace"
|
|
136
|
+
)
|
|
137
|
+
},
|
|
138
|
+
"managed": {
|
|
139
|
+
"description": (
|
|
140
|
+
"If True, search for tests in the namespace "
|
|
141
|
+
+ "only. Defaults to False"
|
|
142
|
+
)
|
|
143
|
+
},
|
|
144
|
+
"poll_interval": {
|
|
145
|
+
"description": ("Seconds to wait between polling for Apex test results.")
|
|
146
|
+
},
|
|
147
|
+
"junit_output": {
|
|
148
|
+
"description": "File name for JUnit output. Defaults to test_results.xml"
|
|
149
|
+
},
|
|
150
|
+
"json_output": {
|
|
151
|
+
"description": "File name for json output. Defaults to test_results.json"
|
|
152
|
+
},
|
|
153
|
+
"retry_failures": {
|
|
154
|
+
"description": "A list of regular expression patterns to match against "
|
|
155
|
+
"test failures. If failures match, the failing tests are retried in "
|
|
156
|
+
"serial mode."
|
|
157
|
+
},
|
|
158
|
+
"retry_always": {
|
|
159
|
+
"description": "By default, all failures must match retry_failures to perform "
|
|
160
|
+
"a retry. Set retry_always to True to retry all failed tests if any failure matches."
|
|
161
|
+
},
|
|
162
|
+
"required_org_code_coverage_percent": {
|
|
163
|
+
"description": "Require at least X percent code coverage across the org following the test run.",
|
|
164
|
+
"usage": "--required_org_code_coverage_percent PERCENTAGE",
|
|
165
|
+
},
|
|
166
|
+
"required_per_class_code_coverage_percent": {
|
|
167
|
+
"description": "Require at least X percent code coverage for every class in the org.",
|
|
168
|
+
},
|
|
169
|
+
"verbose": {
|
|
170
|
+
"description": "By default, only failures get detailed output. "
|
|
171
|
+
"Set verbose to True to see all passed test methods."
|
|
172
|
+
},
|
|
173
|
+
"test_suite_names": {
|
|
174
|
+
"description": "Accepts a comma-separated list of test suite names. Only runs test classes that are part of the test suites specified."
|
|
175
|
+
},
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
def _init_options(self, kwargs):
|
|
179
|
+
super(RunApexTests, self)._init_options(kwargs)
|
|
180
|
+
|
|
181
|
+
self.options["test_name_match"] = self.options.get(
|
|
182
|
+
"test_name_match", self.project_config.project__test__name_match
|
|
183
|
+
)
|
|
184
|
+
self.options["test_name_exclude"] = self.options.get(
|
|
185
|
+
"test_name_exclude", self.project_config.project__test__name_exclude
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
self.options["test_suite_names"] = self.options.get(
|
|
189
|
+
"test_suite_names", self.project_config.project__test__suite__names
|
|
190
|
+
)
|
|
191
|
+
if self.options["test_name_match"] is None:
|
|
192
|
+
self.options["test_name_match"] = ""
|
|
193
|
+
|
|
194
|
+
if self.options["test_name_exclude"] is None:
|
|
195
|
+
self.options["test_name_exclude"] = ""
|
|
196
|
+
|
|
197
|
+
self.options["namespace"] = self.options.get(
|
|
198
|
+
"namespace", self.project_config.project__package__namespace
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
self.options["junit_output"] = self.options.get(
|
|
202
|
+
"junit_output", "test_results.xml"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
self.options["json_output"] = self.options.get(
|
|
206
|
+
"json_output", "test_results.json"
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
self.options["retry_failures"] = process_list_arg(
|
|
210
|
+
self.options.get("retry_failures", [])
|
|
211
|
+
)
|
|
212
|
+
compiled_res = []
|
|
213
|
+
for regex in self.options["retry_failures"]:
|
|
214
|
+
try:
|
|
215
|
+
compiled_res.append(re.compile(regex))
|
|
216
|
+
except re.error as e:
|
|
217
|
+
raise TaskOptionsError(
|
|
218
|
+
"An invalid regular expression ({}) was provided ({})".format(
|
|
219
|
+
regex, e
|
|
220
|
+
)
|
|
221
|
+
)
|
|
222
|
+
self.options["retry_failures"] = compiled_res
|
|
223
|
+
self.options["retry_always"] = process_bool_arg(
|
|
224
|
+
self.options.get("retry_always") or False
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
self.verbose = process_bool_arg(self.options.get("verbose") or False)
|
|
228
|
+
|
|
229
|
+
self.counts = {}
|
|
230
|
+
|
|
231
|
+
if "required_org_code_coverage_percent" in self.options:
|
|
232
|
+
try:
|
|
233
|
+
self.code_coverage_level = int(
|
|
234
|
+
str(self.options["required_org_code_coverage_percent"]).rstrip("%")
|
|
235
|
+
)
|
|
236
|
+
except ValueError:
|
|
237
|
+
raise TaskOptionsError(
|
|
238
|
+
f"Invalid code coverage level {self.options['required_org_code_coverage_percent']}"
|
|
239
|
+
)
|
|
240
|
+
else:
|
|
241
|
+
self.code_coverage_level = 0
|
|
242
|
+
|
|
243
|
+
self.required_per_class_code_coverage_percent = int(
|
|
244
|
+
self.options.get("required_per_class_code_coverage_percent", 0)
|
|
245
|
+
)
|
|
246
|
+
# Raises a TaskOptionsError when the user provides both test_suite_names and test_name_match.
|
|
247
|
+
if (self.options["test_suite_names"]) and (
|
|
248
|
+
self.options["test_name_match"] is not None
|
|
249
|
+
and self.options["test_name_match"] != "%_TEST%"
|
|
250
|
+
):
|
|
251
|
+
raise TaskOptionsError(
|
|
252
|
+
"Both test_suite_names and test_name_match cannot be passed simultaneously"
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
# pylint: disable=W0201
|
|
256
|
+
def _init_class(self):
|
|
257
|
+
self.classes_by_id = {}
|
|
258
|
+
self.classes_by_name = {}
|
|
259
|
+
self.job_id = None
|
|
260
|
+
self.results_by_class_name = {}
|
|
261
|
+
self.result = None
|
|
262
|
+
self.retry_details = None
|
|
263
|
+
|
|
264
|
+
def _get_namespace_filter(self):
|
|
265
|
+
|
|
266
|
+
if self.options.get("managed"):
|
|
267
|
+
|
|
268
|
+
namespace = self.options.get("namespace")
|
|
269
|
+
|
|
270
|
+
if not namespace:
|
|
271
|
+
raise TaskOptionsError(
|
|
272
|
+
"Running tests in managed mode but no namespace available."
|
|
273
|
+
)
|
|
274
|
+
namespace = "'{}'".format(namespace)
|
|
275
|
+
elif self.org_config.namespace:
|
|
276
|
+
namespace = self.org_config.namespace
|
|
277
|
+
namespace = "'{}'".format(namespace)
|
|
278
|
+
else:
|
|
279
|
+
namespace = "null"
|
|
280
|
+
return namespace
|
|
281
|
+
|
|
282
|
+
def _get_test_class_query(self):
|
|
283
|
+
namespace = self._get_namespace_filter()
|
|
284
|
+
# Split by commas to allow multiple class name matching options
|
|
285
|
+
test_name_match = self.options["test_name_match"]
|
|
286
|
+
included_tests = []
|
|
287
|
+
for pattern in test_name_match.split(","):
|
|
288
|
+
if pattern:
|
|
289
|
+
included_tests.append("Name LIKE '{}'".format(pattern))
|
|
290
|
+
# Add any excludes to the where clause
|
|
291
|
+
test_name_exclude = self.options.get("test_name_exclude", "")
|
|
292
|
+
excluded_tests = []
|
|
293
|
+
for pattern in test_name_exclude.split(","):
|
|
294
|
+
if pattern:
|
|
295
|
+
excluded_tests.append("(NOT Name LIKE '{}')".format(pattern))
|
|
296
|
+
# Get all test classes for namespace
|
|
297
|
+
query = "SELECT Id, Name FROM ApexClass " + "WHERE NamespacePrefix = {}".format(
|
|
298
|
+
namespace
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
if included_tests:
|
|
302
|
+
query += " AND ({})".format(" OR ".join(included_tests))
|
|
303
|
+
if excluded_tests:
|
|
304
|
+
query += " AND {}".format(" AND ".join(excluded_tests))
|
|
305
|
+
return query
|
|
306
|
+
|
|
307
|
+
def _get_test_classes(self):
|
|
308
|
+
# If test_suite_names is provided, execute only tests that are a part of the list of test suites provided.
|
|
309
|
+
if self.options["test_suite_names"]:
|
|
310
|
+
test_classes_from_test_suite_names = (
|
|
311
|
+
self._get_test_classes_from_test_suite_names()
|
|
312
|
+
)
|
|
313
|
+
return test_classes_from_test_suite_names
|
|
314
|
+
|
|
315
|
+
# test_suite_names is not provided. Fetch all the test classes from the org.
|
|
316
|
+
else:
|
|
317
|
+
return self._get_all_test_classes()
|
|
318
|
+
|
|
319
|
+
def _get_all_test_classes(self):
|
|
320
|
+
# Fetches all the test classes from the org.
|
|
321
|
+
query = self._get_test_class_query()
|
|
322
|
+
self.logger.info("Fetching all the test classes...")
|
|
323
|
+
result = self.tooling.query_all(query)
|
|
324
|
+
self.logger.info("Found {} test classes".format(result["totalSize"]))
|
|
325
|
+
return result
|
|
326
|
+
|
|
327
|
+
def _get_comma_separated_string_of_items(self, itemlist):
|
|
328
|
+
# Accepts a list of strings. A formatted string is returned.
|
|
329
|
+
# Example: Input: ['TestSuite1', 'TestSuite2'] Output: ''TestSuite1','TestSuite2''
|
|
330
|
+
return ",".join([f"'{item}'" for item in itemlist])
|
|
331
|
+
|
|
332
|
+
def _get_test_suite_ids_from_test_suite_names_query(self, test_suite_names_arg):
|
|
333
|
+
# Returns a query string which when executed fetches the test suite ids of the list of test suite names.
|
|
334
|
+
test_suite_names = self._get_comma_separated_string_of_items(
|
|
335
|
+
test_suite_names_arg.split(",")
|
|
336
|
+
)
|
|
337
|
+
query1 = f"SELECT Id, TestSuiteName FROM ApexTestSuite WHERE TestSuiteName IN ({test_suite_names})"
|
|
338
|
+
return query1
|
|
339
|
+
|
|
340
|
+
def _get_test_classes_from_test_suite_ids_query(self, testSuiteIds):
|
|
341
|
+
# Returns a query string which when executed fetches Apex test classes for the given list of test suite ids.
|
|
342
|
+
# Apex test classes passed under test_name_exclude are ignored.
|
|
343
|
+
testSuiteIds_formatted = self._get_comma_separated_string_of_items(testSuiteIds)
|
|
344
|
+
|
|
345
|
+
if len(testSuiteIds_formatted) == 0:
|
|
346
|
+
testSuiteIds_formatted = "''"
|
|
347
|
+
|
|
348
|
+
test_name_exclude_arg = self.options["test_name_exclude"]
|
|
349
|
+
condition = ""
|
|
350
|
+
|
|
351
|
+
# Check if test_name_exclude is provided. Append to query string if the former is specified.
|
|
352
|
+
if test_name_exclude_arg:
|
|
353
|
+
test_name_exclude = self._get_comma_separated_string_of_items(
|
|
354
|
+
test_name_exclude_arg.split(",")
|
|
355
|
+
)
|
|
356
|
+
condition = f"AND Name NOT IN ({test_name_exclude})"
|
|
357
|
+
|
|
358
|
+
query = f"SELECT Id, Name FROM ApexClass WHERE Id IN (SELECT ApexClassId FROM TestSuiteMembership WHERE ApexTestSuiteId IN ({testSuiteIds_formatted})) {condition}"
|
|
359
|
+
return query
|
|
360
|
+
|
|
361
|
+
def _get_test_classes_from_test_suite_names(self):
|
|
362
|
+
# Returns a list of Apex test classes that belong to the test suite(s) specified. Test classes specified in test_name_exclude are excluded.
|
|
363
|
+
test_suite_names_arg = self.options["test_suite_names"]
|
|
364
|
+
query1 = self._get_test_suite_ids_from_test_suite_names_query(
|
|
365
|
+
test_suite_names_arg
|
|
366
|
+
)
|
|
367
|
+
self.logger.info("Fetching test suite metadata...")
|
|
368
|
+
result = self.tooling.query_all(query1)
|
|
369
|
+
testSuiteIds = []
|
|
370
|
+
|
|
371
|
+
for record in result["records"]:
|
|
372
|
+
testSuiteIds.append(str(record["Id"]))
|
|
373
|
+
|
|
374
|
+
query2 = self._get_test_classes_from_test_suite_ids_query(testSuiteIds)
|
|
375
|
+
self.logger.info("Fetching test classes belonging to the test suite(s)...")
|
|
376
|
+
result = self.tooling.query_all(query2)
|
|
377
|
+
self.logger.info("Found {} test classes".format(result["totalSize"]))
|
|
378
|
+
return result
|
|
379
|
+
|
|
380
|
+
def _get_test_methods_for_class(self, class_name):
|
|
381
|
+
result = self.tooling.query(
|
|
382
|
+
f"SELECT SymbolTable FROM ApexClass WHERE Name='{class_name}'"
|
|
383
|
+
)
|
|
384
|
+
test_methods = []
|
|
385
|
+
|
|
386
|
+
try:
|
|
387
|
+
methods = result["records"][0]["SymbolTable"]["methods"]
|
|
388
|
+
except (TypeError, IndexError, KeyError):
|
|
389
|
+
raise CumulusCIException(
|
|
390
|
+
f"Unable to acquire symbol table for failed Apex class {class_name}"
|
|
391
|
+
)
|
|
392
|
+
for m in methods:
|
|
393
|
+
for a in m.get("annotations", []):
|
|
394
|
+
if a["name"].lower() in ["istest", "testmethod"]:
|
|
395
|
+
test_methods.append(m["name"])
|
|
396
|
+
break
|
|
397
|
+
|
|
398
|
+
return test_methods
|
|
399
|
+
|
|
400
|
+
def _is_retriable_error_message(self, error_message):
|
|
401
|
+
return any(
|
|
402
|
+
[reg.search(error_message) for reg in self.options["retry_failures"]]
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
def _is_retriable_failure(self, test_result):
|
|
406
|
+
return self._is_retriable_error_message(
|
|
407
|
+
test_result["Message"] or ""
|
|
408
|
+
) or self._is_retriable_error_message(test_result["StackTrace"] or "")
|
|
409
|
+
|
|
410
|
+
def _get_test_results(self, allow_retries=True):
|
|
411
|
+
# We need to query at both the test result and test queue item level.
|
|
412
|
+
# Some concurrency problems manifest as all or part of the class failing,
|
|
413
|
+
# without leaving behind any visible ApexTestResult records.
|
|
414
|
+
# See https://salesforce.stackexchange.com/questions/262893/any-way-to-get-consistent-test-counts-when-parallel-testing-is-used
|
|
415
|
+
|
|
416
|
+
# First, gather the Ids of failed test classes.
|
|
417
|
+
test_classes = self.tooling.query_all(
|
|
418
|
+
"SELECT Id, Status, ExtendedStatus, ApexClassId FROM ApexTestQueueItem "
|
|
419
|
+
+ "WHERE ParentJobId = '{}' AND Status = 'Failed'".format(self.job_id)
|
|
420
|
+
)
|
|
421
|
+
class_level_errors = {
|
|
422
|
+
each_class["ApexClassId"]: each_class["ExtendedStatus"]
|
|
423
|
+
for each_class in test_classes["records"]
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
result = self.tooling.query_all(TEST_RESULT_QUERY.format(self.job_id))
|
|
427
|
+
|
|
428
|
+
if allow_retries:
|
|
429
|
+
self.retry_details = {}
|
|
430
|
+
|
|
431
|
+
for test_result in result["records"]:
|
|
432
|
+
class_name = self.classes_by_id[test_result["ApexClassId"]]
|
|
433
|
+
self.results_by_class_name[class_name][
|
|
434
|
+
test_result["MethodName"]
|
|
435
|
+
] = test_result
|
|
436
|
+
self.counts[test_result["Outcome"]] += 1
|
|
437
|
+
|
|
438
|
+
# If we have class-level failures that did not come with line-level
|
|
439
|
+
# failure details, report those as well.
|
|
440
|
+
for class_id, error in class_level_errors.items():
|
|
441
|
+
class_name = self.classes_by_id[class_id]
|
|
442
|
+
|
|
443
|
+
self.logger.error(
|
|
444
|
+
f"Class {class_name} failed to run some tests with the message {error}. Applying error to unit test results."
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
# In Spring '20, we cannot get symbol tables for managed classes.
|
|
448
|
+
if self.options.get("managed"):
|
|
449
|
+
self.logger.error(
|
|
450
|
+
f"Cannot access symbol table for managed class {class_name}. Failure will not be retried."
|
|
451
|
+
)
|
|
452
|
+
continue
|
|
453
|
+
|
|
454
|
+
# Get all the method names for this class
|
|
455
|
+
test_methods = self._get_test_methods_for_class(class_name)
|
|
456
|
+
for test_method in test_methods:
|
|
457
|
+
# If this method was not run due to a class-level failure,
|
|
458
|
+
# synthesize a failed result.
|
|
459
|
+
# If we're retrying and fail again, do the same.
|
|
460
|
+
if (
|
|
461
|
+
test_method not in self.results_by_class_name[class_name]
|
|
462
|
+
or self.results_by_class_name[class_name][test_method]["Outcome"]
|
|
463
|
+
== "Fail"
|
|
464
|
+
):
|
|
465
|
+
self.results_by_class_name[class_name][test_method] = {
|
|
466
|
+
"ApexClassId": class_id,
|
|
467
|
+
"MethodName": test_method,
|
|
468
|
+
"Outcome": "Fail",
|
|
469
|
+
"Message": f"Containing class {class_name} failed with message {error}",
|
|
470
|
+
"StackTrace": "",
|
|
471
|
+
"RunTime": 0,
|
|
472
|
+
}
|
|
473
|
+
self.counts["Fail"] += 1
|
|
474
|
+
|
|
475
|
+
if allow_retries:
|
|
476
|
+
for class_name, results in self.results_by_class_name.items():
|
|
477
|
+
for test_result in results.values():
|
|
478
|
+
# Determine whether this failure is retriable.
|
|
479
|
+
if test_result["Outcome"] == "Fail" and allow_retries:
|
|
480
|
+
can_retry_this_failure = self._is_retriable_failure(test_result)
|
|
481
|
+
if can_retry_this_failure:
|
|
482
|
+
self.counts["Retriable"] += 1
|
|
483
|
+
|
|
484
|
+
# Even if this failure is not retriable per se,
|
|
485
|
+
# persist its details if we might end up retrying
|
|
486
|
+
# all failures.
|
|
487
|
+
if self.options["retry_always"] or can_retry_this_failure:
|
|
488
|
+
self.retry_details.setdefault(
|
|
489
|
+
test_result["ApexClassId"], []
|
|
490
|
+
).append(test_result["MethodName"])
|
|
491
|
+
|
|
492
|
+
def _process_test_results(self):
|
|
493
|
+
test_results = []
|
|
494
|
+
class_names = list(self.results_by_class_name.keys())
|
|
495
|
+
class_names.sort()
|
|
496
|
+
for class_name in class_names:
|
|
497
|
+
self.retry_details = {}
|
|
498
|
+
method_names = list(self.results_by_class_name[class_name].keys())
|
|
499
|
+
# Added to process for the None methodnames
|
|
500
|
+
|
|
501
|
+
if None in method_names:
|
|
502
|
+
class_id = self.classes_by_name[class_name]
|
|
503
|
+
self.retry_details.setdefault(class_id, []).append(
|
|
504
|
+
self._get_test_methods_for_class(class_name)
|
|
505
|
+
)
|
|
506
|
+
del self.results_by_class_name[class_name][None]
|
|
507
|
+
self.logger.info(
|
|
508
|
+
f"Retrying class with id: {class_id} name:{class_name} due to `None` methodname"
|
|
509
|
+
)
|
|
510
|
+
self.counts["Retriable"] += len(self.retry_details[class_id])
|
|
511
|
+
self._attempt_retries()
|
|
512
|
+
|
|
513
|
+
has_failures = any(
|
|
514
|
+
result["Outcome"] in ["Fail", "CompileFail"]
|
|
515
|
+
for result in self.results_by_class_name[class_name].values()
|
|
516
|
+
)
|
|
517
|
+
if has_failures or self.verbose:
|
|
518
|
+
self.logger.info(f"Class: {class_name}")
|
|
519
|
+
method_names = list(self.results_by_class_name[class_name].keys())
|
|
520
|
+
method_names.sort()
|
|
521
|
+
for method_name in method_names:
|
|
522
|
+
result = self.results_by_class_name[class_name][method_name]
|
|
523
|
+
message = f"\t{result['Outcome']}: {result['MethodName']}"
|
|
524
|
+
duration = result["RunTime"]
|
|
525
|
+
result["stats"] = self._get_stats_from_result(result)
|
|
526
|
+
if duration:
|
|
527
|
+
message += f" ({duration}ms)"
|
|
528
|
+
test_results.append(
|
|
529
|
+
{
|
|
530
|
+
"Children": result.get("children", None),
|
|
531
|
+
"ClassName": decode_to_unicode(class_name),
|
|
532
|
+
"Method": decode_to_unicode(result["MethodName"]),
|
|
533
|
+
"Message": decode_to_unicode(result["Message"]),
|
|
534
|
+
"Outcome": decode_to_unicode(result["Outcome"]),
|
|
535
|
+
"StackTrace": decode_to_unicode(result["StackTrace"]),
|
|
536
|
+
"Stats": result.get("stats", None),
|
|
537
|
+
"TestTimestamp": result.get("TestTimestamp", None),
|
|
538
|
+
}
|
|
539
|
+
)
|
|
540
|
+
if result["Outcome"] in ["Fail", "CompileFail"]:
|
|
541
|
+
self.logger.info(message)
|
|
542
|
+
self.logger.info(f"\tMessage: {result['Message']}")
|
|
543
|
+
self.logger.info(f"\tStackTrace: {result['StackTrace']}")
|
|
544
|
+
elif self.verbose:
|
|
545
|
+
self.logger.info(message)
|
|
546
|
+
self.logger.info("-" * 80)
|
|
547
|
+
self.logger.info(
|
|
548
|
+
"Pass: {} Retried: {} Fail: {} CompileFail: {} Skip: {}".format(
|
|
549
|
+
self.counts["Pass"],
|
|
550
|
+
self.counts["Retriable"],
|
|
551
|
+
self.counts["Fail"],
|
|
552
|
+
self.counts["CompileFail"],
|
|
553
|
+
self.counts["Skip"],
|
|
554
|
+
)
|
|
555
|
+
)
|
|
556
|
+
self.logger.info("-" * 80)
|
|
557
|
+
if self.counts["Fail"] or self.counts["CompileFail"]:
|
|
558
|
+
self.logger.error("-" * 80)
|
|
559
|
+
self.logger.error("Failing Tests")
|
|
560
|
+
self.logger.error("-" * 80)
|
|
561
|
+
counter = 0
|
|
562
|
+
for result in test_results:
|
|
563
|
+
if result["Outcome"] in ["Fail", "CompileFail"]:
|
|
564
|
+
counter += 1
|
|
565
|
+
self.logger.error(
|
|
566
|
+
"{}: {}.{} - {}".format(
|
|
567
|
+
counter,
|
|
568
|
+
result["ClassName"],
|
|
569
|
+
result["Method"],
|
|
570
|
+
result["Outcome"],
|
|
571
|
+
)
|
|
572
|
+
)
|
|
573
|
+
self.logger.error(f"\tMessage: {result['Message']}")
|
|
574
|
+
self.logger.error(f"\tStackTrace: {result['StackTrace']}")
|
|
575
|
+
|
|
576
|
+
return test_results
|
|
577
|
+
|
|
578
|
+
def _get_stats_from_result(self, result):
|
|
579
|
+
stats = {"duration": result["RunTime"]}
|
|
580
|
+
|
|
581
|
+
if result.get("ApexTestResults", None):
|
|
582
|
+
for limit_name, details in APEX_LIMITS.items():
|
|
583
|
+
limit_use = result["ApexTestResults"]["records"][0][limit_name]
|
|
584
|
+
limit_allowed = details[
|
|
585
|
+
result["ApexTestResults"]["records"][0]["LimitContext"]
|
|
586
|
+
]
|
|
587
|
+
stats[details["Label"]] = {"used": limit_use, "allowed": limit_allowed}
|
|
588
|
+
|
|
589
|
+
return stats
|
|
590
|
+
|
|
591
|
+
def _enqueue_test_run(self, class_ids):
|
|
592
|
+
if isinstance(class_ids, dict):
|
|
593
|
+
body = {
|
|
594
|
+
"tests": [
|
|
595
|
+
{"classId": class_id, "testMethods": class_ids[class_id]}
|
|
596
|
+
for class_id in class_ids
|
|
597
|
+
]
|
|
598
|
+
}
|
|
599
|
+
else:
|
|
600
|
+
body = {"classids": ",".join(class_ids)}
|
|
601
|
+
|
|
602
|
+
return safe_json_from_response(
|
|
603
|
+
self.tooling._call_salesforce(
|
|
604
|
+
method="POST",
|
|
605
|
+
url=self.tooling.base_url + "runTestsAsynchronous",
|
|
606
|
+
json=body,
|
|
607
|
+
)
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
def _init_task(self):
|
|
611
|
+
super()._init_task()
|
|
612
|
+
self.options["managed"] = determine_managed_mode(
|
|
613
|
+
self.options, self.project_config, self.org_config
|
|
614
|
+
)
|
|
615
|
+
|
|
616
|
+
def _run_task(self):
|
|
617
|
+
result = self._get_test_classes()
|
|
618
|
+
if result["totalSize"] == 0:
|
|
619
|
+
return
|
|
620
|
+
for test_class in result["records"]:
|
|
621
|
+
self.classes_by_id[test_class["Id"]] = test_class["Name"]
|
|
622
|
+
self.classes_by_name[test_class["Name"]] = test_class["Id"]
|
|
623
|
+
self.results_by_class_name[test_class["Name"]] = {}
|
|
624
|
+
self.logger.info("Queuing tests for execution...")
|
|
625
|
+
|
|
626
|
+
self.counts = {
|
|
627
|
+
"Pass": 0,
|
|
628
|
+
"Fail": 0,
|
|
629
|
+
"CompileFail": 0,
|
|
630
|
+
"Skip": 0,
|
|
631
|
+
"Retriable": 0,
|
|
632
|
+
}
|
|
633
|
+
self.job_id = self._enqueue_test_run(
|
|
634
|
+
(str(id) for id in self.classes_by_id.keys())
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
self._wait_for_tests()
|
|
638
|
+
self._get_test_results()
|
|
639
|
+
|
|
640
|
+
# Did we get back retriable test results? Check our retry policy,
|
|
641
|
+
# then enqueue new runs individually, until either (a) all retriable
|
|
642
|
+
# tests succeed or (b) a test fails.
|
|
643
|
+
able_to_retry = (self.counts["Retriable"] and self.options["retry_always"]) or (
|
|
644
|
+
self.counts["Retriable"] and self.counts["Retriable"] == self.counts["Fail"]
|
|
645
|
+
)
|
|
646
|
+
if not able_to_retry:
|
|
647
|
+
self.counts["Retriable"] = 0
|
|
648
|
+
else:
|
|
649
|
+
self._attempt_retries()
|
|
650
|
+
|
|
651
|
+
test_results = self._process_test_results()
|
|
652
|
+
self._write_output(test_results)
|
|
653
|
+
|
|
654
|
+
if self.counts.get("Fail") or self.counts.get("CompileFail"):
|
|
655
|
+
raise ApexTestException(
|
|
656
|
+
"{} tests failed and {} tests failed compilation".format(
|
|
657
|
+
self.counts.get("Fail"), self.counts.get("CompileFail")
|
|
658
|
+
)
|
|
659
|
+
)
|
|
660
|
+
|
|
661
|
+
if self.code_coverage_level or self.required_per_class_code_coverage_percent:
|
|
662
|
+
if self.options.get("namespace") not in self.org_config.installed_packages:
|
|
663
|
+
self._check_code_coverage()
|
|
664
|
+
else:
|
|
665
|
+
self.logger.info(
|
|
666
|
+
"This org contains a managed installation; not checking code coverage."
|
|
667
|
+
)
|
|
668
|
+
else:
|
|
669
|
+
self.logger.info(
|
|
670
|
+
"No code coverage level specified; not checking code coverage."
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
def _check_code_coverage(self):
|
|
674
|
+
self.logger.info("Checking code coverage.")
|
|
675
|
+
class_level_coverage_failures = {}
|
|
676
|
+
|
|
677
|
+
# Query for Class level code coverage using the aggregate
|
|
678
|
+
if self.required_per_class_code_coverage_percent:
|
|
679
|
+
test_classes = self.tooling.query(
|
|
680
|
+
"SELECT ApexClassOrTrigger.Name, ApexClassOrTriggerId, NumLinesCovered, NumLinesUncovered FROM ApexCodeCoverageAggregate ORDER BY ApexClassOrTrigger.Name ASC"
|
|
681
|
+
)["records"]
|
|
682
|
+
|
|
683
|
+
coverage_percentage = 0
|
|
684
|
+
for class_level in test_classes:
|
|
685
|
+
total = (
|
|
686
|
+
class_level["NumLinesCovered"] + class_level["NumLinesUncovered"]
|
|
687
|
+
)
|
|
688
|
+
# prevent division by 0 errors
|
|
689
|
+
if total:
|
|
690
|
+
# calculate coverage percentage
|
|
691
|
+
coverage_percentage = round(
|
|
692
|
+
(class_level["NumLinesCovered"] / total) * 100,
|
|
693
|
+
2,
|
|
694
|
+
)
|
|
695
|
+
|
|
696
|
+
if coverage_percentage < self.required_per_class_code_coverage_percent:
|
|
697
|
+
class_level_coverage_failures[
|
|
698
|
+
class_level["ApexClassOrTrigger"]["Name"]
|
|
699
|
+
] = coverage_percentage
|
|
700
|
+
|
|
701
|
+
# Query for OrgWide coverage
|
|
702
|
+
result = self.tooling.query("SELECT PercentCovered FROM ApexOrgWideCoverage")
|
|
703
|
+
coverage = result["records"][0]["PercentCovered"]
|
|
704
|
+
|
|
705
|
+
errors = []
|
|
706
|
+
if self.required_per_class_code_coverage_percent:
|
|
707
|
+
if class_level_coverage_failures:
|
|
708
|
+
for class_name in class_level_coverage_failures.keys():
|
|
709
|
+
errors.append(
|
|
710
|
+
f"{class_name}'s code coverage of {class_level_coverage_failures[class_name]}% is below required level of {self.required_per_class_code_coverage_percent}."
|
|
711
|
+
)
|
|
712
|
+
else:
|
|
713
|
+
self.logger.info(
|
|
714
|
+
f"All classes meet code coverage expectations of {self.required_per_class_code_coverage_percent}% ."
|
|
715
|
+
)
|
|
716
|
+
|
|
717
|
+
if coverage < self.code_coverage_level:
|
|
718
|
+
errors.append(
|
|
719
|
+
f"Organization-wide code coverage of {coverage}% is below required level of {self.code_coverage_level}"
|
|
720
|
+
)
|
|
721
|
+
else:
|
|
722
|
+
self.logger.info(
|
|
723
|
+
f"Organization-wide code coverage of {coverage}% meets expectations."
|
|
724
|
+
)
|
|
725
|
+
|
|
726
|
+
if errors:
|
|
727
|
+
error_message = "\n".join(errors)
|
|
728
|
+
self.logger.info(error_message)
|
|
729
|
+
raise ApexTestException(error_message)
|
|
730
|
+
|
|
731
|
+
def _attempt_retries(self):
|
|
732
|
+
total_method_retries = sum(
|
|
733
|
+
[len(test_list) for test_list in self.retry_details.values()]
|
|
734
|
+
)
|
|
735
|
+
self.logger.warning(
|
|
736
|
+
"Retrying {} failed methods from {} test classes".format(
|
|
737
|
+
total_method_retries, len(self.retry_details)
|
|
738
|
+
)
|
|
739
|
+
)
|
|
740
|
+
self.counts["Fail"] = 0
|
|
741
|
+
|
|
742
|
+
for class_id, test_list in self.retry_details.items():
|
|
743
|
+
for each_test in test_list:
|
|
744
|
+
self.logger.warning(
|
|
745
|
+
"Retrying {}.{}".format(self.classes_by_id[class_id], each_test)
|
|
746
|
+
)
|
|
747
|
+
self.job_id = self._enqueue_test_run({class_id: [each_test]})
|
|
748
|
+
self._wait_for_tests()
|
|
749
|
+
self._get_test_results(allow_retries=False)
|
|
750
|
+
|
|
751
|
+
# If the retry failed, report the remaining failures.
|
|
752
|
+
if self.counts["Fail"]:
|
|
753
|
+
self.logger.error("Test retry failed.")
|
|
754
|
+
|
|
755
|
+
def _wait_for_tests(self):
|
|
756
|
+
self.poll_complete = False
|
|
757
|
+
self.poll_interval_s = int(self.options.get("poll_interval", 1))
|
|
758
|
+
self.poll_count = 0
|
|
759
|
+
self._poll()
|
|
760
|
+
|
|
761
|
+
def _poll_action(self):
|
|
762
|
+
self.result = self.tooling.query_all(
|
|
763
|
+
"SELECT Id, Status, ApexClassId FROM ApexTestQueueItem "
|
|
764
|
+
+ "WHERE ParentJobId = '{}'".format(self.job_id)
|
|
765
|
+
)
|
|
766
|
+
counts = {
|
|
767
|
+
"Aborted": 0,
|
|
768
|
+
"Completed": 0,
|
|
769
|
+
"Failed": 0,
|
|
770
|
+
"Holding": 0,
|
|
771
|
+
"Preparing": 0,
|
|
772
|
+
"Processing": 0,
|
|
773
|
+
"Queued": 0,
|
|
774
|
+
}
|
|
775
|
+
processing_class_id = None
|
|
776
|
+
total_test_count = self.result["totalSize"]
|
|
777
|
+
for test_queue_item in self.result["records"]:
|
|
778
|
+
counts[test_queue_item["Status"]] += 1
|
|
779
|
+
if test_queue_item["Status"] == "Processing":
|
|
780
|
+
processing_class_id = test_queue_item["ApexClassId"]
|
|
781
|
+
processing_class = ""
|
|
782
|
+
if counts["Processing"] == 1:
|
|
783
|
+
processing_class = f" ({self.classes_by_id[processing_class_id]})"
|
|
784
|
+
self.logger.info(
|
|
785
|
+
"Completed: {} Processing: {}{} Queued: {}".format(
|
|
786
|
+
counts["Completed"],
|
|
787
|
+
counts["Processing"],
|
|
788
|
+
processing_class,
|
|
789
|
+
counts["Queued"],
|
|
790
|
+
)
|
|
791
|
+
)
|
|
792
|
+
if (
|
|
793
|
+
total_test_count
|
|
794
|
+
== counts["Completed"] + counts["Failed"] + counts["Aborted"]
|
|
795
|
+
):
|
|
796
|
+
self.logger.info("Apex tests completed")
|
|
797
|
+
self.poll_complete = True
|
|
798
|
+
|
|
799
|
+
def _write_output(self, test_results):
|
|
800
|
+
junit_output = self.options["junit_output"]
|
|
801
|
+
if junit_output:
|
|
802
|
+
with io.open(junit_output, mode="w", encoding="utf-8") as f:
|
|
803
|
+
f.write('<testsuite tests="{}">\n'.format(len(test_results)))
|
|
804
|
+
for result in test_results:
|
|
805
|
+
s = ' <testcase classname="{}" name="{}"'.format(
|
|
806
|
+
result["ClassName"], result["Method"]
|
|
807
|
+
)
|
|
808
|
+
if (
|
|
809
|
+
"Stats" in result
|
|
810
|
+
and result["Stats"]
|
|
811
|
+
and "duration" in result["Stats"]
|
|
812
|
+
):
|
|
813
|
+
s += ' time="{}"'.format(result["Stats"]["duration"])
|
|
814
|
+
if result["Outcome"] in ["Fail", "CompileFail"]:
|
|
815
|
+
s += ">\n"
|
|
816
|
+
s += ' <failure type="failed" '
|
|
817
|
+
if result["Message"]:
|
|
818
|
+
s += 'message="{}"'.format(html.escape(result["Message"]))
|
|
819
|
+
s += ">"
|
|
820
|
+
|
|
821
|
+
if result["StackTrace"]:
|
|
822
|
+
s += "<![CDATA[{}]]>".format(
|
|
823
|
+
html.escape(result["StackTrace"])
|
|
824
|
+
)
|
|
825
|
+
s += "</failure>\n"
|
|
826
|
+
s += " </testcase>\n"
|
|
827
|
+
else:
|
|
828
|
+
s += " />\n"
|
|
829
|
+
f.write(str(s))
|
|
830
|
+
f.write("</testsuite>")
|
|
831
|
+
|
|
832
|
+
json_output = self.options["json_output"]
|
|
833
|
+
if json_output:
|
|
834
|
+
with io.open(json_output, mode="w", encoding="utf-8") as f:
|
|
835
|
+
f.write(str(json.dumps(test_results, indent=4)))
|