diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..461ba5b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,44 @@ +root = true + +[*] +max_line_length = 100 +trim_trailing_whitespace = true +insert_final_newline = true +charset = utf-8 +indent_style = space + +[{*.sh,gradlew}] +end_of_line = lf + +[{*.bat,*.cmd}] +end_of_line = crlf + +[*.md] +# Trailing space means "paragraph continues" in markdown +trim_trailing_whitespace = false + +[*.java] +indent_size = 2 +ij_continuation_indent_size = 4 +# Doc: https://youtrack.jetbrains.com/issue/IDEA-170643#focus=streamItem-27-3708697.0-0 +# $ means "static" +ij_java_wrap_long_lines = true +ij_java_wrap_comments = true +ij_java_imports_layout = $*,|,* +ij_java_use_single_class_imports = true +# Below does not seem to work as of 2021.1.1 even though +# https://youtrack.jetbrains.com/issue/IDEA-225733 is resolved +wildcard_import_limit = 999 +ij_kotlin_name_count_to_use_star_import = 999 +ij_kotlin_name_count_to_use_star_import_for_members = 999 +ij_java_class_count_to_use_import_on_demand = 999 +ij_java_names_count_to_use_import_on_demand = 999 + +[*.js] +indent_size = 2 + +[*.xml] +indent_size = 2 + +[{*.yml,*.yaml}] +indent_size = 2 diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..58e8da7 --- /dev/null +++ b/.mailmap @@ -0,0 +1,13 @@ +Abraham Lin +Cédric Beust +Cédric Beust +Cédric Beust +Cédric Beust +Cédric Beust +Cédric Beust +Cédric Beust +Jose Dillet +Julien Herr +Konstantin Savin +Tim wu +Tomás Pollak diff --git a/ANNOUNCEMENT.txt b/ANNOUNCEMENT.txt new file mode 100644 index 0000000..0565cc3 --- /dev/null +++ b/ANNOUNCEMENT.txt @@ -0,0 +1,110 @@ +javalobby.org +testdriven.com +opensourcetesting.org +https://saloon.javaranch.com/cgi-bin/ubb/ultimatebb.cgi?ubb=forum&f=68 +java.net +comp.lang.java.programmer + +========== + +The TestNG team is happy to announce the immediate availability of TestNG 4.5. + +This release contains a lot of bug fixes and a few new features. It also +includes the first phase of Distributed TestNG, a work in progress designed +to transparently distribute tests on many machines and collect their results. + +Announcement: + +https://beust.com/weblog/archives/000361.html + +Details on Distributed TestNG: + +https://beust.com/weblog/archives/000362.html + + +========== + +The TestNG team is happy to announce the immediate availability of TestNG 4.0, with a lot of improvements and new features. + +The announcement, along with a few examples and summary of the new features, can be found here: + + https://tinyurl.com/dxlbh + +-- +C�dric +https://testng.org + +================ + +Announcing TestNG 2.3 + +The TestNG team is happy to announce the availability of TestNG 2.3. + +The version is available at https://beust.com/testng as well as the new documentation, which has been considerably improved (highlighted code snippets, detailed DTD, ant task and description of all the new features). + +What's new: + + * beforeSuite, afterSuite, beforeTest, afterTest + * Revamped ant task with haltonfailure and other helpful flags + * Better stack traces and improved level control for verbosity + * Better syntax for including and excluding methods in testng.xml + * Test classes can be invoked on the command line + * ... and many bug fixes. + +For Eclipse users, a new version (1.1.1) of the Eclipse plug-in that includes this new TestNG version is available on the remote update site or for direct download. + +Also, TestNG has joined OpenSymphony (big thanks to Patrick and Hani for setting this up). As a consequence of this move, there is now a TestNG users forum as well as a Wiki and JIRA for issue tracking. + +The users mailing-list has been moved to Google Groups and is connected to the forum, so you only need to subscribe to one. + +Try it and let us know what you think! + +=== + +I am happy to announce the availability of TestNG 2.1 (https://beust.com/testng). + +TestNG is a testing framework inspired from JUnit and NUnit but introducing some new functionalities that make it more powerful and easier to use, such as: + + * JSR 175 Annotations (JDK 1.4 is also supported with JavaDoc annotations). + * Flexible test configuration. + * Default JDK functions for runtime and logging (no dependencies). + * Powerful execution model (no more TestSuite). + * Supports dependent methods. + +Some of the new features in this version include: + + * invocationCount and successPercentage, which I described in a previous entry (https://beust.com/weblog/archives/000236.html), and which allow you to invoke a test method a certain number of times while allowing some of these invocations to fail. If the number of failures is under a certain threshold, the test is still considered a success. + + * timeOut is now applicable to all test methods. Whether you are running your tests in parallel or not, you can specify a time-out for your test method and if it fails to complete within the given amount of time, TestNG will mark it as a failure. + + * dependsOnMethods was the most requested feature. You can now specify dependencies on a method-based basis (no need to specify a group if your dependency graph is simple). You can even mix dependsOnMethods and dependsOnGroups. + + * ... and of course, numerous bug fixes and other additions. + +A special thanks to Alexandru Popescu who has pulled all-nighters to make this release happen! + +We have an exciting list of new features lined up for our next version, among which a plug-in API, but in the meantime, enjoy TestNG 2.1. + +====== + +Announcing TestNG for Eclipse + +The TestNG team is happy to announce the first release of the TestNG plug-in for Eclipse. + +https://beust.com/testng + +This first release covers the basic functionalities of TestNG, among which: + + * Multiple ways to launch tests (from a method, from a class, groups or an entire suite). + * Convenient report view that lets you directly jump to failed tests. + +A more detailed overview can be found here: + +https://beust.com/weblog/archives/000261.html + +and the documentation and snapshots here: + +https://beust.com/testng/main.html#eclipse + +-- +Cedric diff --git a/CHANGES.txt b/CHANGES.txt new file mode 100644 index 0000000..17b5d25 --- /dev/null +++ b/CHANGES.txt @@ -0,0 +1,1747 @@ +Current +Fixed: GITHUB-2701: Bump gradle version to 7.3.3 to support java17 build (ZhangJian He) +Fixed: GITHUB-2646: Streamline Logging Across TestNG (Krishnan Mahadevan) +Fixed: GITHUB-2658: Inheritance + dependsOnMethods (Krishnan Mahadevan) +Fixed: GITHUB-2664: Order for DependsOnGroups has changed after TestNg 7.4.0 (Krishnan Mahadevan) +Fixed: GITHUB-2501: TestNG 7.4.0 throws an exception "sun.net.www.protocol.file.FileURLConnection cannot be cast to java.net.HttpURLConnection" when xml file contain "ENTITY SYSTEM" grammer (Krishnan Mahadevan) +Fixed: GITHUB-2693: TestNG ignores 'dataproviderthreadcount' CLA (Krishnan Mahadevan) +Fixed: GITHUB-2685: TestInvoker should clear Thread.interrupted flag before calling ITestListeners (Roman Morskyi) +Fixed: GITHUB-2684: AfterGroups config annotation does not consider retries for tests (Roman Morskyi) +Fixed: GITHUB-2689: Yaml parser: implement loadClasses flag (Dzmitry Sankouski) +Fixed: GITHUB-2676: NPE is triggered when working with ITestObjectFactory (Krishnan Mahadevan) +Fixed: GITHUB-2674: Run onTestSkipped for each value from data provider (Krishnan Mahadevan) +Fixed: GITHUB-2672: Log real stacktrace when test times out. (cdalexndr) +Fixed: GITHUB-2669: A failed retry with ITestContext will lose the ITestContext. (Nan Liang) +Fixed: GITHUB-2643: assertEquals(Set,Set) now ignores ordering as it did before. (Elis Edlund) +Fixed: GITHUB-2653: Assert methods requires casting since TestNg 7.0 for mixed boxed and unboxed primitives in assertEquals. +Fixed: GITHUB-2229: Restore @BeforeGroups and @AfterGroups Annotations functionality (Krishnan Mahadevan) +Fixed: GITHUB-2563: Skip test if its data provider provides no data (Krishnan Mahadevan) +Fixed: GITHUB-2535: TestResult.getEndMillis() returns 0 for skipped configuration - after upgrading testng to 7.0 + (Krishnan Mahadevan) +Fixed: GITHUB-2638: "[WARN] Ignoring duplicate listener" appears when running .xml suite with and (Krishnan Mahadevan) +Fixed: GITHUB-1297: Passed configuration methods appear in testng-failed.xml, when failure was after passed test (Dzmitry Sankouski) +New: Decouple configuration unit tests from main suite (Dzmitry Sankouski). +Fixed: GITHUB-2536: Problems with Nested Test Classes (Krishnan Mahadevan) +Fixed: GITHUB-2558:Make IExecutionListener, ITestListener, IInvokedMethodListener, IConfigurationListener, ISuiteListener finish method with reverse order (dianny) +Fixed: GITHUB-2532: Apply commandline switches for suites in jar files (Dzmitry Sankouski). +Fixed: GITHUB-2558: Make IExecutionListener, ITestListener, IInvokedMethodListener, IConfigurationListener, ISuiteListener execute in the order of insertion (Krishnan Mahadevan) +Fixed: GITHUB-2611: Config Failures not included in testng-failed.xml when its part of a different test class (Krishnan Mahadevan) +Fixed: GITHUB-2613: Ignored Tests are not retrieved for a mixed test class (test with enabled, disabled and ignored test method) (Krishnan Mahadevan) +Fixed: GITHUB-849: Performance improvement by fixing hashCode (testn & Vladimir Sitnikov) +Fixed: GITHUB-2570: Use Guice injector for instantiate IRetryAnalyzer (Krishnan Mahadevan) +Fix: use proper instances for beforeClass callback when different instances collide on hash codes +Fixed: Fix parallel and configfailurepolicy parsing in tr_TR locale +Fixed: Wrong results from GuiceBasedObjectDispenser when there's Object#hashCode collision +Changed: GITHUB-2564: Source code is split into several modules for better modularity in the future (for now only a combined jar is released as it was before) +Added: GITHUB-2564: Added license files as META-INF/LICENSE.txt within the released jar +Test: GITHUB-2564: Added pax-exam-based OSGi test to verify the manifest +Changed: GITHUB-2564: Migrated the build to Gradle 7.0.2 +Fixed: GITHUB-2576: Guice 5.0 drops no-aop variant, so TestNG should probably upgrade and avoid no-aop dependency (Nan Liang) +Fixed: GITHUB-2566: Reporter#getOutput(ITestResult tr) uses Map.get(tr.hashCode()) which might result in surprising results (Krishnan Mahadevan) +Fixed: GITHUB-2565: Dataprovider only supporting a raw type for Iterator return type (Krishnan Mahadevan) +Fixed: GITHUB-2557: Flaky test: ThreadAffinityTest#testThreadAffinity (Krishnan Mahadevan) +Fixed: GITHUB-2567: MethodHelper#CANONICAL_NAME_CACHE is never reset, so it could result in a memory leak (Krishnan Mahadevan) +Fixed: GITHUB-2540: assertEquals(Collection) need check order (Stuart Marks & Julien Herr) +Fixed: GITHUB-2360: Groovy 3 internal generated methods are detected as test methods (Ian Springer) +Fixed: GITHUB-2522: TestNG 7.4.0 Can not skip test through listener (Nan Liang) +Fixed: GITHUB-2529: Link to testng.xml in CONTRIBUTING.md file was dead +Fixed: GITHUB-2521: The method has a separate string with 'invocation-number' parameters for each failure (does not group) in 'testng-failed.xml' file for DataProvider + Factory (Pavel Sakharchuk) +Fixed: GITHUB-2426: New feature TestNG - getFactoryMethodParamsInfo on ConfigurationMethod (Krishnan Mahadevan) +Fixed: GITHUB-2517: Factory data-provider parameters not displayed in 'testng-failed.xml' file (Pavel Sakharchuk) +Fixed: GITHUB-279: Guice dependency injection into listeners and reporters (Krishnan Mahadevan) +Fixed: GITHUB-2504: Data provider data cannot be populated to onTestStart hook when BeforeMethod is failed (Krishnan Mahadevan) +Fixed: GITHUB-2489: Hierarchical base- and test-class @AfterClass methods out of order using groups (Krishnan Mahadevan) +Fixed: GITHUB-2493: Avoid NPE from TextReporter execution when a dataprovider method provides null (baflQA) +Fixed: GITHUB-2483: Asymmetric not equals (cdalexndr) +Fixed: GITHUB-2486: assertSame/assertNotSame broken after GITHUB-2296 (Vitalii Diravka) +Fixed: GITHUB-2490: assertNotEquals returns fast when argument is null, not calling equals(Object) (Anindya Roy) +Fixed: GITHUB-2500: Mention in assertEqualsNoOrder doc that arrays are not compared deeply (Marcono1234) +Fixed: GITHUB-2544: TestNG Retry Fails on complex data-provider arguments (Anindya Roy) + +7.4.0 +New : GITHUB-2459: Support configurable start time - emailable report (Barry Evans) +Fixed: GITHUB-2467: XmlTest does not copy the xmlClasses during clone (C.V.Aditya) +Fixed: GITHUB-2469: Parameters added in XmlTest during AlterSuiteListener not available in SuiteListener (C.V.Aditya) +Fixed: GITHUB-2296: Fix for assertEquals not working for sets as order is not guaranteed. (Prashant Maroti) +Fixed: GITHUB-2465: Fix bux where Strings.join returns empty String +Fixed: GITHUB-1632: throwing SkipException sets iTestResult status to Failure instead of Skip (Julien Herr & Krishnan Mahadevan) +New : GITHUB-2456: Add onDataProviderFailure listener (Krishnan Mahadevan) +Fixed: GITHUB-2445: NPE in FailedReporter.java With Tests Created in Factory (Arham Jain) +Fixed: GITHUB-2428: Configuration methods have the same test class instance when @Factory is being used (Nan Liang) +Fixed: GITHUB-2440: Fixed an issue when case timeout returned an incorrect exception and effect the next other test case (Yao Ma) +New: GITHUB-2407: Adds "overrideIncludedMethods" to the global config as a command-line argument, which excludes explicitly included test methods if they belong to any excluded groups (Nikhil Suri) +Fixed: GITHUB-2432: Rework MethodInheritance.fixMethodInheritance to "soft" dependencies (Krishnan Mahadevan) +Fixed: GITHUB-2429: Seggregate Dependency Injection out as a clear implementation (Krishnan Mahadevan) +Fixed: GITHUB-2435: getParameterIndex() always return 0 in test listener +Fixed: GITHUB-2406: TestNG 7.3.0 transitive vulnerability CVE-2020-11022 and CVE-2020-11023 due to JQuery 3.4.1 (Krishnan Mahadevan) +Fixed: GITHUB-2405: Regression: Using TestNG via Maven breaks when optional Guice dependency is unavailable (Krishnan Mahadevan) +Fixed: GITHUB-2427: Guice module (suite parent-module and test module) configure() method is called multiple times (Jacek Centkowski) +Fixed: GITHUB-2419: TestNG JUnit reports are not valid if system output contains XML tags (Lorenzo Orsatti) +Fixed: GITHUB-188: suite parallel="methods" does not work when there are multiple tags in the testng.xml (Krishnan Mahadevan) +Fixed: GITHUB-346: When a method is annotated with both BeforeGroups and AfterGroups only AfterGroup is executed (Krishnan Mahadevan) +Fixed: GITHUB-2403: Suite.xml files attempt to make web request when suite references standard TestNG DTD using HTTP (Krishnan Mahadevan) +New: GITHUB-2385: Make @Listeners can work for implemented interfaces and Inherited class (Nan Liang) +Fixed: GITHUB-2053: MethodHelper.collectAndOrderMethods() Hangs when Parallel Instance and dependsOnGroups (Krishnan Mahadevan) +Fixed: GITHUB-2400: BeforeClass/Method (and AfterClass/Method) configuration methods that override default methods are invoked multiple times (Krishnan Mahadevan) +Fixed: GITHUB-2396: @Ignore on method level doesn't work as expected (Krishnan Mahadevan) +Fixed: GITHUB-2382: TestNG version should be specified in MANIFEST.MF (Krishnan Mahadevan) +Fixed: GITHUB-2096: 7.0.0-beta6 memory issues (regression) (Krishnan Mahadevan) +Fixed: GITHUB-2355: TestNG creates multiple Guice Module Instances (Krishnan Mahadevan) +Fixed: GITHUB-2374: Add file name to the warning message (Krishnan Mahadevan) +Fixed: GITHUB-2321: -Dtestng.thread.affinity=true do not work when running multiple instance of test in parallel (Nan Liang) +Fixed: GITHUB-2363: JS error when switching theme (Krishnan Mahadevan) +Fixed: GITHUB-2361: No way to enforce @Test(singleThreaded = true) when test defined in base class (Krishnan Mahadevan) +Fixed: GITHUB-2343: Injectors are not reused when they share the same set of modules (Krishnan Mahadevan) +Fixed: GITHUB-2346: ITestResult attributes are null when retrieved by Listener onTestStart if test fails at BeforeMethod (Krishnan Mahadevan) +Fixed: GITHUB-2357: TestNG 7.3.0 transitive dependencies + +7.3.0 +Fixed: GITHUB-2328: Add ability to get test method for which configuration method was called (Krishnan Mahadevan) +Fixed: GITHUB-2327: Parameters not present on skipped Test (Eric Kubenka) +Fixed: GITHUB-2232: Null Pointer Exception in ConfigInvoker.setMethodInvocationFailure (Krishnan Mahadevan) +Fixed: GITHUB-2312: IAnnotationTransformer called multiple time Discrepancy between 6.x and 7.x (Krishnan Mahadevan) +New: GITHUB-2315: TextReporter console output does not nicely print native array data parameters (James Sassano) +Fixed: GITHUB-2301: Add support for object-based reporter configurations (Scott Babcock) +New: Deprecate org.testng.ReporterConfig (Julien Herr) +Fixed: GITHUB-2300: Vulnerable Dependency: Please upgrade JCommander to 1.75 or above (Krishnan Mahadevan) +Fixed: GITHUB-2182: Removed exception catching as valid behaviour +Fixed: GITHUB-2273: Use SPI to load Guice modules (Bartosz Popiela) +Fixed: GITHUB-2280: Prevent Retry from happening endlessly (Paweł Nadolski) +Fixed: GITHUB-553: Better error message when dealing with classes having both Constructor and Factory methods (Krishnan Mahadevan) +Fixed: GITHUB-2267: RetryAnalyzer is not set properly and consistent when using dataprovider (Eric Kubenka) +Fixed: GITHUB-2266: Support Test retries via Callbacks (Krishnan Mahadevan) +Fixed: GITHUB-2209: @Before and @After are not executed as expected when a combination of class and method level grouping is applied (Krishnan Mahadevan) +Fixed: GITHUB-2259: Missing configuration for ServiceLoader Listeners (Krishnan Mahadevan) +Fixed: GITHUB-2257: Facilitate retry of configuration methods via call backs (Krishnan Mahadevan) +Fixed: GITHUB-2223: testng 7.1.0 java.lang.ClassNotFoundException: com.google.inject.Stage (Krishnan Mahadevan) +Fixed: GITHUB-217: Configure TestNG to fail when all tests are skipped (Krishnan Mahadevan) +Fixed: GITHUB-2255: Ensure test method parameters are visible in BeforeMethod config method (Krishnan Mahadevan) +Fixed: GITHUB-2251: NullPointerException at test with timeOut (Krishnan Mahadevan) +Fixed: GITHUB-2249: Not abstract super-classes mess up test run order (Sergii Kim) +Fixed: GITHUB-2195: NPE Using groups and @Before/@AfterMethod with alwaysRun and dependsOnMethods (Tomas & Julien Herr) +Fixed: GITHUB-2238: Parameter values should be overridable from JVM arguments (Krishnan Mahadevan) +Fixed: GITHUB-2231: Incorrect hierarchy in testng.xml file created programmatically and failed to run. (Krishnan Mahadevan) +Fixed: GITHUB-2235: expectedExceptions mismatch because of wrapping (Krishnan Mahadevan) +Fixed: GITHUB-2220: ITestListener's methods get called multiple times for one test, when @Listeners annotation is used in multiple test classes (Krishnan Mahadevan) +Fixed: GITHUB-2211: assertEquals for Map sometimes does not use provided message (Krishnan Mahadevan) +Fixed: GITHUB-1632: throwing SkipException sets iTestResult status to Failure instead of Skip (Krishnan Mahadevan) +Fixed: GITHUB-2207: Allow dependency injector factory to be configured via args (dmikhievich) +Fixed: GITHUB-2193: Ignore local url for DTD security check (Li.Zhao & Julien Herr) +Fixed: GITHUB-1968: Upgrade to Gradle 6.0 + +7.1.0 +New : GITHUB-2199: Allow users to provide their own Injector for Dependency Injection (Krishnan Mahadevan) +Fixed: GITHUB-2180: Write scenario details for Retried tests (Devendra Raju K) +Fixed: GITHUB-2124: JUnit Report should contain the output of org.testng.Reporter (Krishnan Mahadevan) +Fixed: GITHUB-2171: Ability to embed attachments and make them available on TestNG XML report (Krishnan Mahadevan) +Fixed: GITHUB-2152: Multiple Test Groups Causing @BeforeMethod and @AfterMethod to be called multiple times for a single test (Krishnan Mahadevan) +Fixed: GITHUB-2172: Suite summary report table issue with EmailableReporter2.java (Devendra Raju K) +Fixed: GITHUB-2148: TestNG - configfailurepolicy=“continue” is not working for retried test (Krishnan Mahadevan) +Fixed: GITHUB-2163: Test is executed infinite number of times when the data provider returns a new object (Krishnan Mahadevan) +Fixed: GITHUB-2157: NullPointerException occurs when a Retried test has an exception in DataProvider (Krishnan Mahadevan) +Fixed: GITHUB-2150: Upgraded jQuery from 1.7.1 to 3.4.1 to resolve reported prototype pollution vulnerability +Fixed: GITHUB-2149: Handle NoClassDefFoundError when classloader fails to load a class +New: GITHUB-2111: Provide an interceptor for Data Provider (Krishnan Mahadevan) +Fixed: GITHUB-1709: @Ignore doesn't work when used on child class, and parent has multiple @Test methods (Krishnan Mahadevan) +New: GITHUB-2118: Default assertion message (Jiong Fu) +Fixed: GITHUB-2080: Wrong text for assertTrue (Jiong Fu) +Fixed: GITHUB-2078: Xml to Yaml does not capture the dependency definition in generated Yaml File (Jiong Fu) + +7.0.0 +Fixed: GITHUB-2137: Issue with Priority with too low values (Krishnan Mahadevan) +Fixed: GITHUB-2138: Factory calls are not intercepted by IAnnotationTransformer (Krishnan Mahadevan) +Fixed: GITHUB-2121: SoftAssert: allow custom message (dr29bart) +Fixed: GITHUB-2110: NPE is thrown when running with Thread affinity (Krishnan Mahadevan) +Fixed: GITHUB-2069: JUnit TestSuite not handled correctly in Reports (Krishnan Mahadevan) +Fixed: GITHUB-2075: Thread interrupt flag persists between test methods (Krishnan Mahadevan) +Fixed: GITHUB-2074: Thread Interrupted when using expectedException (Krishnan Mahadevan) +Fixed: GITHUB-2802: some methods' javadoc in ISuiteListener and ITestListener is misleading (Yehui Wang) +Fixed: GITHUB-2061: java.util.ConcurrentModificationException after registration of SuiteListener at the runtime (Krishnan Mahadevan) +Fixed: GITHUB-2055: It's not possible to register a new ITestListener at the runtime (Krishnan Mahadevan) +Fixed: GITHUB-1035: @BeforeClass not executed in parallel when parallel="instances" (Krishnan Mahadevan) +Fixed: GITHUB-2043: IConfigurationListener is not executed and IDataProviderListener is not added to the list of the listeners if a new listener is added at the runtime (Krishnan Mahadevan) +Fixed: GITHUB-1835: Configurable ThreadPoolExecutor (Krishnan Mahadevan) +Fixed: GITHUB-1691: Support reading data provider information from Class level @Test annotation (Krishnan Mahadevan) +Fixed: GITHUB-326: When group-by-instances is set to true the instances created by @Factory does not run in parallel (Krishnan Mahadevan) +Fixed: GITHUB-1930: Running testng-failed.xml correctly runs failed test in child class but incorrectly runs all tests in base class (Krishnan Mahadevan) +Fixed: GITHUB-1987: Retrieve the data provider method's reference from the test method (Krishnan Mahadevan) +Fixed: GITHUB-1976: Add a proper message when types are missing during annotation parsing (Krishnan Mahadevan) +Fixed: GITHUB-2000: ThreadPoolExecutor ConcurrentModificationException (Krishnan Mahadevan) +Fixed: GITHUB-2022: Suite.xml files make web request when suite uses DTD over HTTPS (Krishnan Mahadevan) +Fixed: GITHUB-2017: Don't use child injectors for Guice tests (Joe Barnett) +New: GITHUB-2003: Add to @Test interface fields IDs and issues (Krishnan Mahadevan) +Fixed: GITHUB-2009: Test Timeout not respected in parallel="methods" mode (Krishnan Mahadevan) +Fixed: GITHUB-2008: Preserve parameters in each class and not all of the test parameters +Fixed: GITHUB-1981: Fixes NPE in Assert.assertEquals when an array contains null (Maneesh MS) +New: Upgrade to gradle5 +New: Added a method in Assertion class to allow downstream TestNG consumers to override the error message (Ryan Laseter) +Fixed: GITHUB-165: @AfterGroups is not executed when group member fails or is skipped (Krishnan Mahadevan) +Fixed: GITHUB-118: @BeforeGroups only called if group is specified explicitly (Krishnan Mahadevan) +Fixed: GITHUB-182: Inherited test methods do not get expected group behavior (Krishnan Mahadevan) +Fixed: GITHUB-1988: Add Automatic-Module-Name to MANIFEST.MF (Krishnan Mahadevan) +Fixed: GITHUB-1985: Custom "IMethodSelector" implementation doesn't filter methods properly (Krishnan Mahadevan) +Fixed: GITHUB-993: Handle null test names from ITest implementation (Krishnan Mahadevan) +Fixed: GITHUB-1942: assertDeepEquals takes long time to evaluate size mismatch (Kumaran Bharathan) +Fixed: GITHUB-1967: IInvokedMethod ITestResult status set as -1 for test methods skipped due to config failure (Krishnan Mahadevan) +Fixed: GITHUB-1952: Provide a TestNGListener that can be invoked when a test fails due to a timeout (Krishnan Mahadevan) +Fixed: GITHUB-1953: TestNG throws a misleading error when @Factory method returns empty array. (Krishnan Mahadevan) +Fixed: GITHUB-1946: Retry analyzer does not work properly when coupled with a data provider (Krishnan Mahadevan) +Fixed: GITHUB-1924: Testclass instantiation fails when both no-arg constructor and factory method present (Krishnan Mahadevan) +Fixed: GITHUB-1935: Wrong text for assertEquals (Krishnan Mahadevan) +Fixed: GITHUB-1931: [NPE] Reporter org.testng.reporters.jq.Main failed (Krishnan Mahadevan, Oleg Shaburov) +New: Remove raw type warnings in ConversionUtils +Fixed: GITHUB-1480: Parallel=methods not working when tests have different priorities set (Micah Lapping-Carr) +Fixed: GITHUB-1041: Factory data-provider parameters not displayed in test-result (Krishnan Mahadevan) +Fixed: GITHUB-1901: The overall reported Test time for suite containing parallel tests should be max(tests_times) (Krishnan Mahadevan) +Fixed: GITHUB-1893: Streamline invocation of "init" method within TestResult to be private (Krishnan Mahadevan) +Fixed: GITHUB-1892: Configurable InvokedMethodListener (Krishnan Mahadevan) +Fixed: GITHUB-435: Apply at suite level to all tests (Siegmar Alber) +Fixed: GITHUB-1870: Fix Ambiguous behavior of IInvokedMethodListener by clarifying javadocs(Krishnan Mahadevan) +Fixed: GITHUB-1878: Provide visibility into the actual method (config/test) that caused a downstream test to be skipped (Krishnan Mahadevan) +Fixed: GITHUB-1883: Establish Listener invocation order as tests for documentation purposes (Krishnan Mahadevan) +Fixed: TestNG doesn't use the searchable loaders for JUnit tests (Igor Ignatev) +Fixed: GITHUB-1880: @AfterGroups(alwaysRun = true) is not called if there is an exception in @BeforeGroups (Krishnan Mahadevan) +Fixed: GITHUB-1874: Prevent circular dependency error when suite includes different methods from same class (Krishnan Mahadevan) +Fixed: GITHUB-1863: IMethodInterceptor will be invoked twice when listener implements both ITestListener and IMethodInterceptor via eclipse execution way.(Bin Wu) +Fixed: GITHUB-1865: testngXmlPathInJar- cannot be cleared when vm terminate(Yehui Wang) +Fixed: GITHUB-1850: Parser returns a wrong structure when parent suite has duplicate child suites (Chao Qin) +Fixed: GITHUB-1803: Added new methods for comparing float and double arrays with delta (Atul Agrawal) +Fixed: GITHUB-1661: Fixed Assert logic for two dimensional arrays (Atul Agrawal) +Fixed: GITHUB-1734: Added unit tests for NaN, Max, Min, Positive and Negative infinity (Atul Agrawal) +Fixed: GITHUB-1740: Bumped up artifact version of dependencies for java 8 support (Atul Agrawal) +Fixed: GITHUB-1834: Ensure group dependency defined via suite xml is considered (Krishnan Mahadevan) +New : Expose Graph Visualisation representation for users to build real-time debugging tools (Krishnan Mahadevan) +Fixed: GITHUB-574: String cannot be cast to Integer if log property is set in maven pom.xml (Krishnan Mahadevan) +Fixed: GITHUB-1402: TestNG reporting - Quickly identify Retry-d Tests (Krishnan Mahadevan) +Fixed: GITHUB-1697: Need ability to mark "failed but up for retry" tests differently from "skipped" tests (Krishnan Mahadevan) +Fixed: GITHUB-1810: Nullpointer exception when running TestNG tests from CMD (Krishnan Mahadevan) +Fixed: GITHUB-1590: Started configuration method has wrong status and end time during execution (Krishnan Mahadevan) +Fixed: GITHUB-298: Avoid Javascript errors in function name when suite name has special characters (Krishnan Mahadevan) +Fixed: GITHUB-1048: Fix validation errors in TestNG CSS to adhere to CSS level 3 + SVG (Krishnan Mahadevan) +Fixed: GITHUB-341: TestNG does not respect "-parallel classes" when running with a jar file (Krishnan Mahadevan) +Fixed: GITHUB-1790: IAnnotationTransformer transform method is not called for all test classes annotated with @Test (Krishnan Mahadevan) +Fixed: GITHUB-1787: Method level parameters are missing when using Yaml generation utilities (Krishnan Mahadevan) +New : TestNG now guarantees thread-affinity for methods that use either preserve-order (or) dependsOnMethods (Krishnan Mahadevan) +Fixed: GITHUB-1773: Parallel="classes" executes methods of test class in different threads (Krishnan Mahadevan) +Fixed: GITHUB-1185: DependsOnMethods made parallel class use several threads (Krishnan Mahadevan) +Fixed: GITHUB-1173: Parallel="classes" executes methods of test class in different threads (Krishnan Mahadevan) +Fixed: GITHUB-1066: Regression is in priority. It broke parallel mode (Krishnan Mahadevan) +Fixed: GITHUB-1050: Parallel classes runs methods from one class in different threads, interleaves two classes in one thread (Krishnan Mahadevan) +Fixed: GITHUB-89: parallel="classes" is not forcing test methods from the same testClass to be run in the same thread as it is suposed to (Krishnan Mahadevan) +Fixed: GITHUB-1719: successPercentage does not work correctly for tests with dataProvider (Krishnan Mahadevan) +Fixed: GITHUB-1241: Streamline Retry Analyzer usage when same test is run multiple times (Krishnan Mahadevan) +Fixed: GITHUB-1777: ITestListener.onTestStart() not called after fail or skip from @BeforeMethod (Krishnan Mahadevan) +Fixed: GITHUB-1778: SoftAssert#fail swallows actual root cause (Krishnan Mahadevan) +Fixed: GITHUB-1665: Failed test after rerun would impact next test case result (Yehui Wang) +Fixed: GITHUB-1770: Factory annotation does not work with Parameters annotation on method constructor (Krishnan Mahadevan) +Fixed: GITHUB-1767: Prevent NPE when XmlTest accessed without a proper XmlSuite (Krishnan Mahadevan) +Fixed: GITHUB-1766: Testng generates a lot of temp folders like testngXmlPathInJar- (Andrey) +Fixed: GITHUB-1759: core interfaces refactoring to support default methods (Sergey Korol) +Fixed: GITHUB-1753: TestResult for an SKIP test lose attributes contributed by @BeforeMethod's or @AfterMethod's (Krishnan Mahadevan) +Fixed: GITHUB-1756: ITest .getTestName() doesn't return the actual test name for skipped methods (Krishnan Mahadevan) +Fixed: GITHUB-1602: beforeInvocation method don't get called for skipped tests (Krishnan Mahadevan) +Fixed: GITHUB-549 and GITHUB-780: Introduce onlyForGroups attribute for @BeforeMethod and @AfterMethod (Sergei Tachenov) +Fixed: GITHUB-1745: Support native injection for @Factory methods (Krishnan Mahadevan) +Fixed: GITHUB-1746: Make InvokedMethodNameListener thread-safe, fixing occasional build failures (Sergei Tachenov) +Fixed: GITHUB-1538: Dependent methods don't get invoked when a failed test method is retried via a RetryAnalyser (Krishnan Mahadevan) +Fixed: GITHUB-426: firstTimeOnly ignored for threadPoolSize > 1 (Krishnan Mahadevan) +Fixed: GITHUB-466: JUnitReportReporter always converts the system time into GMT (Krishnan Mahadevan) +Fixed: GITHUB-1723: Standardize timezone reference in XML reports (Krishnan Mahadevan) +Fixed: GITHUB-489: Incorrect timezone info in element of testng-results.xml (Krishnan Mahadevan) +Fixed: GITHUB-1735: IExecutionListener.onStart() running twice when used as annotation (Krishnan Mahadevan) +New : Removed deprecated methods across TestNG (Krishnan Mahadevan) +Fixed: GITHUB-1726: Allow user-defined method interceptors to have the last say in method re-ordering (Krishnan Mahadevan) +Fixed: GITHUB-564: Support using @Optional on @Test method parameters to use null values (Krishnan Mahadevan) +New : Upgrade TestNG to start needing at-least JDK8 (Krishnan Mahadevan) +Fixed: GITHUB-1637: Remove dependency on a pre-created jar for running unit tests for JarFileUtils (Krishnan Mahadevan) +Fixed: GITHUB-1710: Inefficient DynamicGraph causes hang with only 100 tests. (Steve Prentice) +Fixed: GITHUB-1716: Potential NPE in jq.Main reporter (Krishnan Mahadevan) +Fixed: GITHUB-1709: @Ignore doesn't work when used on child class, and parent has @Test methods (Krishnan Mahadevan) +Fixed: GITHUB-1706: Retrying of methods fail when test method involves native injection (Krishnan Mahadevan) +New : Removed deprecated attributes from annotations (Julien Herr) +New : Support all JSR-223 compatible script engine +Fixed: GITHUB-1827: TestNG does not throw an error when a test class does not have a proper constructor (Julien Herr & Krishnan Mahadevan) +Fixed: Annotated default methods of indirectly implemented interfaces should still be called (Ilya Korobitsyn) +New : GITHUB-2105: Include assertEquals(Map, Map) into Assert class + +6.14.3 +Fixed: GITHUB-1077: TestNG cannot handle load (Aheiss) +Fixed: GITHUB-1081: group-by-instances with test dependencies causes instantiation of tests to exponentially slow (Aheiss) +Fixed: GITHUB-1700: Test ignored if @BeforeMethod in base class fails for another test class (Krishnan Mahadevan) +Fixed: GITHUB-1694: @BeforeGroups executed multiple times when tests run in parallel, once if not parallel (Krishnan Mahadevan) +Fixed: GITHUB-1688: @Ignore annotation on base class doesn't ignore tests in child classes (Krishnan Mahadevan) +Fixed: GITHUB-1687: NullPointerException is thrown when beanshell evaluates to null (Krishnan Mahadevan) + +6.14.2 +Fixed: GITHUB-1674: beanshell methodselector applied at suite level is ignored (Krishnan Mahadevan) +Fixed: GITHUB-1668: "Invalid Method Selector" exception triggered when using suite level beanshell methodselectors (Krishnan Mahadevan) +Fixed: GITHUB-1659: New line characters are removed from stack traces in testng-results.xml (Krishnan Mahadevan) +Fixed: GITHUB-1503: Consider adding a -n (no execute) option (Krishnan Mahadevan) +Fixed: GITHUB-1648: Depends on method is not respected on the sequential run on second test that extends same base testClass (Krishnan Mahadevan) +Fixed: GITHUB-1636: Parallel test run is not working in 6.13.1 (Krishnan Mahadevan) +New : GITHUB-1634: Make "-xmlpathinjar" support (Yehui Wang) +New : GITHUB-1634: Make "-testnames" find tests from Multi-level parent-child suites (Yehui Wang) +Fixed: GITHUB-1641: The time for test-method is 8 hours ahead of the the time for suite/class in testng-result xml file (Krishnan Mahadevan) +Fixed: GITHUB-1649: @Test annotated methods cannot inject java.lang.reflect.Method (Krishnan Mahadevan) +Fixed: GITHUB-1625: Null fields in parallel method tests (Krishnan Mahadevan) +Fixed: GITHUB-1605: Research the usefulness of the JVM argument "experimental" (Krishnan Mahadevan) +New : GITHUB-1631: data provider class name injection into Factory meta-data (Sergey Korol) + +6.13.1 +No functional changes. Released with newer version JCommander (1.72.0) + +6.13 +Fixed: GITHUB-1619: ConcurrentHashMap doesn't secure insertion order.(Yehui Wang) +Fixed: GITHUB-1616: Test cases with priority and dependsOnGroups dependencies, execution order is chaos. (Yehui Wang) +Fixed: GITHUB-1613: The constructor removed from TestRunner would stop Eclipse working.(Yehui Wang) +Fixed: GITHUB-1600: Updated in afterInvocation() testResult.status is not used in willRetry condition (Krishnan Mahadevan) +Fixed: GITHUB-1598: Injected types parameter and optional parameters cannot be used together. (Yehui Wang) +Fixed: GITHUB-1594: Cannot filter by "testnames" when suite xml is a suite of suites (Krishnan Mahadevan) +Fixed: GITHUB-1584: Can't run tests from IDEA (Krishnan Mahadevan) +Fixed: GITHUB-1589: TestNGAntTask should be consistently using the Ant Log API for writing log messages (Krishnan Mahadevan) +Fixed: GITHUB-1587: TestNG can not guarantee the ExecutionListener Instance as singleton(Yehui Wang) +Fixed: GITHUB-217: exception in DataProvider doesn't fail test run (Krishnan Mahadevan) +Fixed: GITHUB-987: Parameters threadCount and parallel doesn't work with maven (Krishnan Mahadevan) +Fixed: GITHUB-1472: Optimize DynamicGraph.getUnfinishedNodes (Krishnan Mahadevan & Nathan Reynolds) +Fixed: GITHUB-1566: Invalid XML characters in Params in testng-results.xml (Krishnan Mahadevan) +Fixed: GITHUB-1554: @Parameters and parameter injection not wroking when used on the same method (Krishnan Mahadevan) +Fixed: GITHUB-990: NullPointerExceptions after a superclass configuration method fails with configfailurepolicy="continue" (Krishnan Mahadevan) +Fixed: GITHUB-461 : Annotate annotations with @Documented (Krishnan Mahadevan) +Fixed: GITHUB-778 : XmlSuite toXml() does NOT add the suite time-out property (Krishnan Mahadevan) +Fixed: GITHUB-1029: Issue with getting XmlTest from test method (Krishnan Mahadevan) +Fixed: GITHUB-212: Enable support for providing a URI as suite file location (Krishnan Mahadevan) +Fixed: GITHUB-161 : Provide a way to customize SAXParserFactory implementation (Krishnan Mahadevan) +Fixed: GITHUB-1455: Configure XML output of XmlSuite (Krishnan Mahadevan) +Fixed: GITHUB-1465: Failure policy CONTINUE handling is broken for tests that are skipped in @BeforeMethod method (Krishnan Mahadevan) +Fixed: GITHUB-1533: Duplicate child suites get added when working with parent/child suite scenario (Krishnan Mahadevan) +Fixed: GITHUB-949: dependsOnMethods with alwaysRun = true and inheritance fails to find method (Krishnan Mahadevan) +Fixed: GITHUB-1519: Possibility to retry a test until it FAILED (Krishnan Mahadevan) +Fixed: GITHUB-980: TestNG run inherited method twice (Krishnan Mahadevan) +Fixed: GITHUB-1409: Regression: on expectedExceptionsMessageRegExp expected and actual messages are not printed (Krishnan Mahadevan) +Fixed: GITHUB-1517: TestNG exits with a zero when there are configuration failures (Krishnan Mahadevan) +New : GITHUB-1490: Add a listener for data provider interception (Krishnan Mahadevan) +Fixed: GITHUB-1456: Remove/Warn support of constructor with String param (Krishnan Mahadevan) +Fixed: GITHUB-1509: Improve error message when data provider returns a null value (Krishnan Mahadevan) +Fixed: GITHUB-1507: TestNG runs all methods when filtering via fails (Krishnan Mahadevan) +Fixed: GITHUB-1493: Wrong exception msg when timeout on test (Krishnan Mahadevan) +Fixed: GITHUB-328: Attempt to fix unnecessary execution of @Factory-ctors (@beverage & Julien Herr) +Fixed: GITHUB-1384: Huge performance issue between 6.5.2 and 6.11 (Denis Bazhenov) +New: Remove Serializable (Julien Herr) +Fixed: GITHUB-1496: If method contains "$", run only one method, all methods will be run (@JF-Rabbit & Julien Herr) +Fixed: GITHUB-1220: Recognize annotations on default methods in implemented interface +New: GITHUB-861: Add @Ignore annotation which disables all tests in a class or a package (Julien Herr) + +6.12 +Fixed: GITHUB-1484: Remove irrelevant "targets" for TestNG annotations (Krishnan Mahadevan) +Fixed: GITHUB-1405: Skip considering main() method when @Test used at class level (Krishnan Mahadevan) +Fixed: GITHUB-799: @Factory with dataProvider changes order of iterations (Krishnan Mahadevan & Julien Herr) +New: Enhance XML Reporter to be able to customize the file name (Krishnan Mahadevan) +Fixed: GITHUB-1417: Class param injection is not working with @BeforeClass (Krishnan Mahadevan) +Fixed: GITHUB-1440: Improve error message when wrong params on configuration methods (Krishnan Mahadevan) +Fixed: GITHUB-1433: Missing encoding for emailable reports (Shaburov Oleg) +Fixed: GITHUB-1430: Cannot load class from file XXX when using with ant and classfileset (Olivier Mourez) +Fixed: GITHUB-1394: Optimize ClassHelper.getAvailableMethods() to exclude Object class(Nathan Reynolds & Krishnan Mahadevan) +Fixed: GITHUB-1396: Order established by IMethodInterceptor not honored when running with parallel='instances' (Ryan Scott) +Fixed: GITHUB-1287: Parallel (methods) execution with dependsOn running in unexpected order (Kevyn Reinholt) +Fixed: GITHUB-1362: Ensure AfterGroups methods get executed when involving Method Interceptors (Krishnan Mahadevan) +Fixed: GITHUB-765: Skip invocation of bridged methods (Krishnan Mahadevan) +New: Enhance TestNGAntTask to be customizable (Denys Kurylenko) +New: Make EmailableReporter2 W3C Compliant[XHTML 1.1] (Chris Rankin) +Fixed: GITHUB-1336: (parallel=‘classes’) not working when coupled with priority (Krishnan Mahadevan) +Fixed: GITHUB-1365: Be able to override default XML parser (@ChristiKh & Julien Herr) +Fixed: GITHUB-1360: TestNG does not distinguish between methods of different priorities (Krishnan Mahadevan) +Fixed: GITHUB-1144: Add Class and Constructor as legal native dependency injection (Guillaume Juillot) +Fixed: GITHUB-1380: Circular dependencies may fail in parallel (Julien Herr) +Fixed: GITHUB-1400: TestNG, Multiple duplicate listener warnings on implementing multiple listener interfaces (@bipo1980 & Nick Tan) +Fixed: GITHUB-1426: @AfterMethod(alwaysRun = true) is not getting called if we have exception in @BeforeMethod (@dipak-pawar) +Fixed: GITHUB-128: Using Object[] and Method as parameters for a test in a certain order yields an IllegalArgumentException, citing a type mismatch (@leef590 & Julien Herr,Krishnan Mahadevan) +Fixed: GITHUB-1393: Revert commit 50d534a to allow fail a test from onTestStart method +Fixed: GITHUB-1461: TestNG not getting garbage collected (@kiru) + +6.11 +2017/02/27 +Fixed: GITHUB-1351: FailurePolicy failing with YAML (Steven Zaluk & Julien Herr) +New: The name of all TestNG threads follow "TestNG--" pattern (Julien Herr) +Fixed: GITHUB-1339: Alter ClassHelper to use Maps instead of Lists for extracting methods (Krishnan Mahadevan) +Fixed: GITHUB-1338: @BeforeGroups method is run on a wrong instance (Pavel Vetokhin & Julien Herr) +Fixed: GITHUB-1332: Make EmailableReport name configurable (Krishnan Mahadevan) +Fixed: GITHUB-1297: testng-failed.xml includes setup and tearDown (before and after annotations) of passed tests +(Krishnan Mahadevan) +Fixed: GITHUB-1319: ITestResult#getInstance() returns null in IConfigurationListener implementation (Krishnan Mahadevan) +New: Skipped methods are not supposed to be executed (Julien Herr) +Fixed: GITHUB-1197: Ability to dynamically set the status and exception of a test via ITestResult (Anthony Nguyen) +Fixed: GITHUB-1302: When 'parallel' is set to 'classes', ConcurrentModificationException can be thrown(Jianhua Li) +Fixed: GITHUB-772: Severe thread contention while running large test with parallel methods (Shaburov Oleg) +Fixed: GITHUB-1307: TestNGException when using an anonymous class in Factory (Mike Cowan) +Fixed: GITHUB-1298: ITestResult injection is failing in BeforeMethod method (Krishnan Mahadevan) +Fixed: GITHUB-1293: Beanshell based execution does not work any more (Krishnan Mahadevan) +Fixed: GITHUB-1262: Testcases out of order in XML file in junitreport folder when using testng (Krishnan Mahadevan) +Fixed: GITHUB-116 : BaseTestMethod does respect general contract of Comparable (Testo Nakada) +Fixed: GITHUB-1265: JUnit Reporter includes redundant ignored methods (Krishnan Mahadevan) +Fixed: GITHUB-1266: JUnit Reporter produces a wrong number of total test methods (Krishnan Mahadevan) +Fixed: GITHUB-1257: Group parameter not applying on included +Fixed: GITHUB-1284: Listeners on the child suites are not applied (Vimalraj Selvam) +New: GITHUB-1313: Add Java9 as test environment on Travis (Julien Herr) +Fixed: GITHUB-1296: Configuration listeners run multiple times (@mikimrozowski & Julien Herr) +Fixed: GITHUB-1300: Add deep assertions to assert on array values (array reference by default) (Jordan Zimmerman & Julien Herr) + +6.10 +2016/11/28 +Fixed: GITHUB-551: Failed configuration method always has 0 execution time (dr29bart) +Fixed: GITHUB-1250: Testng-failed.xml is getting test level parameters into suite level parameters (Krishnan Mahadevan) +Fixed: GITHUB-1046: Provide a mechanism to customize a test method name for reporting (Krishnan Mahadevan) +Fixed: GITHUB-1211: Include disabled/ ignored test methods in JUnit reports (Krishnan Mahadevan) +Fixed: GITHUB-1213: Include "ignored" test count in testng-results.xml (Krishnan Mahadevan) +Fixed: GITHUB-674: Enrich Test method skips due to configuration failures with throwable data (Krishnan Mahadevan) +Fixed: GITHUB-1240: Enrich the test results showing mechanism in Travis CI (Krishnan Mahadevan) +Fixed: GITHUB-1232: Prevent TestNG from adding duplicate instances of the same listener (Krishnan Mahadevan) +Fixed: GITHUB-1170: Fixing the test DataProviderTest.shouldNotThrowConcurrentModification (Krishnan Mahadevan) +Fixed: GITHUB-1231: Make IExecutionListener implementation be the last reporter call before JVM exit(Krishnan Mahadevan) +Fixed: GITHUB-1227: Prevent multiple instances of same Reporter from being injected into TestNG (Krishnan Mahadevan) +Fixed: GITHUB-1165: Better message to user when param injection is not good (Krishnan Mahadevan) +Fixed: GITHUB-1228: Control stacktrace levels in XmlReports via a JVM configuration (Krishnan Mahadevan) +Fixed: GITHUB-1203: Add flush to BufferedWriter; fixes incomplete XML reports (Nathan Bruning) +Fixed: GITHUB-1181: Fix MethodMatcherException: Data provider mismatch (Krishnan Mahadevan) +Fixed: GITHUB-1107: TestNG does not report/print/log throwables in data providers (Krishnan Mahadevan) +Fixed: GITHUB-1186: NullPointerException in JUnit reporter when used with Spock (Ian Robertson & Julien Herr) +Fixed: GITHUB-1180: NullPointerException on getting excluded/included groups (Krishnan Mahadevan) +Fixed: GITHUB-1064: Incorrect logging of parallel mode of a test +Fixed: GITHUB-1178: Halt execution when invalid testname is provided. (Krishnan Mahadevan) +Fixed: GITHUB-1139: DataProvider could support Object[] as a valid return type (Julien Herr) +Fixed: GITHUB-1182: Cannot run multiple @Factory-annotated methods in the same class (Ian Donovan & Julien Herr) +New: Hierarchy on order features (from less important to more important): groupByInstance, preserveOrder, priority, dependsOnGroups, dependsOnMethods +Fixed: GITHUB-1156: test execution dependant upon class name order and fails with TestNGException: No free nodes found (@t-weil & Julien Herr) +Fixed: GITHUB-1221: ConcurrentModificationException in TextReporter (Nick Tan) +Fixed: testng-eclipse/issues/298: IConfigurationListener was not loaded when running Test in Eclipse Plugin (@jmcgrail & Nick Tan) +New: Resources for test reports (header, images) now live in a sub directory org/testng to remove conflicts with other similar files in the class loader. + +6.9.13.6 +2016/09/23 + +6.9.13.5 (Bad release) +2016/09/23 + +6.9.13.4 (Bad release: Wrong internal version) +2016/09/22 + +6.9.13.3 (Ok, but too many direct dependencies) +2016/09/22 + +6.9.13.2 (Bad release: Wrong internal version) +2016/09/20 + +6.9.13.1 (Bad release: fat jar) +2016/09/19 + +6.9.13 (Bad release: JDK8 target only) +2016/09/16 + +New: code improvement in order to calculate key for dependency map. Dependency map will use methodQualifiedName as key provided by ITestNGMethod (Chirag Jayswal) +Fixed: GITHUB-1105: Test skipped instead failed if incorrect enum value (Liza Ivanova & Julien Herr) +Fixed: GITHUB-1111: XMLReporter crashes if a test parameter is exactly "]]>" (Łukasz Rekucki & Julien Herr) +Fixed: GITHUB-1108 @BeforeGroups called twice (Krishnan Mahadevan) +Fixed: GITHUB-1112 XmlInclude.getDescription returns null always (Krishnan Mahadevan) +Fixed: GITHUB-1090 Inconsistent handling "preserve-order" on suite/test level (Michal Domagala & Julien Herr) +Fixed: GITHUB-1085 Remove Guava dependency (Erik C. Thauvin & Julien Herr) +Fixed: GITHUB-1084 Using deprecated addListener methods should not register many times (Anna Kozlova & Julien Herr) +Fixed: GITHUB-447 Copy test parameters instead of storing a reference (Huagang Li & Julien Herr) +Fixed: GITHUB-174: NPE when parsing xml where has (Peter Stout & Julien Herr) +Fixed: GITHUB-918: NullPointerException on loading XmlSuites programmatically (@ispitkovskyi & Julien Herr) +Fixed: GITHUB-689: at level not applied to (@kunal546 & Julien Herr) +Fixed: GITHUB-740: More than one execution even when success with DataProvider and IRetryAnalyzer (Sergio Sacristán) +Fixed: GITHUB-877: Retries don't work correctly with DataProvider tests (Simonas Tvirbutas) +Fixed: GITHUB-1103: Add ...junit.ArrayAsserts.assertArrayEquals(boolean[], boolean[]) (Jonathan Halterman & Julien Herr) +Fixed: GITHUB-1122: Use the default value for preserve-order (Guillaume Guillot & Julien Herr) +Fixed: GITHUB-1022: Non static methods from external data providers are not working without @Guice (Sourav Chandra & Julien Herr) +Fixed: GITHUB-1130: IClassListener should only be instantiated once (Guillaume Guillot & Julien Herr) +Fixed: GITHUB-1131: IObjectFactory not being called for factory test instances with constructor-injected data provider (Scott McClure & Julien Herr) +New: GITHUB-1083: Factory supports indices (Julien Herr) +Fixed: GITHUB-148: 'Run Failed Test' doesn't run the proper tests after 2nd rerun when DataProvider is used (@akracheva & Julien Herr) + +6.9.12 +2016/06/21 + +Fixed: GITHUB-1017 Reporter.log is ignored in skipped test listener (Scott Kirkpatrick) +New: Minimal code changes to allow TestNG to work for OpenJDK tests, which should be run with only the java.base module present. +Fixed: GITHUB-1047 IClassListener didn't work (Julien Herr) +Fixed: GITHUB-1049 IClassListener was called many time (Julien Herr) +Fixed: GITHUB-1045 TestNG swallows exceptions silently if @ClassRule is used (Gili Tzabari & Julien Herr) +Fixed: GITHUB-506 TestNG cannot find JUnit method names from Spock (@blackduck-joe & Julien Herr) +Fixed: GITHUB-1009 Iterator DataProvider: indices not working (Mark Fulton & Julien Herr) +Fixed: GITHUB-1030 Parameterized test class crashes when data provider returns empty array (Christoffer Sawicki & Julien Herr) +New: TestNG displays a warning when tests are using an empty data provider + +6.9.11: +2016/03/26 + +Fixed: GITHUB-923 Refactored data provider's parameter values passing to a varargs or non-varargs method with @NoInjection handling (Nitin Verma) +New: GITHUB-933: Deprecate XmlTest#getTestParameters (Julien Herr) +Fixed: GITHUB-911: TestListener#onTestStart should be invoked if a suite configuration method fails (Harmin Parra Rueda & Julien Herr) +Fixed: GITHUB-793: Test suite with tests using dependency and priority has wrong behavior (Martin Hereu & Julien Herr) +Fixed: GITHUB-922: ITestResult doesn't contain name if a class has @Test (@dr29bart & Julien Herr) +Fixed: GITHUB-419: parallel mode was ignored with command line (@khospodarysko & Julien Herr) +New: GITHUB-932: Deprecate true/false parallel values. none is the new default value. (Julien Herr) +Fixed: GITHUB-960: testng-failed.xml gets generated even when there are no failures. (Krishnan Mahadevan) +Fixed: GITHUB-895: Changing status of test by setStatus of ITestResult (Raj Srivastava & Julien Herr) +Fixed: GITHUB-969: testng-failed.xml does not carry over the parameters of methods from origin suite xml(Ning Zhang) + +6.9.10: +2015/12/15 +Fixed: GITHUB-841: testName from @Test is now used and available from ITestResult#getName() and ITestResult#getTestName() (Julien Herr) +New: GITHUB-776: Add BeforeClass/AfterClass like on ITestListener (@vguna & Julien Herr) +Fixed: GITHUB-872: Enable end-users of TestNG to alter XmlSuite and XmlTest (Krishnan Mahadevan) +New: GITHUB-900: Support @Listeners in annotation transformer (Julien Herr) +New: GITHUB-898: Activate XML validation when possible (Julien Herr) +Fixed: GITHUB-889: XmlSuite in nested directories results in FIleNotFoundException (Virender Singh) +Fixed: GITHUB-811: Timeout is not working with parallel=tests (@michael-yxf & Julien Herr) +Fixed: GITHUB-839: Missing encoding meta data for report file (@banbq & Julien Herr) +Fixed: GITHUB-876: NullPointerException creating tests with parameters by a factory (@vixgeo & Julien Herr) +New: GITHUB-886: Add some checks on factory methods (Julien Herr) +New: GITHUB-874 / GITHUB-875 / GITHUB-882 / GITHUB-850 : Some code cleanup (Testo Nakada) +Fixed: GITHUB-866 / GITHUB-869 : Some attributes were not cloned when XmlSuite#clone was used (Virender Singh) +Fixed: GITHUB-842: Add TestResult#getTestName() support for @Test(testName) (Julien Herr) +Fixed: GITHUB-908: Fix Double.NaN assertion (Julien Herr) + +6.9.9: +2015/10/27 + +Fixed: GITHUB-829: Allowing suites to have duplicate names. You can now configure the same suite-file to run multiple times. (Eduardo Born) +Fixed: GITHUB-834: nested suites not supported by 'testnames' (Tibor Digana & Julien Herr) + +6.9.8: +2015/10/12 + +Replace 6.9.7 that was build with Java8 by error. + +6.9.7: +No official release + +Fixed: GITHUB-798: Set suitethreadpoolsize for Maven Surefire (Jan Dundáček) +Fixed: GITHUB-171: ISuiteListener methods called multiple times if multiple test elements (Daniel Qian & Julien Herr) +Fixed: GITHUB-169: IInvokedMethodListener methods executed several times before/after each test method (Mario Duarte & Julien Herr) +Fixed: GITHUB-154: MethodInterceptor will be called twice (Tim wu & Julien Herr) + +6.9.6: +2015/07/15 + +New: GITHUB-717: Add assertThrows and expectThrows (Ryan Schmitt) +Fixed: GITHUB-755: Fixed reporting of retried tests (Ryan Schmitt) +Fixed: GITHUB-773: Test should not be skipped when the exception is expected (@CandyLiuM & Julien Herr) + +6.9.5: +2015/07/12 + +Fixed: The ServiceLoaderTest on Windows (Mathieu Sebire) +Fixed: GITHUB-691: Fix classloading issue when using TestNG 6.9.4 and JMockit. (Mathieu Sebire) +Fixed: GITHUB-686: IAnnotationTransformer.transform is called for methods with testClass populated. (Łukasz Rekucki & Julien Herr) +Fixed: GITHUB-420: Before/AfterSuite methods may not run, when use inheritance, and enabled=false (Jakub Tokaj & Julien Herr) +Fixed: GITHUB-697: Make addFailedInvocationNumber thread-safe (Ryan Schmitt) +Fixed: GITHUB-698: Fix exit code reporting when IRetryAnalyzer is used (Ryan Schmitt) +Fixed: GITHUB-465: assertEquals(Collection, Collection) prints "null" when collections are different sizes (Michael Diamond) +New: GITHUB-710: AppVeyor is used for continuous integration on Windows (Julien Herr) +Fixed: GITHUB-599: IHookable ignored when a timeout is set (@ryanlevell & Julien Herr) +New: GITHUB-723: Allow users to add their own suite parser (Julien Herr) +Fixed: Allow '-testnames' option to work with '-xmlpathinjar' (@earthling) +Fixed: GITHUB-739: TestNG skips all test classes from suite when a @BeforeClass fails (Priyanshu Shekhar & Julien Herr) +Fixed: GITHUB-471: If @beforeMethod or @afterMethod fails then all children of the same base class will be skipped (Anton Panferov & Julien Herr) +Fixed: GITHUB-595: testng hang at switching test cases when running test cases with high thread count (vit0rg) + +Eclipse: + +Fixed: The 57% freeze bug (Patrick Hensley and @denyska) + +6.9.4: +2015/05/09 + +Added: GITHUB-631: Avoid the static limitation of external DataProvider. (Julien Herr) +Added: GITHUB-631: Allow to use Guice injection in DataProvider. (Julien Herr) +Added: Drop support of Java6 and previous. +Added: GITHUB-617: Allow injection of org.testng.ITestContext into the guice parent module. (Julien Herr) +Fixed: GITHUB-606: RetryAnalyzer loops endlessly. (Krishnan Mahadevan) +Fixed: GITHUB-618: Start TestNG from jar cause recursive run of tests from packages in Suite XML without ".*" on the end (Stas Gromov) +Fixed: GITHUB-639: Typo on preserveOrder (tabei-k & Julien Herr) +Fixed: GITHUB-632: Typo in doc (Pétur Ingi Egilsson & Julien Herr) +Fixed: GITHUB-629: InvokedMethod doesn't recognize configuration method (Jan Mewes & Julien Herr) +Fixed: GITHUB-615: XmlSuite, XmlTest: Time-out tag not preserved (jphollingworth & Julien Herr) +New: GITHUB-638: Travis CI is used for continuous integration (Julien Herr) +New: GITHUB-647: SonarQube is used to follow technical debt (Julien Herr) +Added: GITHUB-616: org.testng.internal.Version will be always up-to-date (Julien Herr) +Fixed: GITHUB-634: Review of the collections package (Julien Herr) +Fixed: GITHUB-624: Fixed failure/error inversion in JUnitReportReporter (Jerome Jacob) +Fixed: GITHUB-545: TestNG running JUnit tests but not reporting all results for parameterized tests (Jonathan Leitschuh & jdillet) +Fixed: GITHUB-610: CustomizedSuites must be saved using utf-8 encoding (Juha Heljoranta) +Fixed: GITHUB-602: NoClassDefFoundError in TestNGClassFinder. (aanno) +Fixed: GITHUB-529: Close InputStream and OuputStream after use (Andrew Gaul) +Fixed: GITHUB-532: Create the parent directory if it's missing (Ion Savin) +Fixed: GITHUB-541: Some OSGi manifest fixes (Evgeny Zhuravlev) +Fixed: GITHUB-657: Fix OSGI Import-Package to make jUnit4 dependency optional (Xavier Fournet) +Fixed: GITHUB-523: externally synchronize our use of the static SimpleDateFormat (mcosby) +Fixed: GITHUB-477: Typo in DTD attribute comment (Kamil Szymański) +Fixed: GITHUB-353: Typo in documentation (Jan Święcki) +Fixed: GITHUB-656: Upgrade to JCommander 1.48 (Ryan Schmitt) +Fixed: GITHUB-582: TestNG tests don't pass reliably on JDK8 (Ryan Schmitt) +New: GITHUB-645: TestNG project on Google Code redirect to GitHub +Fixed: GITHUB-310: Upgrade Guice (kronar & Julien Herr) +Fixed: GITHUB-87: @BeforeSuite/@BeforeTest methods happens to be disabled by mistake (romlom & Julien Herr) +Fixed: GITHUB-425: Wrong invocation order with lastTimeOnly (Rafael Winterhalter & Julien Herr) +Fixed: GITHUB-417: Expected Exceptions Message fails to match multi-line messages (Michael Diamond) +New: GITHUB-663: Add Guice Stage configuration for a suite (Clément Guillaume) + +6.8.21: +2015/02/02 + +6.8.15: +2015/01/14 + +Fixed: OutOfMemoryException while generating reports. +Fixed: GITHUB-566: Build does not fail when successPercentage for @Test is not met +Fixed: XmlTest#setGroupInstances was not being shown in toXml(). +Fixed: GITHUB-376: Some results can be lost (Konstantin Savin). +Fixed: Handle relative paths of Suite XML files properly (Nalin Makar) + +6.8.5: +2013/05/13 + +Fixed: the OutOfMemoryException in reports +Fixed: Surefire + listeners "Can't load class" problem + +=========================================================================== +6.8.1 +2013/03/30 + +Added: Descriptions in the HTML reports +Added: Various improvements to EmailableReporter (Abraham Lin) +Added: Allow injection of java.lang.reflect.Constructor and org.testng.ITestNGMethod into DataProvide (Vladislav Rassokhin) +Fixed: Assertions in the Assertions class were not failing properly. +Fixed: GITHUB-337: ConfigurationMethod#m_instance set to Boolean.FALSE due to incorrect constructor call in clone() + auto-boxing (davidely) +Fixed: Fix NPE for dependency methods/groups (Krishnan Mahadevan) +Fixed: preserve-order bug (found by VladSarrokhin). +Fixed: GITHUB-300: OutOfMemoryException from reporters when there are a lot of tests +Fixed: GITHUB-137: Main parameters with a default value should be overridden if a main parameter is specified +Fixed: GITHUB-107: Allow enum values without converting them to uppercase. +Fixed: @Guice with no modules specified is now supported +Fixed: Reporter.log() invoked from listeners were being discarded + +Eclipse: +Added: Predefined listeners (Tim Wu) +Fixed: Compare dialog + +=========================================================================== +6.7 +2012/07/15 + +Added: Big performance improvement when generating the reports (Frank Pavageau) +Added: allows you to specify group dependencies in testng.xml +Added: Blow up early if trying to include/exclude an unknown method +Added: can now be specified under (Storm Qi) +Added: GITHUB-243: Add Reporter Output per Test in XMLReporter (dunse) +Fixed: Better HTML escaping of the stack traces +Fixed: The failed assertions now use [] as delimiters instead of <> (better for the HTML reports) +Fixed: GITHUB-237: Wrong time format in XML reporter +Fixed: Threads were started sequentially instead of being interleaved +Fixed: dataProvider(parallel = true) was not killing its threads properly +Fixed: XmlSuite#toXml wasn't outputting the tag correctly +Fixed: testng-failed.xml was not carrying over the parameters from the original testng.xml +Fixed: BeforeClass failing in parent failed to skip methods in sub classes +Fixed: Better error message if is missing +Fixed: GITHUB-221: Honor excludeGroups on testng tests when run in mixed mode (criccio) +Fixed: dependsOnGroups = {regexp} wasn't working properly (Alistair Ward) +Fixed: GITHUB-205: white-space was spelled whitespace in testng.css (carlin-scott) + +Eclipse: +Fixed: Environment is not transferred when rerunning failed tests. +Fixed: Rerunning failed tests will preserve the environment of the original launch + +=========================================================================== + +6.5.1 +04/10/2012 + +Added: (and in as well) +Added: data-provider attribute to testng-results.xml +Added: Reporter display the results in the same order as test methods (Libor Zoubek) +Added: Support for running JUnit 4 tests (Lukas Jungmann) +Added: Ability to auto-detect JUnit tests ('-mixed' mode) (Lukas Jungmann) +Added: Support for ResourceCollections in an Ant tasks (requires Ant >= 1.7.0) (Lukas Jungmann) +Fixed: GITHUB-198: JUnitReportsReporter use commas in certain locales, which JUnitReports doesn't like +Fixed: GITHUB-173: Dependent methods executed out-of-order if method names match across classes (jjedMoriAnktah) +Fixed: ThreadLocal leak (aslakknutsen) +Fixed: In the HTML reports, only show the first 100 characters of the parameters +Fixed: SkippedException are considered as real exception with @Test(expectedExceptions) + +Eclipse: +Fixed: Java constants are properly resolved if they are used as group names (susanin) +Fixed: @Test(groups = Foo.CONSTANT) (susanin) +Fixed: Failed tests with allow-return-values="true" were not rerun +Added: (and in as well) + +=========================================================================== + +6.4: +02/15/2012 + +Added: @DataProvider(indices) to return specific indices of a data provider +Added: New HTML reports +Added: configfailurepolicy=continue with DataProviders (toddq) +Added: ITestResult#getTestContext (bpedman) +Fixed: invocationCount > 1 + timeOut wasn't timing out properly +Fixed: When running TestNG programmatically, child xml suites are not run (when added using setSuiteFIles()) (Gaurav Gupta) +Fixed: GITHUB-145: Excessive test method execution (githubCast) +Fixed: GITHUB-149: reversed arguments in failAssertEqualsNoOrder(). +Fixed: EmailableReporter: methods are now *really* sorted chronologically. + +Eclipse: + +Added: You can now add the testng.jar sources as a library (Nick Tan) +Added: Upgraded the plug-in to 3.4+ (Nick Tan) +Added: dependsOnGroups now fully supported +Fixed: @Parameters now works with both ("foo") and ({"foo"}) (davekerber) + +=========================================================================== +6.3.1 +10/22/2011 + +Added: New system property: dataproviderthreadcount (Bill Ross) +Fixed: Configuration methods were reported incorrectly in listeners. +Fixed: Was creating too many listeners (Jacek Pulut) +Fixed: IAnnotationTransformer2 beforeTest/afterTest booleans were not being set +Fixed: GITHUB-92: @BeforeTest method in a super class will be called multiple time when alwayRun = true (Bubuntux) +Fixed: GITHUB-111: @AfterClass on base classes run once too many (lrivera) +Fixed: GITHUB-107: Displaying 0 tests run if a listener modifies the parameters of the suite + +=========================================================================== +6.3 +10/17/2011 + +Added: "description" attribute on , made available on ITestNGMethod#getDescription +Added: RemoteTestNG waits infinitely for a connection (Aleksey Kabanov) +Fixed: A method that's both a test and a factory would not invoke its data provider +Fixed: @AfterClass was not called if one of the methods was not enabled (Aleksey Kabanov) +Fixed: Groovy access bug +Fixed: The XML parser doesn't recognize parallel="instances" +Fixed: NPE when using inner classes +Fixed: GITHUB-90: @AfterClass not being run when the class contains included and not included methods +Fixed: @AfterClass not being run in some subclassing situations + +Eclipse: + +Fixed: Verbose levels specified in suites not respected +Fixed: Variable substitution in VM arguments is not working properly (svenhoff) + +=========================================================================== + +6.2 +08/18/2011 + +Added: xmlpathinjar to the TestNG ant task +Added: TestNG can now invoke package protected constructors +Added: Injectors created by the @Guice annotation are now shared at the level +Added: IConfigurationListener is now a public listener, along with a new one: IConfigurationListener2 +Added: When a method fails, only dependents of the same instance will be skipped +Added: parallel=instances for factory instance parallel runs +Added: @Factory(enabled) +Fixed: JUnitReports reports now report the cumulated time @{Before,After}Method+@Test for each test method +Fixed: JUnitReports reports have the name of the instead of that of the first class +Fixed: Using preserve-order with a factory that creates instances of a different class causes NPE +Fixed: GITHUB-74: Bad ordering of test methods when using a @Factory constructor with dataProvider +Fixed: Changing the test result from success to failure in a listener would still count the test as a success +Fixed: ServiceLoader wasn't resolving correctly if no service loader classloader was specified +Fixed: Better ordering with mixed priorities and dependencies +Fixed: Improved detection of graph cycles in parallel runs +Fixed: @BeforeTest was invoked multiple times if a factory is used +Fixed: GITHUB-57: Allow usage of package protected constructor of test classes +Fixed: Injecting both Object[] and Method in @BeforeMethod didn't always work +Fixed: testng-results.xml now lists the results chronologically +Fixed: @Listeners specified on a base class will only be run once per listener class (dbriones) +Fixed: -groups and -excludegroups were no longer overriding testng.xml + +Eclipse: + +Added: Each data provider method now has a separate node entry in the TestNG view +Fixed: Nodes in error would sometimes remain green +Fixed: The TestNG context menu no longer appears where it shouldn't + +=========================================================================== + +6.1.1 +7/5/2011 + +Fixed: https://github.com/cbeust/testng/issues/56 testng-results.xml was reporting the instance name instead of the method name +Fixed: NPE when using preserve-order and factories. +Fixed: Depending on a skipped method would not cause a method to be skipped + +=========================================================================== + +6.1 +6/30/2011 + +Possible backward incompatible changes: + +- Don't mutate the value returned by XmlTest#getIncludedGroups and XmlTest#getExcludedGroups. +Instead, use addIncludedGroup/addExcludedGroup. +- Failing methods that have dependees will only cause skips in the same instance. Different +test instances will not be affected + +Added: Support for ServiceLoader for ITestNGListener +Added: @Factory(dataProvider / dataProviderClass) on constructors +Added: assertNotEquals() to Assert +Added: assertArrayEquals() to AssertJUnit +Added: Nested classes are now automatically added for consideration for inclusion +Added: will cause this attribute to be propagated to all tags +Added: can now be specified under a +Added: Tycho compatibility (Aleksander Pohl) +Added: New and flag: group-by-instances +Added: -xmlpathinjar to specify the path of testng.xml inside a test jar file +Added: ISuite#getAllMethods, to retrieve all the methods at the start of a suite. +Added: Output ITestResult attributes in xml report (nguillaumin) +Fixed: Thread safety problem in MethodInvocationHelper (Baron Roberts) +Fixed: Group dependencies were not being skipped properly. +Fixed: Dependency failures only impact the same instance +Fixed: Static classes could cause a StackOverFlowError +Fixed: IConfigurationListener was not extending ITestNGListener +Fixed: IConfigurationListener#onConfigurationFailure was never called +Fixed: TESTNG-476: tags are now run in the order found in testng.xml +Fixed: Now showing failed/skipped error messages on the console for verbose >= 2 +Fixed: ITestResult#getEndMillis() return 0 +Fixed: TESTNG-410: Clearer error message +Fixed: TESTNG-475: @DataProvider doesn't support varargs +Fixed: Performance problems in EmailableReporter +Fixed: TESTNG-472: Better output for assertNull() +Fixed: ConcurrentModificationException when using parallel data providers. +Fixed: TESTNG-282: Problem when including+excluding packages (addicted) +Fixed: TESTNG-471: assertEquals(Map, Map) fails if a map is a subset of the other +Fixed: JUnitReporter generates an tag for successful expectedExceptions tests +Fixed: ISSUE-47: Don't allow two s with same name within same suite (Nalin Makar) +Fixed: If a listener implements both ISuiteListener and IInvokedMethodListener, only one of them gets invoked + +Eclipse: + +Added: New quick fix "Add static import org.testng.AssertJUnit.assertXXX" +Added: New workspace wide setting: excluded stack traces, to provide shorter stack traces in the view +Added: New "Clear results" icon in the tool bar +Added: When the search filter is modified, don't update the tree live if it is too big +Added: Two new @Test refactorings (pull to class level, push to method level) +Added: JUnit conversion: @Ignore +Added: JUnit conversion: assertArrayEquals() +Added: JUnit conversion: @RunWith(Parameterized.class) +Added: Support for Hamcrest failed assertions in the compare dialog +Added: JUnit conversion: suite() methods can now either be removed, commented out or left untouched +Fixed: JUnit conversion: super.setUp()/tearDown() were being removed when extending a class other than TestCase +Fixed: "Run as" menu not appearing for methods that take a generic parameter. +Fixed: The tree was incorrect if the same class is used in different tags +Fixed: When creating a new Run/Debug configuration, "Launch.label" was displayed +Fixed: TESTNG-459: TestNG menu should not always be present in context menu (Mykola Nikishov) +Fixed: Performance problems in the plug-in +Fixed: Workspace-wide XML template files are not being honored. +Fixed: @BeforeClass/@AfterClass from JUnit4 are not being properly converted +Fixed: Conversions generate @Test() instead of @Test + +=========================================================================== + +6.0 +2011/03/16 + +Added: @Guice(moduleFactory) and IModuleFactory +Added: @Guice(module) +Added: timeOut for configuration methods +Added: -randomizesuites (Nalin Makar) +Added: IConfigurable +Fixed: @Test(priority) was not being honored in parallel mode +Fixed: @Test(timeOut) was causing threadPoolSize to be ignored +Fixed: TESTNG-468: Listeners defined in suite XML file are ignored (Michael Benz) +Fixed: TESTNG-465: Guice modules are bound individually to an injector meaning that multiple modules can't be effectively used (Danny Thomas) +Fixed: Method selectors from suites were not properly initialized (toddq) +Fixed: Throw an error when two data providers have the same name +Fixed: Better handling of classes that don't have any TestNG annotations +Fixed: XmlTest#toXml wasn't displaying the thread-count attribute +Fixed: TESTNG-438: Regression in 5.14.1: JUnit Test Execution no longer working +Fixed: TESTNG-436: Deep Map comparison for assertEquals() (Nikolay Metchev) +Fixed: Skipped tests were not always counted. +Fixed: test listeners that throw were not reporting correctly (ansgarkonermann) +Fixed: wasn't working. +Fixed: In parallel "methods" mode, method interceptors that remove methods would cause a lock up +Fixed: EmailableReporter now sorts methods chronologically +Fixed: TESTNG-411: Throw exception on mismatch of parameter values (via DP and/or Inject) and test parameters +Fixed: IDEA-59073: exceptions that don't match don't have stack trace printed in console (Anna Kozlova) +Fixed: IDEA's plug-in was not honoring ITest (fixed in TestResultMessage) +Fixed: Methods depending on a group they belong were skipped instead of throwing a cycle exception +Fixed: TESTNG-401: ClassCastException when using a listener from Maven +Fixed: TESTNG-186: Rename IWorkerApadter to IWorkerAdapter (Tomas Pollak) +Fixed: TESTNG-415: Assert.assertEquals() for sets and maps fails with 'null' as arguments +Fixed: typo -testRunFactory +Fixed: NPE while printing results for an empty suite (Nalin Makar) +Fixed: Invoke IInvokedMethodListener.afterInvocation after fixing results for tests expecting exceptions (Nalin Makar) +Fixed: TESTNG-441: NPE in SuiteHTMLReporter#generateMethodsChronologically caused by a race condition (Slawomir Ginter) + +Eclipse: +Added: Convert to YAML +Added: New global preference: JVM args +Added: Eclipse can now monitor a test-output/ directory and update the view when a new result is created +Added: Right clicking on a class/package/project now offers a menu "TestNG/Convert to TestNG" +Added: Excluded methods are now listed in the Summary tab +Added: "Description" column in the excluded methods table +Added: Dialog box when the plug-in can't contact RemoteTestNG +Added: Double clicking on an excluded method in the Summary tab will take you to its definition +Added: If you select a package before invoking the "New TestNG class" wizard, the source and package text boxes will be auto-filled +Added: When an item is selected in a tab, the same item will be selected when switching tabs +Added: A new "Summary" tab that allows the user to see a summary of the tests, sort them by time, name, etc... +Added: It's now possible "Run/Debug As" with a right click from pretty much any element that makes sense in the tree. +Added: JUnit conversion: correctly replaces assertNull and assertNotNull +Added: JUnit conversion: removes super.setUp() and super.tearDown() +Added: JUnit conversion: removes @Override +Added: JUnit conversion: replaces @Test(timeout) with @Test(timeOut) (5.14.2.4) +Added: JUnit conversion: replaces @Test(expected) with @Test(expectedExceptions) (5.14.2.4) +Added: JUnit conversion: replaces fail() with AssertJUnit.fail() (5.14.2.2) +Added: JUnit conversion: replaces Assert with AssertJUnit (5.14.2.1) +Added: The progress bar is now orange if the suite contained skipped tests and no failures +Added: Skipped test and suite icons are now orange (previously: blue) +Added: New method shortcuts: "Alt+Shift+X N", "Alt+Shift+D N" (Sven Johansson) +Added: "Create TestNG class" context menu +Added: When generating a new class, handle overridden methods by generating mangled test method names +Fixed: Green nodes could override red parent nodes back to green +Fixed: Was trying to load the classes found in the XML template file +Fixed: Stack traces of skipped tests were not showing in the Exception view +Fixed: XML files should be run in place and not copied. +Fixed: NPE when you select a passed test and click on the Compare Result icon (Mohamed Mansour) +Fixed: When the run is over, the plug-in will no longer force the focus back to the Console view +Fixed: The counter in the progress bar sometimes went over the total number of test methods (5.14.2.9) +Fixed: org.eclipse.ui.internal.ErrorViewPart cannot be cast to org.testng.eclipse.ui.TestRunnerViewPart (5.14.2.9) +Fixed: Workspace preferences now offer the "XML template" option as well as the project specific preferences (Asiel Brumfield) +Fixed: TESTNG-418: Only last suite-file in testng.xml run by Eclipse plugin + +Documentation: +Added: Section on Selenium (Felipe Knorr Kuhn) +Added: Link to an article on TestNG, Mockito and Emma in the Misc section + +=========================================================================== + +5.14.7 +2011/01/27 + +Release for IDEA + +=========================================================================== + +5.14.1 +2010/10/2 + +Fixed: TESTNG-401: ClassCastException when using a listener from Maven + +=========================================================================== + +5.14 +2010/08/28 + +Added: test suites can now be run in parallel with -suitethreadpoolsize +Fixed: @Listeners now aggregate through base classes +Fixed: ISuite was no longer serializable +Fixed: Injection was sometimes not working properly when used with @Parameters +Fixed: TESTNG-400: afterMethod was called after onTestFailure() +Fixed: "excludedgroups" was not working on the ant task because of a typo +Fixed: ant task error if is used with no classes (welex91) +Fixed: TESTNG-404: threaded tests fail due to use of non-threadsafe collections (Marcus Better) +Fixed: preserve-order was not preserving class order with dependent methods +Fixed: RetryAnalyzer wasn't working properly with factories +Fixed: The ant task was no longer supporting ',' for testclass + +Eclipse: + +Fixed: The plug-in wasn't running Groovy tests correctly (Andrew Eisenberg) +Fixed: TESTNG-402 [Eclipse Plug-In] NPE occurred when I run twice a custom "Run configuration" on a group + +=========================================================================== + +5.13.1 +2010/08/05 + +Added: -methods +Added: -configfailurepolicy (Todd Quessenberry) +Added: -methodselectors (Todd Quessenberry) +Added: @NoInjection +Added: +Added: -testnames (command line) and testnames (ant) +Added: New ant task tag: propertyset (Todd Wells) +Added: ITestNGListenerFactory +Added: Passing command line properties via the ant task and doc update (Todd Wells) +Added: Hierarchical XmlSuites (Nalin Makar) +Added: Reporter#clear() +Fixed: NullPointerException when a suite produces no results (Cefn Hoile) +Fixed: Identical configuration methods were not always invoked in the correct order in superclasses (Nalin Makar) +Fixed: @DataProvider(parallel = true) was passing incorrect parameters with injection +Fixed: Replaced @Test(sequential) with @Test(singleThreaded) +Fixed: If inherited configuration methods had defined deps, they could be invoked in incorrect order (Nalin Makar) +Fixed: Initialize all Suite/Test runners at beginning to catch configuration issues right at start (Nalin Makar) +Fixed: Issue7: Issue86 Incorrect dates reported for configuration methods +Fixed: Issue24: OOM errors in SuiteHTMLReporter (Nalin Makar) +Fixed: Time outs specified in XML were not honored for +Fixed: and time outs were hardcoded, they now honor their time-out attribute +Fixed: TestNG was hanging if no test methods were found +Fixed: onTestSuccess() was called after @AfterMethod instead of after the test method (test: test.listener.ListenerTest) +Fixed: XML test results contained skipfailedinvocationCounts instead of skipfailedinvocationcounts +Fixed: Issue4 assertEquals for primitive arrays, Issue34 assertNull javadoc updated +Fixed: Issue78 NPE with non-public class. Now throws TestNG exception +Fixed: NPE with @Optional null parameters (Yves Dessertine) +Fixed: TESTNG-387 TestNG not rerunning test method with the right data set from Data Provider (Francois Reynaud) +Fixed: Show correct number of pass/failed numbers for tests using @DataProvider +Fixed: Return correct method status and exception (if any) in InvokedMethodListener.afterInvocation() +Fixed: Trivial fixes: TESTNG-241 (log message at Info), Issue2 (throw SAXException and not NPE for invalid testng xml) +Fixed: Configuration methods couldn't depend on an abstract method (Nalin Makar) +Fixed: TestNG#setTestClasses was not resetting m_suites +Fixed: Exceptions thrown by IInvokedMethodListeners were not caught (Nalin Makar) +Fixed: @Listeners now works on base classes as well +Fixed: Test priorities were not working properly in non-parallel mode +Fixed: @Listeners wasn't working properly with ITestListener + +Eclipse + +Fixed: TESTNG-395 New wizard was creating classes called "NewTest" +Fixed: TESTNG-397 Class level @Test was preventing groups from showing up in the launch configuration + +Doc +Updated Maven documentation (Brett Porter) + +=========================================================================== + +5.12.1 +2010/03/29 + +Maven update + +=========================================================================== +5.12 + +Removed: Javadoc annotation support + +Added: @Listeners +Added: IAttributes#getAttributeNames and IAttributes#removeAttribute +Added: testng-results.xml now includes test duration in the tag (Cosmin Marginean) +Added: Injection now works for data providers +Added: TestNG#setObjectFactory(IObjectFactory) +Added: Priorities: @Test(priority = -1) +Added: New attribute invocation-numbers in +Added: testng-failed.xml only contains the data provider invocations that failed +Added: IInvokedMethodListener2 to have access to ITestContext in listeners (Karthik Krishnan) +Fixed: @Before methods run from factories were not properly interleaved +Fixed: The TextReporter reports skipped tests as PASSED (Ankur Agrawal) + +Eclipse: + +Added: New file wizard: can now create a class with annotations, including @DataProvider +Added: You can now select multiple XML suites to be run in the launch dialog +Fixed: @Test(groups = ) was taking name of the constant instead of its value. +Fixed: https://jira.codehaus.org/browse/GRECLIPSE-476 NPE with Groovy Tests (Andrew Eisenberg) +Fixed: The custom XML file is now created in the temp directory instead of inside the project +Fixed: In the launch dialog, now display an error if trying to pick groups when no project is selected +Fixed: Was not setting the parallel attribute correctly on the temporary XML file + +=========================================================================== +5.11 +2009/12/08 + +Added: Dependent methods can now run in their own thread +Added: dataProviderThreadCount can be set from the command line and from ant (Adrian Grealish) +Added: ITestAnnotation#setDataProvider +Added: Assert#assertEquals() methods for Sets and Maps +Fixed: The text reporter was no longer reporting stack traces for verbose >= 2 +Fixed: dataProviderClass was not respecting inheritance (like most attributes still) +Fixed: @BeforeSuite/@AfterSuite would run multiple times when used in a @Factory +Fixed: packages=".*" wasn't working properly (sandopolus) +Fixed: TestResult#getName now returns the description instead of the method +Fixed: @DataProvider and dependent methods were not skipping correctly (Francois Reynaud) +Fixed: TESTNG-347 suite with parallel="tests" and test with parallel="classes" doesn't work correctly (Rob Allen) +Fixed: TESTNG-67: @Configuration/@Factory methods in base class being ignored +Fixed: Inner test classes were not excluded properly (Carsten Gubernator) +Fixed: threadPoolSize without invocationCount was causing reporters not to be invoked +Fixed: A @Factory throwing an exception did not cause any error +Fixed: was not working properly in the ant task (Ed Randall) +Fixed: @BeforeClass methods were not running in parallel (Aidan Short) +Fixed: Test class with @ObjectFactory doesn't get instantiated via the factory +Fixed: Allow IObjectFactory to load from non-standard classloader (for PowerMock support) + +Eclipse 5.11.0.19: +Added: New "parallel" preference setting (Windows / Preferences / TestNG) +Fixed: IIinvokedMethodListeners were not invoked + +=========================================================================== +5.10 + +Added: The output in the testng-results.xml is now sorted by the starting timestamp (Daniel Rudman) +Added: Better display of the test name and method description in the default and Emailable report +Added: If both -testjar and an XML file are provided on the command line, the latter will be used +Added: @Before and @After methods can be injected with the current XmlTest +Added: Methods that time out now display the stack trace showing where the time out occurred +Added: ITestResult#getAttribute and ITestResult#setAttribute +Added: @After methods can now be injected with an ITestResult +Added: @BeforeMethod and @AfterMethod methods can now be injected an ITestResult +Added: ISuite#getAttribute and ISuite#setAttribute to share data within a suite +Added: @Test(expectedExceptionsMessageRegExp = ".*foo.*") +Added: @DataProvider(parallel=true) +Fixed: @Test(dataProvider) was not working at the class level +Fixed: Display a better error message if the wrong exception is thrown with an expectedExceptions +Fixed: Classes created by factories were not run in the order they were created +Fixed: Dependent methods are now run closer to methods within their class +Fixed: xmlFileSet in ant was not working correctly (Sean Shou) +Fixed: Various oversights in the DTD (Will McQueen) +Fixed: XMLUtils was not escaping XML attribute values +Fixed: TESTNG-317: Sequence order mis-calculation: testing using suite in sequence for classes and same method names creates non-sequential order +Fixed: Test names (classes that implement org.testng.ITest) now appear more prominently in the HTML reports +Fixed: expectedExceptions=RuntimeException.class was not failing when no exception was throw +Fixed: TESTNG-291: Exceptions thrown by Iterable DataProviders are not caught, no failed test reported (Roberto Tyley) +Fixed: TESTNG-301: Need to include parameters in testNG report for test created by @Factory +Fixed: testng-failed.xml now includes skipped tests +Fixed: TestNG couldn't find Groovy files (Haw-Bin) + +Eclipse + +Fixed: TESTNG-313: Provide extension point to contribute test and report listeners (Erik Putrycz) +Fixed: Quick fixes no longer introduce deprecated annotations (Greg Turnquist) + +=========================================================================== +5.9 +2009/04/09 + +Added: New ant task boolean flag: delegateCommandSystemProperties (Justin) +Added: skipfailedinvocations under in testng-1.0.dtd (Gael Marziou / Stevo Slavic) +Added: -testrunfactory on the command line and in the ant task (Vitalyi Pamajonkov) +Added: TESTNG-298: parallel="classes", which allows entire classes to be run in the same thread +Added: @BeforeMethod can now declare Object[] as a parameter, which will be filled by the parameters of the test method +Added: IAnnotationTransformer2 +Added: @Test(invocationTimeOut), which lets you set a time out for the total time taken by invocationCount +Added: IInvokedMethodListener +Added: -testjar supports jar file with no testng.xml file +Fixed: IInvokedMethodListener wasn't properly recognized from the command line (Leonardo Rafaeli) +Fixed: TESTNG-309 Illegal default value for attribute in DTD file +Fixed: TESTNG-192: JUnit XML output includes wrong tests (Aleksandar Borojevic) +Fixed: Set a generated suite to default to non-parallel (Mark Derricutt) +Fixed: -testJar command line parsing bug +Fixed: testng-failed.xml didn't include the listeners +Fixed: annotation transformers were not run when specified in testng.xml +Fixed: TESTNG-192: JUnit XML output includes wrong tests (Borojevic) +Fixed: @Parameters was not working correctly on @BeforeMethods with @DataProvider used on @Test methods +Fixed: testng-failed.xml was sometimes incorrectly generated (Borojevic) +Fixed: TestNG-228: Assert.assertEqualsNoOrder +Fixed: TestNG-229: Assert.assertEquals does not behave properly when arguments are sets +Fixed: TESTNG-36: assertEquals(Collection actual, Collection expected, String message) may have bug +Fixed: TESTNG-296: Malformed jar URLs breaking -testJar +Fixed: TESTNG-297: TestNG seemingly never stops running while building failed test suite (Gregg Yost) +Fixed: TESTNG-285: @Test(sequential=true) works incorrectly for classes with inheritance +Fixed: TESTNG-254: XMLSuite toXML() ignores listeners +Fixed: TESTNG-276: Thread safety problem in Reporter class +Fixed: TESTNG-277: Make Reporter.getCurrentTestResult() public +Fixed: Potential NPE in XmlTest#getVerbose (Ryan Morgan) +Fixed: EmailableReporter only displayed the first group for each test method +Fixed: time-outs were not working in and +Fixed: @BeforeTest failing in a base class would not cause subsequent test methods to be skipped +Fixed: TESTNG-195: @AfterMethod has no way of knowing if the current test failed +Fixed: TESTNG-249: Overridden test methods were shadowing each other if specified with +Fixed: DataProviders from @Factory-created tests were all invoked from the same instance +Fixed: enabled was not working on configuration methods +Fixed: IIinvokedMethodListener was not correctly added in TestNG +Fixed: NPE in XmlSuite#toXml +Fixed: TESTNG-231: NullPointerException thrown converting a suite to XML (Mark) + +Doc: +Added: 5.20: IInvokedMethodListener +Added: -testjar + +=========================================================================== +5.8 + +Fixed: TestNG-220: Ignore class definition/loader issues when scanning classpath for implicit classes +Fixed: TestNG-224: Fix for relative suite filenames in XML file +Added: TestNG-213: @Optional on a method parameter to allow optional @Parameters +Fixed: TestNG-214: SkipException and TimeBombSkipException should accept nested exceptions +Fixed: TestNG-211: new Parser(inputStream) doesn't work +Added: Methods that form a cycle are now shown when the cycle is detected +Added: Support for in testng.xml +Added: IMethodInterceptor +Added: @TestInstance on a data provider method parameter +Fixed: @AfterMethod(lastTimeOnly) didn't work properly with data providers +Added: antlib.xml to allow autodiscovery of Ant task definition +Fixed: name attribute on is required + +Doc: +Added: Method Interceptor +Added: @Optional +Added: Doc for IMethodInterceptor (5.16) and TestNG listeners (5.18) +Added: 5.19: Dependency injection + +=========================================================================== +5.7 + +Added: @BeforeMethod(firstTimeOnly) and @AfterMethod(lastTimeOnly) +Added: @BeforeMethods can now take a Method and ITestContext parameters (like @DataProvider) +Fixed: logging about abstract classes moved to level 5 +Added: if @Parameter is missing from testng.xml then it is read from the System properties +Fixed: Don't run a @DataProvider method as a test when a class-level @Test is present +Added: Attribute @Test#skipFailedInvocations +Fixed: TESTNG-169 Error message: is depending on nonexistent method null ("null" is uninformative) +Fixed: -listener takes comma-separated classes +Added: RetryAnalyzer (experimental) (Jeremie) + +=========================================================================== +5.6 +2007/06/14 + +Added: SkipException/TimeBombedSkipException for manual skipping +Added: can now be disabled at xml level using +Added: Suite files that only contain other suites do not get reported +Fixed: @BeforeClass methods would incorrectly report cyclic graphs +Added: get/setAttribute to ITestContext +Added: plugging in factory objects to handle the actual instantiation of tests +Added: dataProvider to @Factory +Added: ISuite now gives access to the current XmlSuite +Fixed: TESTNG-139 dependsOnMethods gets confused when dependency is "protected" +Fixed: TESTNG-141 junit attribute set to false in testng-failed.xml when it should be true +Fixed: TESTNG-142 Exceptions in DataProvider are not reported as failed test +Added: Improved behavior for @Before/@AfterClass when using @Factory +(https://forums.opensymphony.com/thread.jspa?threadID=6594&messageID=122294#122294) +Added: Support for concurrent execution for invocationCount=1 threadPoolSize>1 and @DataProvider +(https://forums.opensymphony.com/thread.jspa?threadID=64738&tstart=0) +Added: New TestNG specific XML report, generated by default in 'xml' subdirectory of test-output +Added: support in strprotocol for passing the ITest.getTestName() information +Fixed: TESTNG-152 If DataProvider is not found, the exception message should tell exactly what happened + +Eclipse plug-in + +Fixed: Bug that made group launch configurations unusable +Fixed: The plugin doesn't create the correct launch configuration for @Factory +Fixed: Method based launchers cannot be editted +Fixed: Plugin hangs while executing test with dataprovider that sends \n, \r messages +Added: display ITest.getTestName() + +IDEA plug-in + +Fixed: IDEA 7.0 compatibility +Fixed: occasional 'illegal arguments exception' +Fixed: TESTNG-151 Final passing test result is not properly hidden +Added: Auto-completion for dependsOnMethods +Added: Highlighting of invalid groups/methods in dependsOn* + +=========================================================================== +5.5 +2007/01/25 + +Fixed: @BeforeGroup methods were run twice when in a base class +Fixed: @BeforeGroup methods were run twice with a @Test at class level +Fixed: parallel="tests" didn't work as advertised +Added: Support for thread-count at test level +Added: Method selectors receive a Context and can stop the chain with setStopped() +Fixed: XmlMethodSelector was always run first regardless of its priority +Added: @BeforeGroups/@AfterGroups can live in classes without @Test methods +Added: DataProvider can now take an ITestContext parameter +Fixed: Wasn't parsing correctly +Fixed: Annotation Transformers now work on class-level annotations +Fixed: Some class-level @Test attributes were not always honored +Added: Clean separation between @Test invocation events and @Configuration invocation events + (see also TESTNG-111) +Added: Test instances created by @Factory now run in multiple threads in parallel mode +Fixed: @Before/@AfterGroups invocation order +Fixed: TESTNG-27: Parameters are not used on level anymore +Fixed: TESTNG-107 don't create an output directory if "outputDirectory" is null +Fixed: TESTNG-127 UseDefaultListeners in Ant Task does not work +Fixed: TESTNG-119 Running TestNG runner with invalid '-sourcedir' on JDK14 JavaDoc annotated test classes won't fail. +Fixed: TESTNG-113 Dependent methods within the same static inner class are not found +Fixed: TESTNG-125 TestNG failed for test classes under *.java*.* pakages + +Eclipse plug-in +Fixed: issue with launch configuration +Fixed: TESTNG-124: setting location of testng reports output + +=========================================================================== +5.4 + +Fixed: Ant task issue with paths containing spaces +Added: for @BeforeGroups and @AfterGroups specifying the groups() attribute will auto-include the method + into those groups by default (previously you had to also provide the value() attribute). +Added: the load @Tests (invocationCount + threadPoolSize) are triggered simultaneous +Fixed: reports are correctly displaying the thread info +Added: @DataProvider name defaults to method name +Added: support for remote protocol to pass parameter information +Fixed: TextReporter logs information about the parameters of the test methods +Fixed: concurrency issue in JUnitXMLReporter +Fixed: output of JUnitXMLReporter must be CDATA +Fixed: XML unsupported annotations/parallel attribute values are reported + +Eclipse plug-in +Fixed: groups with multi-attribute javadoc annotations +Fixed: consistent behavior for dependsOnMethods +Fixed: consistent behavior for tests with dependsOnGroups (a warning is emitted) +Fixed: consistent merge of configuration arguments when an existing launch configuration exists +=========================================================================== +5.3 +2006/10/30 + +Fixed: use a single instance of bsh.Interpreter +Added: @Before/@AfterMethod can declare a java.lang.reflect.Method parameter to be informed about the @Test method +Fixed: super classes must not be listed in testng-failures.xml +Fixed: parallel attribute must not appear if empty or null in testng-failures.xml +Fixed: parsing for javadoc annotations is done on request only +Added: improved multiple suite summary page report +Added: -target option deprecated in favor of -annotations javadoc|jdk +Fixed: filesets in the ant task didn't work if the paths have spaces in them +Fixed: Before/After Suite were behaving wrong in parallel execution +Added: A generic/extensible RemoteTestNG was added to the core +Fixed: Before/AfterGroup-s were behaving wrong when using invocationCount, dataProvider and threadPoolSize +Fixed: improved support for running different annotation type tests in the same suite +Fixed: testng-failed.xml was generated even if there were no failures/skipps +Fixed: -usedefaultlisteners was wrongly passed to JVM instead of TestNG options +Added: Attribute dataProviderClass for @Test and @testng.test +Fixed: Forgot to account for cases where both invocationCount and DataProviders are present +Fixed: AfterGroups were invoked out of order with invocationCount and DataProviders +Fixed: Reporter.getOutput() returned an empty array if a timeOut was specified +Added: testng.xml now supports +Added: ant task can receive several listeners +Fixed: TESTNG-109 Skipped tests with expected exceptions are reported as failures +Added: ant task can now select the parallel mode for running tests +Fixed: ant task correctly deals with empty groups and excludedgroups parameters +Added: ant task can override default suite and test names +Added: comand line support for setting parallel mode, suite and test names + +Eclipse plug-in +Added: Support for configuring per project usedefaultlisteners +Added: Contextual drop-down menu on failures tab of the TestNG view to enable running/debugging method failure only +Added: Suppport for configuring per project TestNG jar usage (project provided one or plugin provided one) + +=========================================================================== +5.2 + +Added: "-usedefaultlisteners true/false" to command line and ant +Added: EmailableReporter (from Paul Mendelson) +Added: parallel can now be "methods" or "tests". Boolean version deprecated +Added: TestNGAntTask now uses the @ syntax to invoke TestNG +Added: Command line understands @ syntax +Added: JUnitConverter uses the new syntax +Added: -groups to JUnitConverter +Fixed: Throw proper exception when a DataProvider declares parameters +Added: completely revamped JUnit support (should run all kind of JUnit tests) +Fixed: TESTNG-40 (Bug in testng-failed.xml generation) +Fixed: TESTNG-106 (Failed "@BeforeSuite" method just skipps the last test in xml-file) +Fixed: Success on 0 tests (https://forums.opensymphony.com/thread.jspa?threadID=41213) + +Eclipse plug-in +Added: TESTNG-105 Automaticaly define TESTNG_HOME classpath variable + +=========================================================================== +5.1 +2006/08/18 + +Added: @Test(sequential = true) +Fixed: TESTNG-102 (Incorrect ordering of @BeforeMethod calls when a dependency is specified) +Fixed: TESTNG-101 (HTML output contains nested

tags and a missing tag) +Added: support for specifying test-only classpath (https://forums.opensymphony.com/thread.jspa?messageID=78048&tstart=0) +Fixed: TESTNG-93 (method selectors filtering @BeforeMethod) +Fixed: TESTNG-81 (Assert.assertFalse() displays wrong expected, actual value) +Fixed: TESTNG-59 (multiple method selectors usage results in no tests run) +Fixed: TESTNG-56 (invocation of @Before/AfterClass methods in parallel/sequential scenarios) +Fixed: TESTNG-40 (failures suite does not contain @Before/After Suite/Test methods) +Fixed: TESTNG-37 (allow passing null parameter value from testng.xml) +Fixed: TESTNG-7 (display classname when hovering method) + + +Eclipse plug-in + +Added: run contextual test classes with parameters from suite definition files +Added: TESTNG-100 (Show HTML reports after running tests) +Added: TESTNG-97 (Double click top stack to raise comparison) +Added: TESTNG-84 (plug-in UI for suite option should support absolute path) +Added: TESTNG-20 (copy stack trace) + +Fixed: TESTNG-72 (display groups with non-array values) +Fixed: TESTNG-64 (Eclipse plug-in applies added groups to all launch configurations) +Fixed: TESTNG-28 (Cannot select groups from dependent eclipse projects) +Fixed: TESTNG-25 (do not display fully qualified method name when running contextual test class) + +Improved behavior: + TESTNG-98 (temporary files have guaranteed fixed names) + TESTNG-95 (Assertion failed comparison trims trailing ">") + TESTNG-70 (TestNG prevents eclipse from opening an older CVS version of a java class) + display of test hierarchy information (TESTNG-29) + +=========================================================================== + +5.0.1 + +Eclipse plug-in + +Added: Output directory for the tests +Added: Can now specify listener classes + +=========================================================================== +5.0.1 + +Fixed: reports generated by SuiteHTMLReporter do not work with JDK1.4 + +=========================================================================== + +5.0 +2009/04/01 + +Added: Ant task: support for JVM, workingDir, timeout +Added: Stack traces can be interactively shown in the HTML reports +Added: Link to testng.xml in the reports +Added: New structure for reports, suites go in their individual directory +Added: @Test(suiteName) and @Test(testName) +Added: The stack traces in reports do not include TestNG frames (system property testng.exception) + (see: https://groups.google.com/group/testng-dev/browse_thread/thread/9f4d46ade10b0fda) +Fixed: Exit with error when no methods are run + (see: https://groups.google.com/group/testng-dev/browse_thread/thread/3c26e8a5658f22ac) +Added: List of methods in alphabetical order +Fixed: Class-scoped annotations were not recognized when inherited +Added: Deprecated @Configuration and introduced @BeforeSuite/Test/Class/TestMethod +Added: Deprecated @ExpectedExceptions and moved it into @Test +Added: expectedExceptions to @Test, deprecated @ExpectedExceptions +Added: New annotations: @BeforeSuite, @BeforeTest, etc... +Fixed: Was returning an exit code of 0 if a cyclic graph was detected +Added: Interface org.testng.ITest so that tests can declare a name +Fixed: The Text reporter was reporting the square of the actual number of methods +Fixed: Bug reported by Eran about dependencies with an afterClass method +Added: IHookCallBack now receives the ITestResult in its run() method +Added: Name of suite for command line can be set with -Dtestng.suite.name=xxx +Fixed: TestNGAntTask was hardcoding m_haltOnFSP to true +Fixed: Passing a null parameter caused an NPE in the reports +Added: "listener" to the ant task (and documentation) +Added: if patch-testng-sourcedir.properties is found in the classpath + with a property "sourcedir" containing a ; separated list of + directories, this list will override -sourcedir. + +=========================================================================== + + +4.7 + +Added: Maven 2 plug-in +Fixed: Message formattings in TestNG assertion utility class +Fixed: @Factory methods were counted as @Test as well + https://jira.opensymphony.com/browse/TESTNG-51 +Fixed: All DataProvider parameters were shown in the HTML report +Fixed: Bug in testng-failed.xml generation +Fixed: bug when using a jar file to load the test classes +Added: alwaysRun for before @Configuration methods + https://jira.opensymphony.com/browse/TESTNG-35 +Fixed: groupless @Configurations were not invoked if a method depends on a group + https://jira.opensymphony.com/browse/TESTNG-45 +Added: beforeGroups/afterGroups to @Configuration + +Eclipse plugin: + +Added: last contextual launch is available in Eclipse launcher lists +Fixed: 3.2M5 integration (removed dependency on non-existing class) +Fixed: testng-failures.xml generation + +=========================================================================== + +4.6 +2006/27/02 + +Added: Documentation contains the new reports +Added: TestNG.setUseDefaultListeners(boolean) +Added: Descriptions now appear in TextReporter (verbose>=2) and the HTML reports +Added: description attribute to @Test and @Configuration +Added: combined Reporter output in the reports +Added: methods not run in the reports +Added: org.testng.IReporter +Added: threadPoolSize to @Test +Added: Reports now show relative timings (start at 0) +Added: Reports now show different colors depending on the methods' classes +Added: Reports now show all parameters used to invoke the test method +Added: org.testng.Reporter +Added: DataProviders can accept a Method as first parameter +Fixed: Extraneous implicit inclusion of a method + +Eclipse plugin: + +Added: Run/Debug as TestNG test from the editor contextual menu +Fixed: TESTNG-24: 'Run as testng test' does not appear of the Test annotation does not have a group +Fixed: TESTNG-18: Eclipse plugin ignores Factory annotation +Fixed: TESTNG-21: Show differences when double clicking assertion exceptions +Added: UI allows setting orientation (even more space) + https://forums.opensymphony.com/thread.jspa?threadID=17225&messageID=33805#33805 + +=========================================================================== + +4.5 +2007/07/02 + +Core: + +Fixed: Methods were not implicitly included, only groups +Fixed: Bug with failed parent @Configuration don't skip child @Configuration/@Test invocations +Fixed: Bug with overridding @Configuration methods (both parent and child were run) +Fixed: Bug when overriding beforeClass methods in base class (cyclic graph) +Added: Support for JAAS (see org.testng.IHookable) +Fixed: Problem with nested classes inside +Fixed: ArrayIndexOutOfBoundsException for jMock +Added: dependsOnMethods can contain methods from another class +Fixed: JUnitConverter required -restore, not any more (option is now a no-op) +Fixed: JUnit mode wasn't invoking setName() on test classes +Added: Regular expressions for classes in +Added: Distributed TestNG +Fixed: Command line parameters and testng.xml are now cumulative +Fixed: Reports now work for multiple suites +Fixed: Was ignoring abstract classes even if they have non-abstract instances +Fixed: If setUp() failed, methods were not skipped +Fixed: Was not clearly indicating when beforeSuite fails +Added: @Configuration.inheritGroups +Fixed: inconsistency between testng.xml and objects regarding method selectors + +Eclipse plug-in: + +New look for the progress view. + +=========================================================================== + +4.4 + +Core: + +Fixed: testng-failures.xml was not excluding methods from base classes +Fixed: Bug in suites of suites for JUnit mode + +=========================================================================== + +4.3 + +Core: + +Fixed: testng-failures.xml was not excluding methods from base classes +Fixed: Bug in suites of suites for JUnit mode +Added: Excluded groups on command line and ant task +Fixed: When including a group, implicitly include groups depended upon +Fixed: When depending on several groups, wasn't skipped if one of them failed +Fixed: Failures weren't reported accurately in the JUnitReports report +Fixed: Wasn't throwing an exception if depending on a non-existing group + +=========================================================================== + +4.2 + +Core: + +Fixed: wasn't excluding methods in base classes +Added: alwaysRun for tests (soft dependencies) +Fixed: Class-level enabled=false were not honored +Fixed: Bug with multiple dataproviders on same class +Fixed: Bug with dataprovider defined in the parent class +Fixed: Bug with dataprovider defined in a subclass +Fixed: Bug with dataprovider defined in an abstract class +Fixed: testng-failures generation was excluding the methods even if a failed test depended on it + +=========================================================================== + +4.1 + +Core: + +Added: @DataProviders can return Iterable +Fixed: Superclass test methods were not called in the presence of a class @Test +Added: Reporter class to log messages in the HTML reports + +=========================================================================== + +4.0 +2005/11/10 + +Core: + +Fixed: suite methods now invoked only once in a hierarchy +Added: @DataProvider and @testng.data-provider +Fixed: Interleave order now respected for before/afterClass methods +Added: Can now invoke java -jar testng-2.6.jar <...> +Added: Support for BeanShell +Added: Method Selectors (IMethodSelector) +Fixed: In the absence of dependencies, @Configuration methods respect inheritance +Fixed: Bug in multithreaded dependencies on methods +Fixed: dependsOnGroups wasn't working on regular expressions +Fixed: Bug in when directories contain spaces in their names +Fixed: Introduced a JDK5 dependency in the JDK1.4 build (getEnclosingClass()) +Fixed: Output directory in ant task was not honored if it didn't exist +Fixed: Problem with timeout according to + https://forums.opensymphony.com/thread.jspa?threadID=6707 + +Eclipse plug-in: + +Fixed: Wasn't handling linked directories correctly +Fixed: Bug in QuickFix implementation +Added: Quick Fix for JUnit conversion (Annotations and JavaDoc) +Fixed: Methods Run as TestNG test +Added: Package level Run as TestNG test +Fixed: Resources from the linked directories are using a wrong path when + passed to command line TestNG + +IDEA plug-in: + +Added: Support for JDK 1.4 (both projects and IDEA itself) +Fixed: Classes that contained only configuration were ignored + +=========================================================================== + +2.5 +2005/08/08 + +Added: ITestListener.onTestStart(ITestResult) +Added: Support for +Added: Resource files for easier ant taskdefs +Fixed: @Configuration methods were not invoked with individual test methods +Fixed: Bug with ExpectedExceptions +Fixed: Didn't support nested factory classes +Fixed: NPE if -target is omitted with JDK 1.4 +Fixed: @Configuration failures in a class would cause other classes to fail +Added: alwaysRun +Fixed: beforeTestClass/afterTestClass were broken for a pathological case +Added: @Configuration(alwaysRun) +Added: JUnitConverter task +Fixed: < and > characters in reports were not escaped + +Eclipse plug-in: + +Fixed: Class dialog wasn't showing @Factory classes + +IDEA plug-in: + +First release! + +Documentation: + +Added: Brand new look!!! +Added: Section on testng.xml +Fixed: Numbering of sections + +=========================================================================== + +2.4 +2005/07/05 + +Changed: New package: testng.org +Fixed: Bug with @ExpectedException occuring the parallel mode +Fixed: Bug with parameters and beforeTest +Added: IInstanceInfo support +Fixed: methods were not excluded when included by groups +Fixed: testng-failures.xml is now including also the beforeSuite/afterSuite methods +Fixed: generating the testng-failures.xml is now working as expected +Fixed: Factories call all the tests even if some of them fail along the way +Fixed: Better JUnit support (wasn't creating individual instances) +Fixed: dependsOnGroups didn't work across different classes +Added: command line (and Ant) -groups option +Added: @Parameters (and made parameters attribute deprecated) +Added: Parameters for constructors +Fixed: Better interleaving of before/afterTestMethods +Fixed: Ant task +Fixed: TestNGException thrown when TestNG conditions are not fulfilled + +Documentation: +- New assert classes +- New ways to launch +- JUnitConverter documentation +- new beforeSuite/afterSuite + +=========================================================================== + +2.3 +2005/04/12 + +Fixed: Spaces are now legal in JavaDoc comments +Added: documentation for @Factory +Fixed: factories were called multiple times +Added: beforeSuite and afterSuite +Fixed: inheritance and scope now working properly for annotations +Fixed: dependsOnMethods wasn't working for 1.4 +Added: Better stack traces +Added: Better syntax for included/excluded methods +Fixed: Better verbose support +Fixed: Various fixes for the Eclipse plug-in +Added: Can specify a class name on the command line +Fixed: Default package bug in JUnitConverter +Added: Regression tests for JUnitConverter +Added: -quiet option to JUnitConverter + +=========================================================================== + +2.2 + +Fixed: Wasn't handling several testng.xml files correctly +Fixed: Renamed -src to -sourcedir +Fixed: Complains if no sourcedir is specified in 1.4 +Added: In 1.4, don't require annotations="javadoc" +Fixed: If setUp fails, complain and mark test methods as skips +Fixed: Dependent methods weren't working for 1.4 + +=========================================================================== + +2.1 +2005/02/12 + +Added: Parser can accept an InputStream for testng.xml +Fixed: expected-exceptions now fails if test passes +Fixed: reports now use the suite name in HTML +Added: invocationCount and successPercentage +Added: dependsOnMethods +Added: timeOut works in non-parallel mode + +=========================================================================== + +2.0 +2004/12/06 + +Added: port on JDK 1.4 + +=========================================================================== + +1.3 + +Added: new view: classes (still experimental) +Added: timeout on methods +Added: thread-count +Added: TestNG is now multithread, see "parallel" in + +=========================================================================== + +1.2 + +Added: JUnitConverter +Fixed: Bug with afterClasses (test: AfterClassCalledAtTheEnd) + +=========================================================================== + +1.1 + +Added: new links for methods and groups in the HTML report +Added: +Added: to + +=========================================================================== + +1.0 +2004/04/28 +https://beust.com/weblog/2004/04/28/ + +Fixed: Updated to the new DTD +Fixed: Suite table of contents displays failures first +Fixed: Bug in afterTestClass +Added: Validating testng.xml +Added: Scoped parameters +Added: testng.xml +Removed: Property quiet +Changed: Verbose is now an integer +Added: Dependent methods + +=========================================================================== + +0.9 + +Added: Groups of groups +Added: Groups for Configuration methods +Added: Parameters + +=========================================================================== + +0.2 + +Fixed: Merged TestMethod and TestClass into Test +Added: HTML report +Added: Regexps for groups +Fixed: Inheritance of methods +Fixed: ExpectedException is now called ExpectedExceptions diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..20e4bd8 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 9ebb840..1168afb 100644 --- a/README.md +++ b/README.md @@ -1 +1,49 @@ -# template-repository \ No newline at end of file +[![Maven Central](https://img.shields.io/maven-central/v/org.testng/testng.svg)](https://maven-badges.herokuapp.com/maven-central/org.testng/testng) +[![License](https://img.shields.io/github/license/cbeust/testng.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) +[![Sonarqube tech debt](https://img.shields.io/sonar/https/sonarqube.com/org.testng:testng/tech_debt.svg?label=Sonarqube%20tech%20debt)](https://sonarqube.com/dashboard/index?id=org.testng:testng) +[![Sonarqube Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=org.testng%3Atestng&metric=alert_status)](https://sonarcloud.io/dashboard?id=org.testng%3Atestng) + +Documentation available at [TestNG's main web site](https://testng.org). + +### Release Notes + +* [7.4.0](https://groups.google.com/g/testng-users/c/dwSJ04qeu8k) +* [7.3.0](https://groups.google.com/forum/#!topic/testng-users/a81uaZvtEZI) +* [7.1.0](https://groups.google.com/forum/#!topic/testng-users/84bYPJ1rjno) +* [7.0.0](https://groups.google.com/forum/#!topic/testng-users/HKujuefBhXA) + +### Need help? +Before opening a new issue, did you ask your question on +* [Google group](https://groups.google.com/group/testng-users) +* [StackOverflow](https://stackoverflow.com/questions/tagged/testng) + +If you posted on both sites, please provide the link to the other question to avoid duplicating the answer. + +### Are you sure it is a TestNG bug? +Before posting the issue, try to reproduce the issue in [a shell window](https://testng.org/doc/documentation-main.html#running-testng). + +If the problem does not exist with the shell, first check if the issue exists on the bugtracker of the runner, and open an issue there first: +* Eclipse: https://github.com/cbeust/testng-eclipse/issues +* IntelliJ: [https://youtrack.jetbrains.com/issues](https://youtrack.jetbrains.com/issues?q=Subsystem:%20%7BJava.%20Tests.%20TestNG%7D) +* Maven: https://issues.apache.org/jira/browse/SUREFIRE +* Gradle: https://issues.gradle.org/projects/GRADLE + +### Which version are you using? +Always make sure your issue is happening on the latest TestNG version. Bug reports occurring on older versions will not be looked at quickly. + +### Have you considered sending a pull request instead of filing an issue? +The best way to report a bug is to provide the TestNG team with a full test case reproducing the issue. +Maybe you can write a runnable test case (check the `src/test/` folder for examples) and propose it in a pull request +Don't worry if the CI fails because it is the expected behavior. +This pull request will be a perfect start to find the fix :) + +### How to create a pull request? +Refer our [Contributing](.github/CONTRIBUTING.md) section for detailed set of steps. + +### We encourage pull requests that: + + * Add new features to TestNG (or) + * Fix bugs in TestNG + + If your pull request involves fixing SonarQube issues then we would suggest that you please discuss this with the + [TestNG-dev](https://groups.google.com/forum/#!forum/testng-dev) before you spend time working on it. diff --git a/bin/junitconverter.bat b/bin/junitconverter.bat new file mode 100644 index 0000000..0abcd0b --- /dev/null +++ b/bin/junitconverter.bat @@ -0,0 +1,6 @@ +set ROOT=c:\java\TestNG +set JAR=%ROOT%\testng-4.5-jdk15.jar;%ROOT%\test\build + rem set JAR=%ROOT%\testng-4.5-jdk14.jar;%ROOT%\test-14\build + +java -ea -classpath %ROOT%\3rdparty\junit.jar;%JAVA_HOME%\lib\tools.jar;%JAR%;%CLASSPATH% org.testng.JUnitConverter -restore -overwrite -annotation -srcdir src + diff --git a/bin/master.bat b/bin/master.bat new file mode 100644 index 0000000..a4040d8 --- /dev/null +++ b/bin/master.bat @@ -0,0 +1 @@ +testng -hostfile test\hosts.properties -d test\test-output %1 %2 %3 %4 %5 test\testng.xml \ No newline at end of file diff --git a/bin/run-tests.sh b/bin/run-tests.sh new file mode 100755 index 0000000..32792e1 --- /dev/null +++ b/bin/run-tests.sh @@ -0,0 +1,14 @@ +ROOT=~/java/testng +VERSION=5.0 +JAR14=$ROOT/testng-$VERSION-jdk14.jar +JAR15=$ROOT/testng-$VERSION-jdk15.jar + +java -ea -classpath test/build:$ROOT/3rdparty/junit.jar:$JAVA_HOME/lib/tools.jar:$JAR15:$CLASSPATH org.testng.TestNG test/testng.xml + +java -ea -classpath test/v4/build:$ROOT/3rdparty/junit.jar:$JAVA_HOME/lib/tools.jar:$JAR15:$CLASSPATH org.testng.TestNG test/v4/testng.xml + +java -ea -classpath test-14/build:$ROOT/3rdparty/junit.jar:$JAVA_HOME/lib/tools.jar:$JAR14:$CLASSPATH org.testng.TestNG -sourcedir test-14/src test-14/testng.xml + +java -ea -classpath test-14/v4/build:$ROOT/3rdparty/junit.jar:$JAVA_HOME/lib/tools.jar:$JAR14:$CLASSPATH org.testng.TestNG -sourcedir test-14/v4/src test-14/v4/testng.xml + + diff --git a/bin/slave.bat b/bin/slave.bat new file mode 100644 index 0000000..24d2fb1 --- /dev/null +++ b/bin/slave.bat @@ -0,0 +1 @@ +testng -d client-output -slave %1 %2 %3 diff --git a/bin/testng.bat b/bin/testng.bat new file mode 100644 index 0000000..8ea1edf --- /dev/null +++ b/bin/testng.bat @@ -0,0 +1,5 @@ +set ROOT=c:\java\TestNG +set JAR=%ROOT%\testng-5.2beta-jdk15.jar;%ROOT%\test\build + rem set JAR=%ROOT%\testng-4.5-jdk14.jar;%ROOT%\test-14\build + +java -ea -classpath %ROOT%\3rdparty\junit.jar;%JAVA_HOME%\lib\tools.jar;%JAR%;%CLASSPATH% org.testng.TestNG %1 %2 %3 %4 %5 %6 %7 %8 %9 diff --git a/bin/testng.sh b/bin/testng.sh new file mode 100755 index 0000000..af8bbb9 --- /dev/null +++ b/bin/testng.sh @@ -0,0 +1,6 @@ +ROOT=~/java/testng +VERSION=5.0 +JAR14=$ROOT/testng-$VERSION-jdk14.jar +JAR15=$ROOT/testng-$VERSION-jdk15.jar + +java -ea -classpath $ROOT/test/build:$ROOT/3rdparty/junit.jar:$JAVA_HOME/lib/tools.jar:$JAR15:$CLASSPATH org.testng.TestNG $* diff --git a/build-logic/basics/build.gradle.kts b/build-logic/basics/build.gradle.kts new file mode 100644 index 0000000..bc0172f --- /dev/null +++ b/build-logic/basics/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + `kotlin-dsl` +} diff --git a/build-logic/basics/src/main/kotlin/testng.repositories.gradle.kts b/build-logic/basics/src/main/kotlin/testng.repositories.gradle.kts new file mode 100644 index 0000000..1e4717f --- /dev/null +++ b/build-logic/basics/src/main/kotlin/testng.repositories.gradle.kts @@ -0,0 +1,10 @@ +// Can't use settings plugin: https://github.com/gradle/gradle/issues/17295 +// dependencyResolutionManagement { +// repositories { +// mavenCentral() +// } +//} + +repositories { + mavenCentral() +} diff --git a/build-logic/basics/src/main/kotlin/testng.reproducible-builds.gradle.kts b/build-logic/basics/src/main/kotlin/testng.reproducible-builds.gradle.kts new file mode 100644 index 0000000..cab1514 --- /dev/null +++ b/build-logic/basics/src/main/kotlin/testng.reproducible-builds.gradle.kts @@ -0,0 +1,7 @@ +tasks.withType().configureEach { + // Ensure builds are reproducible + isPreserveFileTimestamps = false + isReproducibleFileOrder = true + dirMode = "775".toInt(8) + fileMode = "664".toInt(8) +} diff --git a/build-logic/basics/src/main/kotlin/testng.versioning.gradle.kts b/build-logic/basics/src/main/kotlin/testng.versioning.gradle.kts new file mode 100644 index 0000000..09eb6c9 --- /dev/null +++ b/build-logic/basics/src/main/kotlin/testng.versioning.gradle.kts @@ -0,0 +1,5 @@ +if (project != rootProject) { + // The root project takes its version from /gradle.properties -> testng.version + // All the rest projects take the version from the root + version = rootProject.version +} diff --git a/build-logic/code-quality/build.gradle.kts b/build-logic/code-quality/build.gradle.kts new file mode 100644 index 0000000..3955765 --- /dev/null +++ b/build-logic/code-quality/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + `kotlin-dsl` +} + +repositories { + gradlePluginPortal() +} + +dependencies { + implementation("org.sonarqube:org.sonarqube.gradle.plugin:2.8") + implementation("com.github.autostyle:autostyle-plugin-gradle:3.1") +} diff --git a/build-logic/code-quality/src/main/kotlin/testng.sonarqube.gradle.kts b/build-logic/code-quality/src/main/kotlin/testng.sonarqube.gradle.kts new file mode 100644 index 0000000..7551b01 --- /dev/null +++ b/build-logic/code-quality/src/main/kotlin/testng.sonarqube.gradle.kts @@ -0,0 +1,12 @@ +plugins { + id("org.sonarqube") +} + +sonarqube { + properties { + property("sonar.host.url", "https://sonarcloud.io/") + property("sonar.organization", "testng-team") + property("sonar.github.repository", "cbeust/testng") + property("sonar.github.login", "testng-bot") + } +} diff --git a/build-logic/code-quality/src/main/kotlin/testng.style.gradle.kts b/build-logic/code-quality/src/main/kotlin/testng.style.gradle.kts new file mode 100644 index 0000000..709e849 --- /dev/null +++ b/build-logic/code-quality/src/main/kotlin/testng.style.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("com.github.autostyle") +} + +autostyle { + java { + importOrder() + removeUnusedImports() + trimTrailingWhitespace() + endWithNewline() + googleJavaFormat() + } +} diff --git a/build-logic/code-quality/src/main/kotlin/testng.testing.gradle.kts b/build-logic/code-quality/src/main/kotlin/testng.testing.gradle.kts new file mode 100644 index 0000000..ff097f5 --- /dev/null +++ b/build-logic/code-quality/src/main/kotlin/testng.testing.gradle.kts @@ -0,0 +1,40 @@ +import org.gradle.api.tasks.testing.Test + +plugins { + `java-library` +} + +dependencies { + testImplementation("org.assertj:assertj-core:_") +} + +tasks.withType().configureEach { + useTestNG() + providers.gradleProperty("testng.test.extra.jvmargs") + .forUseAtConfigurationTime() + .orNull?.toString()?.trim() + ?.takeIf { it.isNotEmpty() } + ?.let { + // TODO: support quoted arguments + jvmArgs(it.split(Regex("\\s+"))) + } + systemProperty("test.resources.dir", "build/resources/test") + fun passProperty(name: String, default: String? = null) { + val value = System.getProperty(name) ?: default + value?.let { systemProperty(name, it) } + } + // Default verbose is 0, however, it can be adjusted vi -Dtestng.default.verbose=2 + passProperty("testng.default.verbose", "0") + // Allow running tests in a custom locale with -Duser.language=... + passProperty("user.language") + passProperty("user.country") + + @Suppress("unchecked_cast") + val props = System.getProperties().propertyNames() as `java.util`.Enumeration + // Pass testng.* properties to the test JVM + for (e in props) { + if (e.startsWith("testng.")) { + passProperty(e) + } + } +} diff --git a/build-logic/jvm/build.gradle.kts b/build-logic/jvm/build.gradle.kts new file mode 100644 index 0000000..5323a4e --- /dev/null +++ b/build-logic/jvm/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + `kotlin-dsl` +} + +repositories { + gradlePluginPortal() +} + +dependencies { + implementation(project(":basics")) + implementation(project(":code-quality")) + implementation("com.github.vlsi.gradle-extensions:com.github.vlsi.gradle-extensions.gradle.plugin:1.74") + implementation("org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.5.10") +} diff --git a/build-logic/jvm/src/main/kotlin/buildlogic/CopySpecExtensions.kt b/build-logic/jvm/src/main/kotlin/buildlogic/CopySpecExtensions.kt new file mode 100644 index 0000000..2b3df5b --- /dev/null +++ b/build-logic/jvm/src/main/kotlin/buildlogic/CopySpecExtensions.kt @@ -0,0 +1,16 @@ +package buildlogic + +import org.apache.tools.ant.filters.FixCrLfFilter +import org.gradle.api.file.CopySpec +import org.gradle.kotlin.dsl.filter + +fun CopySpec.filterEolSimple(eol: String) { + filteringCharset = "UTF-8" + filter( + FixCrLfFilter::class, mapOf( + "eol" to FixCrLfFilter.CrLf.newInstance(eol), + "fixlast" to true, + "ctrlz" to FixCrLfFilter.AddAsisRemove.newInstance("asis") + ) + ) +} diff --git a/build-logic/jvm/src/main/kotlin/testng.java-library.gradle.kts b/build-logic/jvm/src/main/kotlin/testng.java-library.gradle.kts new file mode 100644 index 0000000..8ccc1a1 --- /dev/null +++ b/build-logic/jvm/src/main/kotlin/testng.java-library.gradle.kts @@ -0,0 +1,64 @@ +import buildlogic.filterEolSimple + +plugins { + `java-library` + id("testng.java") + id("testng.testing") +} + +tasks.withType().configureEach { + excludes.add("org/testng/internal/**") +} + +tasks.withType().configureEach { + inputs.property("java.version", System.getProperty("java.version")) + inputs.property("java.vm.version", System.getProperty("java.vm.version")) + options.apply { + encoding = "UTF-8" + compilerArgs.add("-Xlint:deprecation") + compilerArgs.add("-Werror") + } +} + +tasks.withType().configureEach { + into("META-INF") { + filterEolSimple("crlf") + from("$rootDir/LICENSE.txt") + from("$rootDir/NOTICE") + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } + manifest { + // providers.gradleProperty does not work + // see https://github.com/gradle/gradle/issues/14972 + val name = rootProject.findProperty("project.name") + val vendor = rootProject.findProperty("project.vendor.name") + attributes(mapOf( + "Specification-Title" to name, + "Specification-Version" to project.version, + "Specification-Vendor" to vendor, + "Implementation-Title" to name, + "Implementation-Version" to project.version, + "Implementation-Vendor" to vendor, + "Implementation-Vendor-Id" to rootProject.findProperty("project.vendor.id"), + "Implementation-Url" to rootProject.findProperty("project.url"), + )) + } +} + +@Suppress("unused") +val transitiveSourcesElements by configurations.creating { + description = "Share sources folder with other projects for aggregation (e.g. sources, javadocs, etc)" + isVisible = false + isCanBeResolved = false + isCanBeConsumed = true + extendsFrom(configurations.implementation.get()) + attributes { + attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME)) + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION)) + attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named("source-folders")) + } + // afterEvaluate is to allow creation of the new source sets + afterEvaluate { + sourceSets.main.get().java.srcDirs.forEach { outgoing.artifact(it) } + } +} diff --git a/build-logic/jvm/src/main/kotlin/testng.java-platform.gradle.kts b/build-logic/jvm/src/main/kotlin/testng.java-platform.gradle.kts new file mode 100644 index 0000000..7b0fb95 --- /dev/null +++ b/build-logic/jvm/src/main/kotlin/testng.java-platform.gradle.kts @@ -0,0 +1,4 @@ +plugins { + id("testng.java") + `java-platform` +} diff --git a/build-logic/jvm/src/main/kotlin/testng.java.gradle.kts b/build-logic/jvm/src/main/kotlin/testng.java.gradle.kts new file mode 100644 index 0000000..85e0d6d --- /dev/null +++ b/build-logic/jvm/src/main/kotlin/testng.java.gradle.kts @@ -0,0 +1,28 @@ +plugins { + `java-base` + id("testng.versioning") + id("testng.style") + id("testng.repositories") + // Improves Gradle Test logging + // See https://github.com/vlsi/vlsi-release-plugins/tree/master/plugins/gradle-extensions-plugin + id("com.github.vlsi.gradle-extensions") +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +tasks.withType().configureEach { + inputs.property("java.version", System.getProperty("java.version")) + inputs.property("java.vendor", System.getProperty("java.vendor")) + inputs.property("java.vm.version", System.getProperty("java.vm.version")) + inputs.property("java.vm.vendor", System.getProperty("java.vm.vendor")) +} + +tasks.withType().configureEach { + inputs.property("java.version", System.getProperty("java.version")) + inputs.property("java.vendor", System.getProperty("java.vendor")) + inputs.property("java.vm.version", System.getProperty("java.vm.version")) + inputs.property("java.vm.vendor", System.getProperty("java.vm.vendor")) +} diff --git a/build-logic/jvm/src/main/kotlin/testng.kotlin-library.gradle.kts b/build-logic/jvm/src/main/kotlin/testng.kotlin-library.gradle.kts new file mode 100644 index 0000000..e4ad87d --- /dev/null +++ b/build-logic/jvm/src/main/kotlin/testng.kotlin-library.gradle.kts @@ -0,0 +1,17 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + id("testng.java-library") + kotlin("jvm") +} + +dependencies { + testImplementation(platform("org.jetbrains.kotlin:kotlin-bom:1.5.10")) + testImplementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = "1.8" + } +} diff --git a/build-logic/publishing/build.gradle.kts b/build-logic/publishing/build.gradle.kts new file mode 100644 index 0000000..5bc085b --- /dev/null +++ b/build-logic/publishing/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + `kotlin-dsl` +} + +repositories { + gradlePluginPortal() +} + +dependencies { + implementation(project(":jvm")) + implementation("com.github.vlsi.gradle-extensions:com.github.vlsi.gradle-extensions.gradle.plugin:1.74") + implementation("com.github.johnrengelman.shadow:com.github.johnrengelman.shadow.gradle.plugin:7.0.0") + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin") +} diff --git a/build-logic/publishing/src/main/kotlin/buildlogic/ConfigurationExtensions.kt b/build-logic/publishing/src/main/kotlin/buildlogic/ConfigurationExtensions.kt new file mode 100644 index 0000000..e677b36 --- /dev/null +++ b/build-logic/publishing/src/main/kotlin/buildlogic/ConfigurationExtensions.kt @@ -0,0 +1,19 @@ +package buildlogic + +import org.gradle.api.artifacts.Configuration +import org.gradle.api.attributes.Category +import org.gradle.api.attributes.Usage +import org.gradle.api.model.ObjectFactory +import org.gradle.kotlin.dsl.named + +fun Configuration.javaLibrary(objects: ObjectFactory, usage: String) = + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY)) + attribute(Usage.USAGE_ATTRIBUTE, objects.named(usage)) + } + +fun Configuration.javaLibraryApi(objects: ObjectFactory) = + javaLibrary(objects, Usage.JAVA_API) + +fun Configuration.javaLibraryRuntime(objects: ObjectFactory) = + javaLibrary(objects, Usage.JAVA_RUNTIME) diff --git a/build-logic/publishing/src/main/kotlin/buildlogic/CopySpecExtensions.kt b/build-logic/publishing/src/main/kotlin/buildlogic/CopySpecExtensions.kt new file mode 100644 index 0000000..2b3df5b --- /dev/null +++ b/build-logic/publishing/src/main/kotlin/buildlogic/CopySpecExtensions.kt @@ -0,0 +1,16 @@ +package buildlogic + +import org.apache.tools.ant.filters.FixCrLfFilter +import org.gradle.api.file.CopySpec +import org.gradle.kotlin.dsl.filter + +fun CopySpec.filterEolSimple(eol: String) { + filteringCharset = "UTF-8" + filter( + FixCrLfFilter::class, mapOf( + "eol" to FixCrLfFilter.CrLf.newInstance(eol), + "fixlast" to true, + "ctrlz" to FixCrLfFilter.AddAsisRemove.newInstance("asis") + ) + ) +} diff --git a/build-logic/publishing/src/main/kotlin/buildlogic/DependencyHandlerExtensions.kt b/build-logic/publishing/src/main/kotlin/buildlogic/DependencyHandlerExtensions.kt new file mode 100644 index 0000000..3b4100d --- /dev/null +++ b/build-logic/publishing/src/main/kotlin/buildlogic/DependencyHandlerExtensions.kt @@ -0,0 +1,34 @@ +package buildlogic + +import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.component.ModuleComponentIdentifier +import org.gradle.api.artifacts.component.ProjectComponentIdentifier +import org.gradle.api.artifacts.dsl.DependencyHandler +import org.gradle.api.artifacts.result.ResolvedVariantResult +import org.gradle.api.attributes.Category +import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.project + +/** + * Converts the resolved result back to a dependency notation, so it can be resolved again. + * Note: the conversion does not support "classifiers" + */ +fun DependencyHandler.reconstruct(variant: ResolvedVariantResult): Dependency { + val category = variant.attributes.run { + keySet().firstOrNull { it.name == Category.CATEGORY_ATTRIBUTE.name }?.let { getAttribute(it) } + } + + val id = variant.owner + return when (id) { + is ProjectComponentIdentifier -> project(id.projectPath) + is ModuleComponentIdentifier -> create(id.group, id.module, id.version) + else -> throw IllegalArgumentException("Can't convert $id to dependency") + }.let { + when (category) { + Category.REGULAR_PLATFORM -> platform(it) + Category.ENFORCED_PLATFORM -> enforcedPlatform(it) + Category.LIBRARY -> it + else -> throw IllegalStateException("Unexpected dependency type $category for id $id") + } + } +} diff --git a/build-logic/publishing/src/main/kotlin/buildlogic/FirstLayerDependencies.kt b/build-logic/publishing/src/main/kotlin/buildlogic/FirstLayerDependencies.kt new file mode 100644 index 0000000..af43dfa --- /dev/null +++ b/build-logic/publishing/src/main/kotlin/buildlogic/FirstLayerDependencies.kt @@ -0,0 +1,89 @@ +package buildlogic + +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.result.ResolvedComponentResult +import org.gradle.api.artifacts.result.ResolvedDependencyResult +import org.gradle.api.artifacts.result.ResolvedVariantResult +import org.gradle.api.artifacts.result.UnresolvedDependencyResult +import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.kotlin.dsl.the + +/** + * Walks over the dependency tree and selects the first non-shaded dependencies. + */ +fun ResolvedComponentResult.filterFirstLayerDependenciesTo( + set: MutableSet, + isShaded: (ResolvedVariantResult) -> Boolean +): MutableSet { + for (dependency in dependencies) { + when (dependency) { + is UnresolvedDependencyResult -> throw IllegalStateException( + "Unresolved dependency $dependency: ${dependency.failure}", + dependency.failure + ) + is ResolvedDependencyResult -> { + val resolvedVariant = dependency.resolvedVariant + if (resolvedVariant in set) { + continue + } + if (isShaded(resolvedVariant)) { + dependency.selected.filterFirstLayerDependenciesTo(set, isShaded) + continue + } + // Ok, we detected the first non-shaded dependency, so we do not need to dig its dependencies + set += resolvedVariant + } + } + } + return set +} + +fun ResolvedComponentResult.filterFirstLayerDependencies(isShaded: (ResolvedVariantResult) -> Boolean) = + filterFirstLayerDependenciesTo(mutableSetOf(), isShaded) + +/** + * Prepares a configuration that selects the first non-shaded dependencies: + * 1. It resolves "AllDependencies" configuration + * 2. Then the function walks over the dependency tree and finds the first non-shaded dependencies + * 3. Then it converts the resolution results to dependency notation + * 4. The collected dependencies are added to "FirstNonMergedDependencies" configuration. + * Note: the trigger for all this computation is withDependencies, so the resolution performed only when it is required. + */ +fun Project.firstLayerDependencies( + usage: String, + conf: Configuration, + vararg rest: Configuration, +): Configuration { + val optionalFeatures = (the() as ExtensionAware).the() + val usageKind = usage.removePrefix("java-").capitalize() + + val allDependencies = configurations.create(conf.name + "${usageKind}AllDependencies") { + description = "Resolves the list of all dependencies for $usage in ${conf.name}" + isCanBeConsumed = false + isCanBeResolved = true + isVisible = false + extendsFrom(conf) + extendsFrom(*rest) + javaLibrary(objects, usage) + } + + return configurations.create(conf.name + "${usageKind}FirstNonMergedDependencies") { + description = "Resolves the list of external dependencies for $usage in ${conf.name}" + isCanBeConsumed = false + isCanBeResolved = true + isTransitive = false + isVisible = false + javaLibrary(objects, usage) + withDependencies { + // Clear any user-added-by-mistake dependencies + clear() + addAll( + allDependencies.incoming.resolutionResult.root + .filterFirstLayerDependencies(optionalFeatures.shadedDependenciesFilter.get()) + .map { project.dependencies.reconstruct(it) } + ) + } + } +} diff --git a/build-logic/publishing/src/main/kotlin/buildlogic/OptionalFeaturesExtension.kt b/build-logic/publishing/src/main/kotlin/buildlogic/OptionalFeaturesExtension.kt new file mode 100644 index 0000000..05bbf2a --- /dev/null +++ b/build-logic/publishing/src/main/kotlin/buildlogic/OptionalFeaturesExtension.kt @@ -0,0 +1,100 @@ +package buildlogic + +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.component.ProjectComponentIdentifier +import org.gradle.api.artifacts.dsl.DependencyHandler +import org.gradle.api.artifacts.result.ResolvedVariantResult +import org.gradle.api.attributes.Usage +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.provider.Property +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.kotlin.dsl.get +import org.gradle.kotlin.dsl.invoke +import org.gradle.kotlin.dsl.provideDelegate +import org.gradle.kotlin.dsl.the + +/** + * DSL for declaring optional features. The dependencies are stored to plugin-local "declared-*" configurations + */ +class OptionalFeatureBuilder( + private val dependencyHandler: DependencyHandler, + private val declaredApi: Configuration, + private val declaredImplementation: Configuration +) { + fun platform(dependencyNotation: Any) = + dependencyHandler.platform(dependencyNotation) + + fun api(dependencyNotation: Any) { + dependencyHandler.add(declaredApi.name, dependencyNotation) + } + + fun implementation(dependencyNotation: Any) { + dependencyHandler.add(declaredImplementation.name, dependencyNotation) + } +} + +abstract class OptionalFeaturesExtension(private val project: Project) { + // It allows to explicitly list which modules should be merged and which will be left alone as dependencies + abstract val shadedDependenciesFilter: Property<(ResolvedVariantResult) -> Boolean> + + init { + // By default, shade all modules from the current build + shadedDependenciesFilter.convention { + it.owner.let { id -> id is ProjectComponentIdentifier && id.build.isCurrentBuild } + } + } + + fun create(name: String, builder: OptionalFeatureBuilder.() -> Unit) { + project.the().registerFeature(name) { + val sourceSets: SourceSetContainer by project + usingSourceSet(sourceSets["main"]) + } + + val declaredApi = project.configurations.create("${name}DeclaredApi") { + description = "Api dependencies for feature $name" + isCanBeResolved = false + isCanBeConsumed = false + } + val declaredImplementation = project.configurations.create("${name}DeclaredImplementation") { + description = "Implementation dependencies for feature $name" + isCanBeResolved = false + isCanBeConsumed = false + } + val declaredRuntime = project.configurations.create("${name}DeclaredRuntime") { + description = "Runtime dependencies for feature $name" + isCanBeResolved = false + isCanBeConsumed = false + extendsFrom(declaredApi, declaredImplementation) + } + + OptionalFeatureBuilder( + project.dependencies, + declaredApi, + declaredImplementation + ).builder() + + // This is to include all testng modules (even optional) to -all.jar + project.configurations["shadedDependencyFullRuntimeClasspath"] + .extendsFrom(declaredRuntime) + + // By default Gradle adds the jar as artifact, however, we won't need it + // We'll put merged jar later as a main artifact + project.configurations { + get("${name}ApiElements").apply { + // This effectively adds "optional" pom dependencies for scope=compile + extendsFrom(project.firstLayerDependencies(Usage.JAVA_API, declaredApi)) + artifacts.clear() + // The feature do not provide their own classes or resources + // All the feature resources would come from the dependencies + outgoing.variants.removeIf { it.name == "classes" || it.name == "resources" } + } + get("${name}RuntimeElements").apply { + // This effectively adds "optional" pom dependencies for scope=runtime + extendsFrom(project.firstLayerDependencies(Usage.JAVA_RUNTIME, declaredRuntime)) + artifacts.clear() + outgoing.variants.removeIf { it.name == "classes" || it.name == "resources" } + } + } + } +} diff --git a/build-logic/publishing/src/main/kotlin/testng.local-maven-repo.gradle.kts b/build-logic/publishing/src/main/kotlin/testng.local-maven-repo.gradle.kts new file mode 100644 index 0000000..cef8f94 --- /dev/null +++ b/build-logic/publishing/src/main/kotlin/testng.local-maven-repo.gradle.kts @@ -0,0 +1,40 @@ +plugins { + `maven-publish` +} + +val localRepoElements by configurations.creating { + isCanBeConsumed = true + isCanBeResolved = false + description = + "Shares local maven repository directory that contains the artifacts produced by the current project" + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named("maven-repository")) + attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL)) + } +} + +val localRepoDir = layout.buildDirectory.dir("local-maven-repo") + +publishing { + repositories { + maven { + name = "local" + url = uri(localRepoDir) + } + } +} + +localRepoElements.outgoing.artifact(localRepoDir) { + builtBy(tasks.named("publishAllPublicationsToLocalRepository")) +} + +val cleanLocalRepository by tasks.registering(Delete::class) { + description = "Clears local-maven-repo so timestamp-based snapshot artifacts do not consume space" + delete(localRepoDir) +} + +tasks.withType() + .matching { it.name.contains("ToLocalRepository") } + .configureEach { + dependsOn(cleanLocalRepository) + } diff --git a/build-logic/publishing/src/main/kotlin/testng.maven-publish.gradle.kts b/build-logic/publishing/src/main/kotlin/testng.maven-publish.gradle.kts new file mode 100644 index 0000000..5216f3f --- /dev/null +++ b/build-logic/publishing/src/main/kotlin/testng.maven-publish.gradle.kts @@ -0,0 +1,51 @@ +plugins { + `maven-publish` + id("testng.local-maven-repo") +} + +// It takes value from root project always: https://github.com/gradle/gradle/issues/13302 +val scmUrl = providers.gradleProperty("scm.url") + +publishing { + publications { + withType().configureEach { + pom { + name.set(artifactId) + description.set(providers.provider { project.description }) + // It takes value from root project always: https://github.com/gradle/gradle/issues/13302 + url.set(providers.gradleProperty("project.url")) + licenses { + license { + name.set("Apache License, Version 2.0") + url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") + } + } + issueManagement { + system.set("Github") + url.set(scmUrl.map { "${it.removeSuffix(".git")}/issues" }) + } + developers { + developer { + id.set("cbeust") + name.set("Cedric Beust") + email.set("cedric@beust.com") + } + developer { + id.set("juherr") + name.set("Julien Herr") + email.set("julien@herr.fr") + } + developer { + id.set("krmahadevan") + name.set("Krishnan Mahadevan") + email.set("krishnan.mahadevan1978@gmail.com") + } + } + scm { + connection.set(scmUrl.map { "scm:git:$it" }) + url.set(scmUrl) + } + } + } + } +} diff --git a/build-logic/publishing/src/main/kotlin/testng.merge-feature-jars.gradle.kts b/build-logic/publishing/src/main/kotlin/testng.merge-feature-jars.gradle.kts new file mode 100644 index 0000000..5fc3282 --- /dev/null +++ b/build-logic/publishing/src/main/kotlin/testng.merge-feature-jars.gradle.kts @@ -0,0 +1,160 @@ +import buildlogic.OptionalFeaturesExtension +import buildlogic.firstLayerDependencies +import buildlogic.javaLibrary +import buildlogic.reconstruct +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +plugins { + `java-base` + `reporting-base` + id("testng.published-java-library") +} + +val optionalFeatures = (the() as ExtensionAware).extensions + .create("optionalFeatures", project) + +inline fun AttributeContainer.attribute(attr: Attribute, value: String) = + attribute(attr, objects.named(value)) + +val shadedDependencyElements by configurations.creating { + description = "Declares which modules to aggregate into ...-all.jar" + isCanBeConsumed = false + isCanBeResolved = false +} + +fun Configuration.javaLibraryRuntime() = javaLibrary(objects, Usage.JAVA_RUNTIME) + +configurations["api"].extendsFrom( + firstLayerDependencies( + Usage.JAVA_API, + shadedDependencyElements + ) +) + +configurations["implementation"].extendsFrom( + firstLayerDependencies( + Usage.JAVA_RUNTIME, + shadedDependencyElements + ) +) + +val shadedDependencyFullRuntimeClasspath by configurations.creating { + description = "Resolves the list of shadedDependencyElements to testng and external dependencies" + isCanBeConsumed = false + isCanBeResolved = true + isVisible = false + extendsFrom(shadedDependencyElements) + javaLibraryRuntime() +} + +val mergedJars by configurations.creating { + description = "Resolves the list of testng modules to include into -all jar" + isCanBeConsumed = false + isCanBeResolved = true + isTransitive = false + javaLibraryRuntime() + withDependencies { + // Clear any user-added-by-mistake dependencies + clear() + // Identifies TestNG projects in shadedDependencyFullRuntimeClasspath dependency tree + addAll( + shadedDependencyFullRuntimeClasspath.incoming.resolutionResult.allDependencies + .asSequence() + .filter { !it.isConstraint } + .filterIsInstance() + .mapNotNull { resolved -> + resolved.resolvedVariant + .takeIf { optionalFeatures.shadedDependenciesFilter.get()(it) } + ?.let { project.dependencies.reconstruct(it) } + } + ) + } +} + +val shadedDependencyJavadocClasspath by configurations.creating { + description = "Resolves a runtime classpath of the aggregated -all dependenices" + isCanBeConsumed = false + isCanBeResolved = true + isVisible = false + extendsFrom(mergedJars) + extendsFrom(configurations["compileClasspath"]) + extendsFrom(shadedDependencyFullRuntimeClasspath) + javaLibraryRuntime() + attributes { + attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, LibraryElements.JAR) + attribute(Bundling.BUNDLING_ATTRIBUTE, Bundling.EXTERNAL) + } +} + +val mergedJar by tasks.registering(ShadowJar::class) { + group = LifecycleBasePlugin.BUILD_GROUP + description = "Builds all-project jar (third-party dependencies are left as is)" + configurations = listOf(mergedJars) + // Individual jars have their own license, and ShadowJar overrides "duplicateStrategy" option + // So we exclude LICENSE.txt from the merged jars, and let testng.java.gradle.kts to add the default one + exclude("META-INF/LICENSE.txt") + archiveClassifier.set("all") +} + +dependencies { + "implementation"(files(mergedJar)) +} + +val sourcesToMerge by configurations.creating { + description = "Resolves the list of source directories to include into sources-all jar" + isCanBeConsumed = false + isCanBeResolved = true + isTransitive = false // jarsToMerge is a full set of modules, so no need to have transitivity here + extendsFrom(mergedJars) + attributes { + attribute(Usage.USAGE_ATTRIBUTE, Usage.JAVA_RUNTIME) + attribute(Category.CATEGORY_ATTRIBUTE, Category.DOCUMENTATION) + attribute(DocsType.DOCS_TYPE_ATTRIBUTE, "source-folders") + } +} + +val mergedSourcesJar by tasks.registering(Jar::class) { + from(sourcesToMerge.incoming.artifactView { lenient(true) }.files) + archiveClassifier.set("sources-all") +} + +val mergedJavadoc by tasks.registering(Javadoc::class) { + description = "Generates an aggregate javadoc" + group = LifecycleBasePlugin.BUILD_GROUP + setSource(sourcesToMerge.incoming.artifactView { lenient(true) }.files) + include("**/*.java") + setDestinationDir(reporting.file("mergedJavadoc")) + classpath = shadedDependencyJavadocClasspath +} + +val mergedJavadocJar by tasks.registering(Jar::class) { + description = "Generates an aggregate javadoc jar" + group = LifecycleBasePlugin.BUILD_GROUP + from(mergedJavadoc) + archiveClassifier.set("javadoc-all") +} + +// Configure merged artifacts for publication +configurations.named("sourcesElements") { + artifacts.clear() + outgoing.artifact(mergedSourcesJar) { + classifier = "sources" + } +} + +configurations.named("javadocElements") { + artifacts.clear() + outgoing.artifact(mergedJavadocJar) { + classifier = "javadoc" + } +} + +for (name in listOf("apiElements", "runtimeElements")) { + configurations.named(name) { + artifacts.clear() + outgoing.artifact(mergedJar) { + classifier = null + } + outgoing.variants.removeIf { it.name == "classes" || it.name == "resources" } + } +} diff --git a/build-logic/publishing/src/main/kotlin/testng.published-java-library.gradle.kts b/build-logic/publishing/src/main/kotlin/testng.published-java-library.gradle.kts new file mode 100644 index 0000000..21ff5b8 --- /dev/null +++ b/build-logic/publishing/src/main/kotlin/testng.published-java-library.gradle.kts @@ -0,0 +1,41 @@ +import com.github.vlsi.gradle.publishing.dsl.simplifyXml +import com.github.vlsi.gradle.publishing.dsl.versionFromResolution + +plugins { + id("testng.reproducible-builds") + id("testng.java-library") + id("testng.maven-publish") +} + +java { + withJavadocJar() + withSourcesJar() +} + +dependencies { + // If the user adds core and api with different versions, + // then Gradle would select **both** core and api with the same version + // Note: un-comment when testng-bom is published + // implementation(platform(project(":testng-bom"))) + // For some reason this can't be in code-quality/testng.testing :( + testImplementation(project(":testng-test-kit")) +} + +publishing { + publications { + create("maven") { + from(components["java"]) + // Gradle feature variants can't be mapped to Maven's pom + suppressAllPomMetadataWarnings() + + // Use the resolved versions in pom.xml + // Gradle might have different resolution rules, so we set the versions + // that were used in Gradle build/test. + versionFromResolution() + + pom { + simplifyXml() + } + } + } +} diff --git a/build-logic/publishing/src/main/kotlin/testng.published-java-platform.gradle.kts b/build-logic/publishing/src/main/kotlin/testng.published-java-platform.gradle.kts new file mode 100644 index 0000000..d78cdb1 --- /dev/null +++ b/build-logic/publishing/src/main/kotlin/testng.published-java-platform.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("testng.reproducible-builds") + id("testng.java-platform") + id("testng.maven-publish") +} + +publishing { + publications { + create("maven") { + from(components["javaPlatform"]) + } + } +} diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts new file mode 100644 index 0000000..ee7e4ab --- /dev/null +++ b/build-logic/settings.gradle.kts @@ -0,0 +1,12 @@ +dependencyResolutionManagement { + repositories { + gradlePluginPortal() + } +} + +rootProject.name = "build-logic" + +include(":basics") +include(":code-quality") +include(":jvm") +include(":publishing") diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..2c43413 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,56 @@ +plugins { + id("testng.repositories") + id("com.github.vlsi.stage-vote-release") + id("idea") +} + +val String.v: String get() = rootProject.extra["$this.version"] as String +val buildVersion = "testng".v + releaseParams.snapshotSuffix +version = buildVersion + +println("Building testng $buildVersion") + +/** + * Release procedure: + * 1. ./gradlew prepareVote -Prc=1 -Pgh (builds artifacts, stages them to Central, closes staging repository) + * 2. ./gradlew publishDist -Prc=1 -Pgh (publishes staging repository and pushes release tag) + * + * See https://github.com/vlsi/vlsi-release-plugins#stage-vote-release-plugin + * The following properties (e.g. $HOME/.gradle/gradle.properties) configure credentials. + * The plugin would raise a warning if the property is not found. + * signing.gnupg.keyName=... + * signing.password=... + * signing.secretKeyRingFile=... + * ghNexusUsername=... + * ghNexusPassword=... + * ghGitSourceUsername=... + * ghGitSourcePassword=... + * + * You can use https://github.com/vlsi/asflike-release-environment as a playground to dry run releases. + */ + +fun property(name: String) = + providers.gradleProperty(name).forUseAtConfigurationTime() + +releaseParams { + tlp.set(property("github.repository")) + organizationName.set(property("github.organization")) + componentName.set(property("project.name")) + prefixForProperties.set("gh") + svnDistEnabled.set(false) + sitePreviewEnabled.set(false) + releaseTag.set(buildVersion) // or "testng-$buildVersion" + nexus { + packageGroup.set(property("nexus.profile")) + mavenCentral() + } + voteText.set { + """ + ${it.componentName} v${it.version}-rc${it.rc} is ready for preview. + + Git SHA: ${it.gitSha} + Staging repository: ${it.nexusRepositoryUri} + """.trimIndent() + } +} + diff --git a/debian/README.source b/debian/README.source new file mode 100644 index 0000000..e7c3eec --- /dev/null +++ b/debian/README.source @@ -0,0 +1,9 @@ +Information about testng +------------------------ + +This package was debianized using the mh_make command +from the maven-debian-helper package. + +The build system uses Maven but prevents it from downloading +anything from the Internet, making the build compliant with +the Debian policy. diff --git a/debian/changelog b/debian/changelog index bad88e2..4fb1d00 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,224 @@ -template-repository (1.0-1) unstable; urgency=medium +testng7 (7.5-2) unstable; urgency=medium - * Initial release + * Source-only upload. - -- Tsic404 Sat, 28 Jan 2023 13:46:49 +0800 + -- Matthias Klose Thu, 15 Jun 2023 20:21:39 +0200 + +testng7 (7.5-1) unstable; urgency=medium + + * New upstream release 7.5, packaged as a separate source and binary, + required for jtreg version 6 and 7 (LP: #2012320). Closes: #990538. + * d/p/build-with-gradle.patch: provide Groovy Gradle build. + + -- Vladimir Petko Tue, 13 Jun 2023 07:59:17 +0200 + +testng (6.9.12-4) unstable; urgency=medium + + * Team upload. + + [ Tiago Stürmer Daitx ] + * d/p/remove-guava-dependency-pr1086.patch: apply upstream patch to + remove guava dependency (Closes: #905675, LP: #1785896) + + -- tony mancill Wed, 08 Aug 2018 06:24:23 -0700 + +testng (6.9.12-3) unstable; urgency=medium + + * Team upload. + * Use the non relocated jcommander artifact to help Gradle (Closes: #895886) + * Standards-Version updated to 4.1.5 + * Switch to debhelper level 11 + * Use salsa.debian.org Vcs-* URLs + + -- Emmanuel Bourg Mon, 30 Jul 2018 17:53:55 +0200 + +testng (6.9.12-2) unstable; urgency=medium + + * Team upload. + * Added a relocation pom redirecting the version '6.x' to 'debian' + * Standards-Version updated to 4.1.0 + * Switch to debhelper level 10 + + -- Emmanuel Bourg Fri, 15 Sep 2017 11:17:33 +0200 + +testng (6.9.12-1) unstable; urgency=medium + + * Fix Vcs download URL + * New upstream release + + -- Eugene Zhukov Fri, 08 Jul 2016 14:51:20 +0200 + +testng (6.9.10-1) unstable; urgency=medium + + * New upstream release + * Standards-Version updated to 3.9.7 (no changes) + * d/control: + - Use https Vcs URLs + - Remove redundant Build-Depends-Indep + - Use my d.o email + * Update version_java.patch with 6.9.10 version + + -- Eugene Zhukov Sun, 03 Apr 2016 10:15:02 +0300 + +testng (6.9.5-1) unstable; urgency=medium + + * New upstream release + * Add copy-rename-maven-plugin to d/maven.ignoreRules + * Add patch for Version.java + * Switch away from CDBS build system + + -- Eugene Zhukov Tue, 18 Aug 2015 10:27:45 +0000 + +testng (6.9.4-2) unstable; urgency=medium + + * maven.properties: Add UTF-8 source encoding + * d/control: Add build dependency on libbuild-helper-maven-plugin-java + + -- Eugene Zhukov Wed, 03 Jun 2015 07:41:21 +0000 + +testng (6.9.4-1) unstable; urgency=medium + + * New upstream release + * maven.ignoreRules: Add maven-release-plugin, assertj-core + * maven.properties: Remove maven.compiler.target=1.5 + * maven.rules: Drop no_aop classifier for guice dependency + + -- Eugene Zhukov Mon, 01 Jun 2015 07:21:10 +0000 + +testng (6.8.21-1) unstable; urgency=low + + * New upstream release + + -- Eugene Zhukov Wed, 06 May 2015 11:26:59 +0000 + +testng (6.8.20-1) experimental; urgency=medium + + * New upstream release + + -- Eugene Zhukov Tue, 03 Feb 2015 13:25:20 +0000 + +testng (6.8.17-1) experimental; urgency=medium + + * New upstream release + + -- Eugene Zhukov Fri, 23 Jan 2015 20:29:58 +0000 + +testng (6.8.13-1) experimental; urgency=medium + + [ tony mancill ] + * Add explicit build-dep on ant. (Closes: #771260) + + [ Eugene Zhukov ] + * New upstream release + + -- Eugene Zhukov Sat, 03 Jan 2015 17:43:11 +0000 + +testng (6.8.8-3) unstable; urgency=low + + * Dropped orig-tar.sh in favor of jh_repack in d/watch + * Added source for jquery-1.7.1.min.js (Closes: #769831) + * Standards-Version updated to version 3.9.6 + * Updated debhelper to version 9 in d/control + + -- Eugene Zhukov Mon, 17 Nov 2014 11:43:10 +0000 + +testng (6.8.8-2) unstable; urgency=low + + * Description updated. + * Switched from bsh (pulls in Java runtime) to libbsh-java dependency + (Closes: #684427) + * Switched to orig-tar.sh in d/watch to exclude jquery-1.7.1.min.js + + -- Eugene Zhukov Thu, 24 Apr 2014 08:53:40 +0000 + +testng (6.8.8-1) unstable; urgency=low + + * Team upload + * New upstream release + * Removed orig-tar.sh in favor of jh_repack in d/watch + * Standards-Version updated to version 3.9.5 + + -- Eugene Zhukov Fri, 14 Mar 2014 12:08:55 +0000 + +testng (6.8.7-2) unstable; urgency=low + + * Team upload + * Upload to unstable + + -- Sylvestre Ledru Mon, 21 Oct 2013 15:53:15 +0200 + +testng (6.8.7-1) experimental; urgency=low + + * Team upload. + * Package adoption (Closes: #705103) + * New upstream release (Closes: #579122) + * repackaged with maven-debian-helper + * dropped all previous patches + + -- Eugene Zhukov Sat, 07 Sep 2013 10:42:29 +0000 + +testng (5.11+dfsg-3) unstable; urgency=low + + * Sync from Ubuntu (Closes: #605369, #639046) + * Standards-Version updated to version 3.9.2 + * Package moved under the pkg-java team (thanks Marcus) + * Add myself as maintainer + * Fix a bad detection of the JVM path (LP: #888128) + + -- Sylvestre Ledru Tue, 20 Dec 2011 00:39:49 +0100 + +testng (5.11+dfsg-2ubuntu2) oneiric; urgency=low + + * Fix FTBFS (LP: #829508): + - d/patches/debian/junitconvertor-test-fix.diff: Switch from use of + deprecated parameters attribute in @Test to using @Parameters to + ensure that JUnitConvertor Test passes. + + -- James Page Tue, 20 Sep 2011 12:15:27 +0100 + +testng (5.11+dfsg-2ubuntu1) natty; urgency=low + + * debian/rules: updated to use ant-nodeps to fix FTBFS (LP: #674382) + + -- James Page Fri, 12 Nov 2010 08:08:26 +0000 + +testng (5.11+dfsg-2) unstable; urgency=low + + * Fix non-threadsafe test. Thanks to Lucas Nussbaum. (Closes: #593040). + + -- Marcus Better Mon, 16 Aug 2010 10:52:51 +0200 + +testng (5.11+dfsg-1) unstable; urgency=low + + * New upstream version. + * Switch to source format 3.0 (quilt). Thanks to Raphael + Hertzog. (Closes: #538708) + * Disable tests that fail with qdox 1.11. Thanks to Daniel Schepler + (Closes: #577477). + * debian/control: Bump standards-version (no changed needed). + + -- Marcus Better Sun, 25 Apr 2010 09:42:04 +0200 + +testng (5.10+dfsg-2) unstable; urgency=low + + * Install Maven repository metadata. Thanks to Ludovic Claude. + * debian/control: Bump standards-version. + + -- Marcus Better Mon, 07 Sep 2009 10:33:28 +0200 + +testng (5.10+dfsg-1) unstable; urgency=low + + * Changed section to "java". + * debian/rules: Make it work with debhelper 7.3.5. (Closes: #538016) + * Use qdox 1.9 and add a patch for the API changes. Thanks to Ludovic + Claude. + * debian/control: Bump policy version to 3.8.2. + * debian/control: Build with default-jdk. + + -- Marcus Better Thu, 23 Jul 2009 12:11:17 +0200 + +testng (5.9+dfsg-1) unstable; urgency=low + + * Initial release. (Closes: #465326) + + -- Marcus Better Mon, 04 May 2009 10:47:43 +0200 diff --git a/debian/compat b/debian/compat deleted file mode 100644 index b4de394..0000000 --- a/debian/compat +++ /dev/null @@ -1 +0,0 @@ -11 diff --git a/debian/control b/debian/control index cb7c4a0..65ffef4 100644 --- a/debian/control +++ b/debian/control @@ -1,15 +1,47 @@ -Source: template-repository -Section: unknown +Source: testng7 +Section: java Priority: optional -Maintainer: Tsic404 -Build-Depends: debhelper (>= 11) -Standards-Version: 4.1.3 -Homepage: https://github.com/deepin-community/template-repository -#Vcs-Browser: https://salsa.debian.org/debian/deepin-community-template-repository -#Vcs-Git: https://salsa.debian.org/debian/deepin-community-template-repository.git +Maintainer: Debian Java Maintainers +Uploaders: Eugene Zhukov , Vladimir Petko , Matthias Klose +Build-Depends: + ant, + debhelper-compat (=13), + default-jdk, + junit4, + libbsh-java, + libbuild-helper-maven-plugin-java, + libguice-java, + libjcommander-java, + libmaven-bundle-plugin-java, + libyaml-snake-java, + maven-repo-helper, + gradle-debian-helper, + libassertj-core-java +Rules-Requires-Root: no +Standards-Version: 4.6.2 +Vcs-Git: https://salsa.debian.org/java-team/testng.git +Vcs-Browser: https://salsa.debian.org/java-team/testng +Homepage: http://testng.org -Package: template-repository -Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends} -Description: - +Package: libtestng7-java +Architecture: all +Depends: ${maven:Depends}, ${misc:Depends} +Recommends: ${maven:OptionalDepends} +Conflicts: testng +Description: testing framework for Java + TestNG is a testing framework inspired from JUnit and NUnit but introducing + some new functionalities that make it more powerful and easier to use, such as: + * Annotations. + * Run your tests in arbitrarily big thread pools with various policies + available (all methods in their own thread, one thread per test class, etc.) + * Test that your code is multithread safe. + * Flexible test configuration. + * Support for data-driven testing (with @DataProvider). + * Support for parameters. + * Powerful execution model (no more TestSuite). + * Supported by a variety of tools and plug-ins (Eclipse, IDEA, Maven, etc.) + * Embeds BeanShell for further flexibility. + * Default JDK functions for runtime and logging (no dependencies). + * Dependent methods for application server testing. + TestNG is designed to cover all categories of tests: + unit, functional, end-to-end, integration, etc. diff --git a/debian/copyright b/debian/copyright index f5c805e..8ee2d51 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,22 +1,22 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: template-repository -Source: https://github.com/deepin-community/template-repository +Files-Excluded: + .gitignore + .gitattributes + .github/* + gradle/* +Comment: Exclude github artifacts, .gitignore, .gitattributes and gradle wrapper +Upstream-Name: TestNG +Source: http://testng.org Files: * -Copyright: 2023 Tsic404 -License: GPL-2+ - This package is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - . - This package is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - . - You should have received a copy of the GNU General Public License - along with this program. If not, see - . - On Debian systems, the complete text of the GNU General - Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". +Copyright: 2013, Cedric Beust +License: Apache-2.0 + +Files: debian/* +Copyright: 2013, Eugene Zhukov + 2023, Canonical Ltd. +License: Apache-2.0 + +License: Apache-2.0 + On Debian systems, the full text of the Apache-2.0 license + can be found in the file '/usr/share/common-licenses/Apache-2.0' diff --git a/debian/libtestng7-java.poms b/debian/libtestng7-java.poms new file mode 100644 index 0000000..8640402 --- /dev/null +++ b/debian/libtestng7-java.poms @@ -0,0 +1,28 @@ +# List of POM files for the package +# Format of this file is: +# [option]* +# where option can be: +# --ignore: ignore this POM and its artifact if any +# --ignore-pom: don't install the POM. To use on POM files that are created +# temporarily for certain artifacts such as Javadoc jars. [mh_install, mh_installpoms] +# --no-parent: remove the tag from the POM +# --package=: an alternative package to use when installing this POM +# and its artifact +# --has-package-version: to indicate that the original version of the POM is the same as the upstream part +# of the version for the package. +# --keep-elements=: a list of XML elements to keep in the POM +# during a clean operation with mh_cleanpom or mh_installpom +# --artifact=: path to the build artifact associated with this POM, +# it will be installed when using the command mh_install. [mh_install] +# --java-lib: install the jar into /usr/share/java to comply with Debian +# packaging guidelines +# --usj-name=: name to use when installing the library in /usr/share/java +# --usj-version=: version to use when installing the library in /usr/share/java +# --no-usj-versionless: don't install the versionless link in /usr/share/java +# --dest-jar=: the destination for the real jar. +# It will be installed with mh_install. [mh_install] +# --classifier=: Optional, the classifier for the jar. Empty by default. +# --site-xml=: Optional, the location for site.xml if it needs to be installed. +# Empty by default. [mh_install] +# +build/debian/testng.pom --artifact=build/libs/testng-*.jar --java-lib --relocate=org.testng:testng:7.x diff --git a/debian/maven.cleanIgnoreRules b/debian/maven.cleanIgnoreRules new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/debian/maven.cleanIgnoreRules @@ -0,0 +1 @@ + diff --git a/debian/maven.ignoreRules b/debian/maven.ignoreRules new file mode 100644 index 0000000..43e63ca --- /dev/null +++ b/debian/maven.ignoreRules @@ -0,0 +1,7 @@ + +com.coderplus.maven.plugins copy-rename-maven-plugin * * * * +org.apache.maven.plugins maven-gpg-plugin * * * * +org.apache.maven.plugins maven-javadoc-plugin * * * * +org.apache.maven.plugins maven-release-plugin * * * * +org.apache.maven.plugins maven-source-plugin * * * * +org.apache.maven.plugins maven-surefire-plugin * * * * diff --git a/debian/maven.properties b/debian/maven.properties new file mode 100644 index 0000000..242da7e --- /dev/null +++ b/debian/maven.properties @@ -0,0 +1,6 @@ +# Include here properties to pass to Maven during the build. +# For example: +# maven.test.skip=true + +maven.test.skip=true +project.build.sourceEncoding=UTF-8 diff --git a/debian/maven.rules b/debian/maven.rules new file mode 100644 index 0000000..45a3b3e --- /dev/null +++ b/debian/maven.rules @@ -0,0 +1,7 @@ + +com.google.inject guice jar s/.*/debian/ s/no_aop// * +junit junit jar s/4\..*/4.x/ * * +org.yaml snakeyaml bundle s/1\..*/1.x/ * * +org.yaml snakeyaml s/jar/bundle/ s/1\..*/1.x/ * * +s/ant/org.apache.ant/ * * s/.*/debian/ * * +org.assertj assertj-core jar s/.*/debian/ * * diff --git a/debian/patches/build-with-gradle.patch b/debian/patches/build-with-gradle.patch new file mode 100644 index 0000000..b1a3b64 --- /dev/null +++ b/debian/patches/build-with-gradle.patch @@ -0,0 +1,128 @@ +Description: Provide a Groovy gradle build for testng +Author: Vladimir Petko +Forwarded: not-needed + +--- /dev/null ++++ b/build.gradle +@@ -0,0 +1,117 @@ ++plugins { ++ id 'java-library' ++ id 'maven-publish' ++} ++ ++repositories { ++ mavenLocal() ++} ++ ++apply plugin: 'java' ++apply plugin: 'java-library' ++apply plugin: 'maven-publish' ++ ++sourceCompatibility = 8 ++targetCompatibility = 8 ++ ++dependencies { ++ compile 'org.slf4j:slf4j-api:1.7.36' ++ compile 'com.beust:jcommander:1.82' ++ compile 'org.apache.ant:ant:1.10.12' ++ compile 'com.google.inject:guice:5.1.0' ++ compile 'junit:junit:4.13.2' ++ compile 'org.yaml:snakeyaml:1.33' ++ compile 'org.assertj:assertj-core:2.3.0' ++} ++ ++group = 'org.testng' ++version = System.env.DEB_VERSION_UPSTREAM ++description = 'testng' ++ ++sourceSets { ++ main { ++ java { ++ srcDirs 'testng-runner-api/src/main/java' ++ srcDirs 'testng-core/src/main/java' ++ srcDirs 'testng-asserts/src/main/java' ++ srcDirs 'testng-core-api/src/main/java' ++ srcDirs 'testng-test-kit/src/main/java' ++ srcDirs 'testng-reflection-utils/src/main/java' ++ srcDirs 'testng-collections/src/main/java' ++ } ++ resources { ++ srcDirs 'testng-core/src/main/resources' ++ } ++ } ++} ++ ++tasks { ++ jar { ++ manifest { ++ attributes( ++ "Specification-Title": rootProject.name, ++ "Specification-Version": version, ++ "Specification-Vendor" : rootProject.name, ++ "Implementation-Title" : rootProject.name, ++ "Implementation-Version": version, ++ "Implementation-Vendor" : rootProject.name, ++ "Implementation-Vendor-Id" : project.group, ++ "Implementation-Url": "https://testng.org", ++ "Automatic-Module-Name" : project.group, ++ "Bundle-ManifestVersion": "2", ++ "Bundle-Name": rootProject.name, ++ "Bundle-SymbolicName": project.group, ++ "Bundle-Vendor": rootProject.name, ++ "Bundle-License": "Apache-2.0", ++ "Bundle-Description": "Testing framework for Java", ++ "Bundle-Version": version, ++ "Import-Package" : """ ++ bsh.*;version="[2.0.03.0.0)";resolution:=optional, ++ com.beust.jcommander.*;version="[1.7.03.0.0)";resolution:=optional, ++ com.google.inject.*;version="[1.21.3)";resolution:=optional, ++ junit.framework;version="[3.8.1 5.0.0)";resolution:=optional, ++ org.junit.*;resolution:=optional, ++ org.apache.tools.ant.*;version="[1.7.0 2.0.0)";resolution:=optional, ++ org.yaml.*;version="[1.62.0)";resolution:=optional, ++ *;resolution:=optional ++ """.replace(" ", "").replace("\n", ""), ++ "Export-Package" : """ ++ org.testng, ++ org.testng.annotations, ++ org.testng.asserts, ++ org.testng.collections, ++ org.testng.internal, ++ org.testng.internal.annotations, ++ org.testng.internal.ant, ++ org.testng.internal.collections, ++ org.testng.internal.invokers, ++ org.testng.internal.invokers.objects, ++ org.testng.internal.junit, ++ org.testng.internal.objects, ++ org.testng.internal.objects.pojo, ++ org.testng.internal.reflect, ++ org.testng.internal.thread, ++ org.testng.internal.thread.graph, ++ org.testng.junit, ++ org.testng.log, ++ org.testng.log4testng, ++ org.testng.reporters, ++ org.testng.reporters.jq, ++ org.testng.reporters.util, ++ org.testng.thread, ++ org.testng.util, ++ org.testng.xml, ++ org.testng.xml.internal ++ """.replace(" ", "").replace("\n", " ") ++ ) ++ } ++ } ++} ++ ++publishing { ++ publications { ++ testng(MavenPublication) { ++ from(components.java) ++ } ++ } ++} +--- /dev/null ++++ b/settings.gradle +@@ -0,0 +1 @@ ++rootProject.name = 'testng' diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..09d92ef --- /dev/null +++ b/debian/patches/series @@ -0,0 +1 @@ +build-with-gradle.patch diff --git a/debian/rules b/debian/rules index 2d33f6a..901808d 100755 --- a/debian/rules +++ b/debian/rules @@ -1,4 +1,12 @@ #!/usr/bin/make -f +include /usr/share/dpkg/pkg-info.mk + +export DEB_VERSION_UPSTREAM + %: - dh $@ + dh $@ --buildsystem=gradle --with maven-repo-helper + +override_dh_auto_build: + mkdir -p src/main/java + dh_auto_build diff --git a/debian/source/lintian-overrides b/debian/source/lintian-overrides new file mode 100644 index 0000000..399d043 --- /dev/null +++ b/debian/source/lintian-overrides @@ -0,0 +1,5 @@ +# Override lintian warnings for jar files used in test cases +testng7: source-contains-prebuilt-java-object [testng-core/src/test/resources/serviceloader.jar] +testng7: source-contains-prebuilt-java-object [testng-core/src/test/resources/with-different-name-testng-xml.jar] +testng7: source-contains-prebuilt-java-object [testng-core/src/test/resources/withouttestngxml.jar] +testng7: source-contains-prebuilt-java-object [testng-core/src/test/resources/withtestngxml.jar] \ No newline at end of file diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000..1d83a4b --- /dev/null +++ b/debian/watch @@ -0,0 +1,8 @@ +version=4 +opts=\ +compression=gzip,\ +repack,\ +repacksuffix=~us1,\ +dversionmangle=s/(\d)[\+\.](?:dfsg|debian|ds)\.?\d*$/$1/ \ +https://github.com/cbeust/testng/tags \ +.*/([\d\.]+)\.tar\.gz debian diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..dba9f5f --- /dev/null +++ b/gradle.properties @@ -0,0 +1,23 @@ +org.gradle.parallel=true +systemProp.org.gradle.kotlin.dsl.precompiled.accessors.strict=true +kotlin.code.style=official +# See https://kotlinlang.org/docs/gradle.html#dependency-on-the-standard-library +# Note: testng.kotlin-library.gradle.kts adds kotlin-stdlib for testImplementation +kotlin.stdlib.default.dependency=false + +testng.version=7.5 + +group=org.testng + +project.name=TestNG +project.url=https\://testng.org +project.vendor.name=TestNG +project.vendor.id=org.testng + +# For now this URL is used only in POM references, and release tags are pused +scm.url=https\://github.com/cbeust/testng.git + +# In most cases it is the same as the project group +nexus.profile=org.testng +github.organization=cbeust +github.repository=testng diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..4f906e0 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..ac1b06f --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..99b7be8 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,41 @@ +pluginManagement { + includeBuild("build-logic") +} + +rootProject.name = "testng-root" + +plugins { + `gradle-enterprise` + id("de.fayard.refreshVersions") version "0.10.0" +} + +gradleEnterprise { + buildScan { + termsOfServiceUrl = "https://gradle.com/terms-of-service" + termsOfServiceAgree = "yes" + } +} + +// Sorted by name +include(":testng") +include(":testng-ant") +include(":testng-api") +include(":testng-asserts") +include(":testng-bom") +include(":testng-collections") +include(":testng-core") +include(":testng-core-api") +include(":testng-reflection-utils") +include(":testng-runner-api") +include(":testng-runner-junit4") +include(":testng-test-kit") +include(":testng-test-osgi") + +enableFeaturePreview("VERSION_CATALOGS") +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") + +for (project in rootProject.children) { + project.apply { + buildFileName = "${project.name}-build.gradle.kts" + } +} diff --git a/testng-ant/src/main/java/org/testng/TestNGAntTask.java b/testng-ant/src/main/java/org/testng/TestNGAntTask.java new file mode 100644 index 0000000..412b796 --- /dev/null +++ b/testng-ant/src/main/java/org/testng/TestNGAntTask.java @@ -0,0 +1,1108 @@ +package org.testng; + +import static java.lang.Boolean.TRUE; +import static org.testng.internal.Utils.isStringNotBlank; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.Enumeration; +import java.util.List; +import java.util.Properties; +import java.util.StringTokenizer; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Target; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.Execute; +import org.apache.tools.ant.taskdefs.ExecuteWatchdog; +import org.apache.tools.ant.taskdefs.LogOutputStream; +import org.apache.tools.ant.taskdefs.PumpStreamHandler; +import org.apache.tools.ant.types.Commandline; +import org.apache.tools.ant.types.CommandlineJava; +import org.apache.tools.ant.types.Environment; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.PropertySet; +import org.apache.tools.ant.types.Reference; +import org.apache.tools.ant.types.Resource; +import org.apache.tools.ant.types.ResourceCollection; +import org.apache.tools.ant.types.resources.FileResource; +import org.apache.tools.ant.types.selectors.FilenameSelector; +import org.testng.collections.Lists; +import org.testng.internal.ExitCode; +import org.testng.internal.Utils; +import org.testng.internal.ant.AntReporterConfig; +import org.testng.log4testng.Logger; +import org.testng.reporters.VerboseReporter; + +/** + * TestNG settings: + * + *

    + *
  • classfileset (inner) + *
  • classfilesetref (attribute) + *
  • xmlfileset (inner) + *
  • xmlfilesetref (attribute) + *
  • enableAssert (attribute) + *
  • excludedGroups (attribute) + *
  • groups (attribute) + *
  • junit (attribute) + *
  • listener (attribute) + *
  • outputdir (attribute) + *
  • parallel (attribute) + *
  • reporter (attribute) + *
  • sourcedir (attribute) + *
  • sourcedirref (attribute) + *
  • suitename (attribute) + *
  • suiterunnerclass (attribute) + *
  • target (attribute) + *
  • testjar (attribute) + *
  • testname (attribute) + *
  • threadcount (attribute) + *
  • dataproviderthreadcount (attribute) + *
  • verbose (attribute) + *
  • testrunfactory (attribute) + *
  • configFailurepolicy (attribute) + *
  • randomizeSuites (attribute) + *
  • methodselectors (attribute) + *
+ * + * Ant settings: + * + *
    + *
  • classpath (inner) + *
  • classpathref (attribute) + *
  • jvm (attribute) + *
  • workingDir (attribute) + *
  • env (inner) + *
  • sysproperty (inner) + *
  • propertyset (inner) + *
  • jvmarg (inner) + *
  • timeout (attribute) + *
  • haltonfailure (attribute) + *
  • onHaltTarget (attribute) + *
  • failureProperty (attribute) + *
  • haltonFSP (attribute) + *
  • FSPproperty (attribute) + *
  • haltonskipped (attribute) + *
  • skippedProperty (attribute) + *
  • testRunnerFactory (attribute) + *
+ * + * Debug information: + * + *
    + *
  • dumpCommand (boolean) + *
  • dumpEnv (boolean) + *
  • dumpSys (boolean) + *
+ * + * @author Alexandru Popescu + * @author Cedric Beust + * @author Lukas Jungmann + */ +public class TestNGAntTask extends Task { + + protected CommandlineJava m_javaCommand; + + protected List m_xmlFilesets = Lists.newArrayList(); + protected List m_classFilesets = Lists.newArrayList(); + protected File m_outputDir; + protected File m_testjar; + protected File m_workingDir; + private Integer m_timeout; + private List m_listeners = Lists.newArrayList(); + private List m_methodselectors = Lists.newArrayList(); + private String m_objectFactory; + protected String m_testRunnerFactory; + private boolean m_delegateCommandSystemProperties = false; + + protected Environment m_environment = new Environment(); + + /** The suite runner name (defaults to TestNG.class.getName(). */ + protected String m_mainClass = TestNG.class.getName(); + + /** + * True if the temporary file created by the Ant Task for command line parameters to TestNG should + * be preserved after execution. + */ + protected boolean m_dump; + + private boolean m_dumpEnv; + private boolean m_dumpSys; + + protected boolean m_assertEnabled = true; + protected boolean m_haltOnFailure; + protected String m_onHaltTarget; + protected String m_failurePropertyName; + protected boolean m_haltOnSkipped; + protected String m_skippedPropertyName; + protected boolean m_haltOnFSP; + protected String m_fspPropertyName; + protected String m_includedGroups; + protected String m_excludedGroups; + protected String m_parallelMode; + protected String m_threadCount; + protected String m_dataproviderthreadCount; + protected String m_configFailurePolicy; + protected Boolean m_randomizeSuites; + public String m_useDefaultListeners; + private String m_suiteName = "Ant suite"; + private String m_testName = "Ant test"; + private Boolean m_skipFailedInvocationCounts; + private String m_methods; + private Mode mode = Mode.testng; + private boolean forkJvm = true; + + public enum Mode { + // lower-case to better look in build scripts + testng, + junit, + mixed + } + + private static final Logger LOGGER = Logger.getLogger(TestNGAntTask.class); + + /** The list of report listeners added via <reporter> sub-element of the Ant task */ + private List reporterConfigs = Lists.newArrayList(); + + private String m_testNames = ""; + + public void setParallel(String parallel) { + m_parallelMode = parallel; + } + + public void setThreadCount(String threadCount) { + m_threadCount = threadCount; + } + + public void setDataProviderThreadCount(String dataproviderthreadCount) { + m_dataproviderthreadCount = dataproviderthreadCount; + } + + public void setUseDefaultListeners(String f) { + m_useDefaultListeners = f; + } + + // Ant task settings + public void setHaltonfailure(boolean value) { + m_haltOnFailure = value; + } + + public void setOnHaltTarget(String targetName) { + m_onHaltTarget = targetName; + } + + public void setFailureProperty(String propertyName) { + m_failurePropertyName = propertyName; + } + + public void setHaltonskipped(boolean value) { + m_haltOnSkipped = value; + } + + public void setSkippedProperty(String propertyName) { + m_skippedPropertyName = propertyName; + } + + public void setHaltonFSP(boolean value) { + m_haltOnFSP = value; + } + + public void setFSPProperty(String propertyName) { + m_fspPropertyName = propertyName; + } + + public void setDelegateCommandSystemProperties(boolean value) { + m_delegateCommandSystemProperties = value; + } + + /** + * @param verbose the flag to log the command line. When verbose is set to true the command line + * parameters are stored in a temporary file stored in the user's default temporary file + * directory. The file created is prefixed with "testng". + */ + public void setDumpCommand(boolean verbose) { + m_dump = verbose; + } + + /** + * Sets the flag to write on System.out the Ant Environment properties. + * + * @param verbose true for printing + */ + public void setDumpEnv(boolean verbose) { + m_dumpEnv = verbose; + } + + /** + * Sets te flag to write on System.out the system properties. + * + * @param verbose true for dumping the info + */ + public void setDumpSys(boolean verbose) { + m_dumpSys = verbose; + } + + public void setEnableAssert(boolean flag) { + m_assertEnabled = flag; + } + + /** + * The directory to invoke the VM in. + * + * @param workingDir the directory to invoke the JVM from. + */ + public void setWorkingDir(File workingDir) { + m_workingDir = workingDir; + } + + /** + * Sets a particular JVM to be used. Default is 'java' and is solved by Runtime.exec() + * . + * + * @param jvm the new jvm + */ + public void setJvm(String jvm) { + getJavaCommand().setVm(jvm); + } + + /** + * Set the timeout value (in milliseconds). + * + *

If the tests are running for more than this value, the tests will be canceled. + * + * @param value the maximum time (in milliseconds) allowed before declaring the test as + * 'timed-out' + */ + public void setTimeout(Integer value) { + m_timeout = value; + } + + public Commandline.Argument createJvmarg() { + return getJavaCommand().createVmArgument(); + } + + public void addSysproperty(Environment.Variable sysp) { + getJavaCommand().addSysproperty(sysp); + } + + /** + * Adds an environment variable; used when forking. + * + * @param var The variable + */ + public void addEnv(Environment.Variable var) { + m_environment.addVariable(var); + } + + /** + * Adds path to classpath used for tests. + * + * @return reference to the classpath in the embedded java command line + */ + public Path createClasspath() { + return getJavaCommand().createClasspath(getProject()).createPath(); + } + + /** + * Adds a path to the bootclasspath. + * + * @return reference to the bootclasspath in the embedded java command line + */ + public Path createBootclasspath() { + return getJavaCommand().createBootclasspath(getProject()).createPath(); + } + + /** + * Set the classpath to be used when running the Java class + * + * @param s an Ant Path object containing the classpath. + */ + public void setClasspath(Path s) { + createClasspath().append(s); + } + + /** + * Classpath to use, by reference. + * + * @param r a reference to an existing classpath + */ + public void setClasspathRef(Reference r) { + createClasspath().setRefid(r); + } + + public void addXmlfileset(FileSet fs) { + m_xmlFilesets.add(fs); + } + + public void setXmlfilesetRef(Reference ref) { + m_xmlFilesets.add(createResourceCollection(ref)); + } + + public void addClassfileset(FileSet fs) { + m_classFilesets.add(appendClassSelector(fs)); + } + + public void setClassfilesetRef(Reference ref) { + m_classFilesets.add(createResourceCollection(ref)); + } + + public void setTestNames(String testNames) { + m_testNames = testNames; + } + + /** + * Sets the suite runner class to invoke + * + * @param s the name of the suite runner class + */ + public void setSuiteRunnerClass(String s) { + m_mainClass = s; + } + + /** + * Sets the suite name + * + * @param s the name of the suite + */ + public void setSuiteName(String s) { + m_suiteName = s; + } + + /** + * Sets the test name + * + * @param s the name of the test + */ + public void setTestName(String s) { + m_testName = s; + } + + // TestNG settings + public void setJUnit(boolean value) { + mode = value ? Mode.junit : Mode.testng; + } + + // TestNG settings + public void setMode(Mode mode) { + this.mode = mode; + } + + public void setForkJvm(boolean forkJvm) { + this.forkJvm = forkJvm; + } + + /** + * Sets the test output directory + * + * @param dir the name of directory + */ + public void setOutputDir(File dir) { + m_outputDir = dir; + } + + /** + * Sets the test jar + * + * @param s the name of test jar + */ + public void setTestJar(File s) { + m_testjar = s; + } + + public void setGroups(String groups) { + m_includedGroups = groups; + } + + public void setExcludedGroups(String groups) { + m_excludedGroups = groups; + } + + private Integer m_verbose = null; + + private Integer m_suiteThreadPoolSize; + + private String m_xmlPathInJar; + + public void setVerbose(Integer verbose) { + m_verbose = verbose; + } + + public void setReporter(String listener) { + m_listeners.add(listener); + } + + public void setObjectFactory(String className) { + m_objectFactory = className; + } + + public void setTestRunnerFactory(String testRunnerFactory) { + m_testRunnerFactory = testRunnerFactory; + } + + public void setSuiteThreadPoolSize(Integer n) { + m_suiteThreadPoolSize = n; + } + + public void setListeners(String listeners) { + StringTokenizer st = new StringTokenizer(listeners, " ,"); + while (st.hasMoreTokens()) { + m_listeners.add(st.nextToken()); + } + } + + public void setMethodSelectors(String methodSelectors) { + StringTokenizer st = new StringTokenizer(methodSelectors, " ,"); + while (st.hasMoreTokens()) { + m_methodselectors.add(st.nextToken()); + } + } + + public void setConfigFailurePolicy(String failurePolicy) { + m_configFailurePolicy = failurePolicy; + } + + public void setRandomizeSuites(Boolean randomizeSuites) { + m_randomizeSuites = randomizeSuites; + } + + public void setMethods(String methods) { + m_methods = methods; + } + + /** + * Launches TestNG in a new JVM. + * + *

{@inheritDoc} + */ + @Override + public void execute() throws BuildException { + validateOptions(); + + CommandlineJava cmd = getJavaCommand(); + cmd.setClassname(m_mainClass); + if (m_assertEnabled) { + cmd.createVmArgument().setValue("-ea"); + } + if (m_delegateCommandSystemProperties) { + delegateCommandSystemProperties(); + } + List argv = createArguments(); + + if (!forkJvm) { + TestNG tng = TestNG.privateMain(argv.toArray(new String[0]), null); + actOnResult(tng.getStatus(), false); + return; + } + + String fileName = ""; + FileWriter fw = null; + BufferedWriter bw = null; + try { + File f = File.createTempFile("testng", ""); + fileName = f.getAbsolutePath(); + + // If the user asked to see the command, preserve the file + if (!m_dump) { + f.deleteOnExit(); + } + fw = new FileWriter(f); + bw = new BufferedWriter(fw); + for (String arg : argv) { + bw.write(arg); + bw.newLine(); + } + bw.flush(); + } catch (IOException e) { + LOGGER.error(e.getMessage(), e); + } finally { + try { + if (bw != null) { + bw.close(); + } + if (fw != null) { + fw.close(); + } + } catch (IOException e) { + LOGGER.error(e.getMessage(), e); + } + } + + printDebugInfo(fileName); + + createClasspath().setLocation(findJar()); + + cmd.createArgument().setValue("@" + fileName); + + ExecuteWatchdog watchdog = createWatchdog(); + boolean wasKilled = false; + int exitValue = executeAsForked(cmd, watchdog); + if (null != watchdog) { + wasKilled = watchdog.killedProcess(); + } + + actOnResult(exitValue, wasKilled); + } + + protected List createArguments() { + List argv = Lists.newArrayList(); + addBooleanIfTrue(argv, CommandLineArgs.JUNIT, mode == Mode.junit); + addBooleanIfTrue(argv, CommandLineArgs.MIXED, mode == Mode.mixed); + addBooleanIfTrue( + argv, CommandLineArgs.SKIP_FAILED_INVOCATION_COUNTS, m_skipFailedInvocationCounts); + addIntegerIfNotNull(argv, CommandLineArgs.LOG, m_verbose); + addDefaultListeners(argv); + addOutputDir(argv); + addFileIfFile(argv, CommandLineArgs.TEST_JAR, m_testjar); + addStringIfNotBlank(argv, CommandLineArgs.GROUPS, m_includedGroups); + addStringIfNotBlank(argv, CommandLineArgs.EXCLUDED_GROUPS, m_excludedGroups); + addFilesOfRCollection(argv, CommandLineArgs.TEST_CLASS, m_classFilesets); + addListOfStringIfNotEmpty(argv, CommandLineArgs.LISTENER, m_listeners); + addListOfStringIfNotEmpty(argv, CommandLineArgs.METHOD_SELECTORS, m_methodselectors); + addStringIfNotNull(argv, CommandLineArgs.OBJECT_FACTORY, m_objectFactory); + addStringIfNotNull(argv, CommandLineArgs.TEST_RUNNER_FACTORY, m_testRunnerFactory); + addStringIfNotNull(argv, CommandLineArgs.PARALLEL, m_parallelMode); + addStringIfNotNull(argv, CommandLineArgs.CONFIG_FAILURE_POLICY, m_configFailurePolicy); + addBooleanIfTrue(argv, CommandLineArgs.RANDOMIZE_SUITES, m_randomizeSuites); + addStringIfNotNull(argv, CommandLineArgs.THREAD_COUNT, m_threadCount); + addStringIfNotNull(argv, CommandLineArgs.DATA_PROVIDER_THREAD_COUNT, m_dataproviderthreadCount); + addStringIfNotBlank(argv, CommandLineArgs.SUITE_NAME, m_suiteName); + addStringIfNotBlank(argv, CommandLineArgs.TEST_NAME, m_testName); + addStringIfNotBlank(argv, CommandLineArgs.TEST_NAMES, m_testNames); + addStringIfNotBlank(argv, CommandLineArgs.METHODS, m_methods); + addReporterConfigs(argv); + addIntegerIfNotNull(argv, CommandLineArgs.SUITE_THREAD_POOL_SIZE, m_suiteThreadPoolSize); + addStringIfNotNull(argv, CommandLineArgs.XML_PATH_IN_JAR, m_xmlPathInJar); + addXmlFiles(argv); + return argv; + } + + private void addDefaultListeners(List argv) { + if (m_useDefaultListeners != null) { + String useDefaultListeners = "false"; + if ("yes".equalsIgnoreCase(m_useDefaultListeners) + || "true".equalsIgnoreCase(m_useDefaultListeners)) { + useDefaultListeners = "true"; + } + argv.add(CommandLineArgs.USE_DEFAULT_LISTENERS); + argv.add(useDefaultListeners); + } + } + + private void addOutputDir(List argv) { + if (null != m_outputDir) { + if (!m_outputDir.exists()) { + m_outputDir.mkdirs(); + } + if (m_outputDir.isDirectory()) { + argv.add(CommandLineArgs.OUTPUT_DIRECTORY); + argv.add(m_outputDir.getAbsolutePath()); + } else { + throw new BuildException("Output directory is not a directory: " + m_outputDir); + } + } + } + + private void addReporterConfigs(List argv) { + for (AntReporterConfig reporterConfig : reporterConfigs) { + argv.add(CommandLineArgs.REPORTER); + argv.add(reporterConfig.serialize()); + } + } + + private void addFilesOfRCollection( + List argv, String name, List resources) { + addArgumentsIfNotEmpty(argv, name, getFiles(resources), ","); + } + + private void addListOfStringIfNotEmpty(List argv, String name, List arguments) { + addArgumentsIfNotEmpty(argv, name, arguments, ";"); + } + + private void addArgumentsIfNotEmpty( + List argv, String name, List arguments, String separator) { + if (arguments != null && !arguments.isEmpty()) { + argv.add(name); + String value = Utils.join(arguments, separator); + argv.add(value); + } + } + + private void addFileIfFile(List argv, String name, File file) { + if ((null != file) && file.isFile()) { + argv.add(name); + argv.add(file.getAbsolutePath()); + } + } + + private void addBooleanIfTrue(List argv, String name, Boolean value) { + if (TRUE.equals(value)) { + argv.add(name); + } + } + + private void addIntegerIfNotNull(List argv, String name, Integer value) { + if (value != null) { + argv.add(name); + argv.add(value.toString()); + } + } + + private void addStringIfNotNull(List argv, String name, String value) { + if (value != null) { + argv.add(name); + argv.add(value); + } + } + + private void addStringIfNotBlank(List argv, String name, String value) { + if (isStringNotBlank(value)) { + argv.add(name); + argv.add(value); + } + } + + private void addXmlFiles(List argv) { + for (String file : getSuiteFileNames()) { + argv.add(file); + } + } + + /** @return the list of the XML file names. This method can be overridden by subclasses. */ + protected List getSuiteFileNames() { + List result = Lists.newArrayList(); + + for (String file : getFiles(m_xmlFilesets)) { + result.add(file); + } + + return result; + } + + private void delegateCommandSystemProperties() { + // Iterate over command-line args and pass them through as sysproperty + // exclude any built-in properties that start with "ant." + for (Object propKey : getProject().getUserProperties().keySet()) { + String propName = (String) propKey; + String propVal = getProject().getUserProperty(propName); + if (propName.startsWith("ant.")) { + log("Excluding ant property: " + propName + ": " + propVal, Project.MSG_DEBUG); + } else { + log("Including user property: " + propName + ": " + propVal, Project.MSG_DEBUG); + Environment.Variable var = new Environment.Variable(); + var.setKey(propName); + var.setValue(propVal); + addSysproperty(var); + } + } + } + + private void printDebugInfo(String fileName) { + if (m_dumpSys) { + debug("* SYSTEM PROPERTIES *"); + Properties props = System.getProperties(); + Enumeration en = props.propertyNames(); + while (en.hasMoreElements()) { + String key = (String) en.nextElement(); + debug(key + ": " + props.getProperty(key)); + } + debug(""); + } + if (m_dumpEnv) { + String[] vars = m_environment.getVariables(); + if (null != vars && vars.length > 0) { + debug("* ENVIRONMENT *"); + for (String v : vars) { + debug(v); + } + debug(""); + } + } + if (m_dump) { + dumpCommand(fileName); + } + } + + private void debug(String message) { + log("[TestNGAntTask] " + message, Project.MSG_DEBUG); + } + + protected void actOnResult(int exitValue, boolean wasKilled) { + if (exitValue == -1) { + executeHaltTarget(exitValue); + throw new BuildException("an error occurred when running TestNG tests"); + } + + if ((exitValue & ExitCode.HAS_NO_TEST) == ExitCode.HAS_NO_TEST) { + if (m_haltOnFailure) { + executeHaltTarget(exitValue); + throw new BuildException("No tests were run"); + } else { + if (null != m_failurePropertyName) { + getProject().setNewProperty(m_failurePropertyName, "true"); + } + + log("TestNG haven't found any tests to be run", Project.MSG_DEBUG); + } + } + + boolean failed = (ExitCode.hasFailure(exitValue)) || wasKilled; + if (failed) { + final String msg = wasKilled ? "The tests timed out and were killed." : "The tests failed."; + if (m_haltOnFailure) { + executeHaltTarget(exitValue); + throw new BuildException(msg); + } else { + if (null != m_failurePropertyName) { + getProject().setNewProperty(m_failurePropertyName, "true"); + } + + log(msg, Project.MSG_INFO); + } + } + + if (ExitCode.hasSkipped(exitValue)) { + if (m_haltOnSkipped) { + executeHaltTarget(exitValue); + throw new BuildException("There are TestNG SKIPPED tests"); + } else { + if (null != m_skippedPropertyName) { + getProject().setNewProperty(m_skippedPropertyName, "true"); + } + + log("There are TestNG SKIPPED tests", Project.MSG_DEBUG); + } + } + + if (ExitCode.hasFailureWithinSuccessPercentage(exitValue)) { + if (m_haltOnFSP) { + executeHaltTarget(exitValue); + throw new BuildException("There are TestNG FAILED WITHIN SUCCESS PERCENTAGE tests"); + } else { + if (null != m_fspPropertyName) { + getProject().setNewProperty(m_fspPropertyName, "true"); + } + + log("There are TestNG FAILED WITHIN SUCCESS PERCENTAGE tests", Project.MSG_DEBUG); + } + } + } + + /** Executes the target, if any, that user designates executing before failing the test */ + private void executeHaltTarget(int exitValue) { + if (m_onHaltTarget != null) { + if (m_outputDir != null) { + getProject().setProperty("testng.outputdir", m_outputDir.getAbsolutePath()); + } + getProject().setProperty("testng.returncode", String.valueOf(exitValue)); + Target t = getProject().getTargets().get(m_onHaltTarget); + if (t != null) { + t.execute(); + } + } + } + + /** + * Executes the command line as a new process. + * + * @param cmd the command to execute + * @param watchdog - A {@link ExecuteWatchdog} object. + * @return the exit status of the subprocess or INVALID. + */ + protected int executeAsForked(CommandlineJava cmd, ExecuteWatchdog watchdog) { + Execute execute = + new Execute( + new TestNGLogSH( + this, Project.MSG_INFO, Project.MSG_WARN, (m_verbose == null || m_verbose < 5)), + watchdog); + execute.setCommandline(cmd.getCommandline()); + execute.setAntRun(getProject()); + if (m_workingDir != null) { + if (m_workingDir.exists() && m_workingDir.isDirectory()) { + execute.setWorkingDirectory(m_workingDir); + } else { + log("Ignoring invalid working directory : " + m_workingDir, Project.MSG_WARN); + } + } + + String[] environment = m_environment.getVariables(); + if (null != environment) { + for (String envEntry : environment) { + log("Setting environment variable: " + envEntry, Project.MSG_VERBOSE); + } + } + + execute.setEnvironment(environment); + + log(cmd.describeCommand(), Project.MSG_VERBOSE); + int retVal; + try { + retVal = execute.execute(); + } catch (IOException e) { + throw new BuildException("Process fork failed.", e, getLocation()); + } + + return retVal; + } + + /** @return the created (or create) the CommandlineJava. */ + protected CommandlineJava getJavaCommand() { + if (null == m_javaCommand) { + m_javaCommand = new CommandlineJava(); + } + + return m_javaCommand; + } + + /** + * @return null if there is no timeout value, otherwise the watchdog instance. + * @throws BuildException under unspecified circumstances + * @since Ant 1.2 + */ + protected ExecuteWatchdog createWatchdog() /*throws BuildException*/ { + if (m_timeout == null) { + return null; + } + + return new ExecuteWatchdog(m_timeout.longValue()); + } + + protected void validateOptions() throws BuildException { + int suiteCount = getSuiteFileNames().size(); + if (suiteCount == 0 + && m_classFilesets.size() == 0 + && Utils.isStringEmpty(m_methods) + && ((null == m_testjar) || !m_testjar.isFile())) { + throw new BuildException("No suites, classes, methods or jar file was specified."); + } + + if ((null != m_includedGroups) && (m_classFilesets.size() == 0 && suiteCount == 0)) { + throw new BuildException("No class filesets or xml file sets specified while using groups"); + } + + if (m_onHaltTarget != null) { + if (!getProject().getTargets().containsKey(m_onHaltTarget)) { + throw new BuildException("Target " + m_onHaltTarget + " not found in this project"); + } + } + } + + private ResourceCollection createResourceCollection(Reference ref) { + Object o = ref.getReferencedObject(); + if (!(o instanceof ResourceCollection)) { + throw new BuildException("Only File based ResourceCollections are supported."); + } + ResourceCollection rc = (ResourceCollection) o; + if (!rc.isFilesystemOnly()) { + throw new BuildException("Only ResourceCollections from local file system are supported."); + } + return rc; + } + + private FileSet appendClassSelector(FileSet fs) { + FilenameSelector selector = new FilenameSelector(); + selector.setName("**/*.class"); + selector.setProject(getProject()); + fs.appendSelector(selector); + + return fs; + } + + private File findJar() { + Class thisClass = getClass(); + String resource = thisClass.getName().replace('.', '/') + ".class"; + URL url = thisClass.getClassLoader().getResource(resource); + + if (null != url) { + String u = url.toString(); + if (u.startsWith("jar:file:")) { + int pling = u.indexOf("!"); + String jarName = u.substring(4, pling); + + return new File(fromURI(jarName)); + } else if (u.startsWith("file:")) { + int tail = u.indexOf(resource); + String dirName = u.substring(0, tail); + + return new File(fromURI(dirName)); + } + } + + return null; + } + + private String fromURI(String uri) { + URL url = null; + try { + url = new URL(uri); + } catch (MalformedURLException murle) { + // Gobble exceptions and do nothing. + } + if ((null == url) || !("file".equals(url.getProtocol()))) { + throw new IllegalArgumentException("Can only handle valid file: URIs"); + } + + StringBuilder buf = new StringBuilder(url.getHost()); + if (buf.length() > 0) { + buf.insert(0, File.separatorChar).insert(0, File.separatorChar); + } + + String file = url.getFile(); + int queryPos = file.indexOf('?'); + buf.append((queryPos < 0) ? file : file.substring(0, queryPos)); + + uri = buf.toString().replace('/', File.separatorChar); + + if ((File.pathSeparatorChar == ';') + && uri.startsWith("\\") + && (uri.length() > 2) + && Character.isLetter(uri.charAt(1)) + && (uri.lastIndexOf(':') > -1)) { + uri = uri.substring(1); + } + + StringBuilder sb = new StringBuilder(); + CharacterIterator iter = new StringCharacterIterator(uri); + for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) { + if (c == '%') { + char c1 = iter.next(); + if (c1 != CharacterIterator.DONE) { + int i1 = Character.digit(c1, 16); + char c2 = iter.next(); + if (c2 != CharacterIterator.DONE) { + int i2 = Character.digit(c2, 16); + sb.append((char) ((i1 << 4) + i2)); + } + } + } else { + sb.append(c); + } + } + + return sb.toString(); + } + + /** + * Returns the list of files corresponding to the resource collection + * + * @param resources - A list of {@link ResourceCollection} + * @return the list of files corresponding to the resource collection + * @throws BuildException + */ + private List getFiles(List resources) throws BuildException { + List files = Lists.newArrayList(); + for (ResourceCollection rc : resources) { + for (Resource o : rc) { + if (o instanceof FileResource) { + FileResource fr = ((FileResource) o); + if (fr.isDirectory()) { + throw new BuildException("Directory based FileResources are not supported."); + } + if (!fr.isExists()) { + log("'" + fr.toLongString() + "' does not exist", Project.MSG_VERBOSE); + } + files.add(fr.getFile().getAbsolutePath()); + } else { + log("Unsupported Resource type: " + o.toString(), Project.MSG_VERBOSE); + } + } + } + return files; + } + + private void dumpCommand(String fileName) { + log("TESTNG PASSED @" + fileName + " WHICH CONTAINS:", Project.MSG_INFO); + readAndPrintFile(fileName); + } + + private void readAndPrintFile(String fileName) { + try { + Files.readAllLines(Paths.get(fileName)).forEach(line -> log(" " + line, Project.MSG_INFO)); + } catch (IOException ex) { + LOGGER.error(ex.getMessage(), ex); + } + } + + public void addConfiguredReporter(AntReporterConfig reporterConfig) { + reporterConfigs.add(reporterConfig); + } + + public void setSkipFailedInvocationCounts(boolean skip) { + m_skipFailedInvocationCounts = skip; + } + + public void setXmlPathInJar(String path) { + m_xmlPathInJar = path; + } + /** + * Add the referenced property set as system properties for the TestNG JVM. + * + * @param sysPropertySet A PropertySet of system properties. + */ + public void addConfiguredPropertySet(PropertySet sysPropertySet) { + Properties properties = sysPropertySet.getProperties(); + log( + properties.keySet().size() + " properties found in nested propertyset", + Project.MSG_VERBOSE); + for (Object propKeyObj : properties.keySet()) { + String propKey = (String) propKeyObj; + Environment.Variable sysProp = new Environment.Variable(); + sysProp.setKey(propKey); + if (properties.get(propKey) instanceof String) { + String propVal = (String) properties.get(propKey); + sysProp.setValue(propVal); + getJavaCommand().addSysproperty(sysProp); + log("Added system property " + propKey + " with value " + propVal, Project.MSG_VERBOSE); + } else { + log("Ignoring non-String property " + propKey, Project.MSG_WARN); + } + } + } + + @Override + protected void handleOutput(String output) { + if (output.startsWith(VerboseReporter.LISTENER_PREFIX)) { + // send everything from VerboseReporter to verbose level unless log level is > 4 + log(output, m_verbose < 5 ? Project.MSG_VERBOSE : Project.MSG_INFO); + } else { + super.handleOutput(output); + } + } + + private static class TestNGLogOS extends LogOutputStream { + + private Task task; + private boolean verbose; + + public TestNGLogOS(Task task, int level, boolean verbose) { + super(task, level); + this.task = task; + this.verbose = verbose; + } + + @Override + protected void processLine(String line, int level) { + if (line.startsWith(VerboseReporter.LISTENER_PREFIX)) { + task.log(line, verbose ? Project.MSG_VERBOSE : Project.MSG_INFO); + } else { + super.processLine(line, level); + } + } + } + + protected static class TestNGLogSH extends PumpStreamHandler { + + public TestNGLogSH(Task task, int outlevel, int errlevel, boolean verbose) { + super(new TestNGLogOS(task, outlevel, verbose), new LogOutputStream(task, errlevel)); + } + } +} diff --git a/testng-ant/src/test/java/test/ant/AntTest.java b/testng-ant/src/test/java/test/ant/AntTest.java new file mode 100644 index 0000000..d096be9 --- /dev/null +++ b/testng-ant/src/test/java/test/ant/AntTest.java @@ -0,0 +1,29 @@ +package test.ant; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.regex.Pattern; +import org.apache.tools.ant.BuildFileRule; +import org.testng.annotations.Test; + +public class AntTest { + + private final BuildFileRule rule = new BuildFileRule(); + + @Test + public void testSimple() { + rule.configureProject("src/test/resources/ant/build-simple.xml"); + rule.executeTarget("testng"); + String expectedText = "Total tests run: 1, Passes: 1, Failures: 0, Skips: 0"; + assertThat(rule.getLog()).containsPattern(Pattern.compile(expectedText)); + } + + @Test + public void testReporter() { + MyReporter.expectedFilter = "*insert*"; + MyReporter.expectedFiltering = true; + + rule.configureProject("src/test/resources/ant/build-reporter-config.xml"); + rule.executeTarget("testng"); + } +} diff --git a/testng-ant/src/test/java/test/ant/MyReporter.java b/testng-ant/src/test/java/test/ant/MyReporter.java new file mode 100644 index 0000000..5d858a1 --- /dev/null +++ b/testng-ant/src/test/java/test/ant/MyReporter.java @@ -0,0 +1,21 @@ +package test.ant; + +import org.testng.IReporter; + +public class MyReporter implements IReporter { + + public static String expectedFilter; + public static boolean expectedFiltering; + + public void setMethodFilter(String filter) { + if (!filter.equals(expectedFilter)) { + throw new IllegalArgumentException("Expect filter: " + expectedFilter); + } + } + + public void setEnableFiltering(boolean enableFiltering) { + if (enableFiltering != expectedFiltering) { + throw new IllegalArgumentException("Expect filtering: " + expectedFiltering); + } + } +} diff --git a/testng-ant/src/test/java/test/ant/SimpleSample.java b/testng-ant/src/test/java/test/ant/SimpleSample.java new file mode 100644 index 0000000..0802c59 --- /dev/null +++ b/testng-ant/src/test/java/test/ant/SimpleSample.java @@ -0,0 +1,12 @@ +package test.ant; + +import org.testng.Assert; +import org.testng.annotations.Test; + +public class SimpleSample { + + @Test + public void test() { + Assert.assertTrue(true); + } +} diff --git a/testng-ant/src/test/resources/ant/build-reporter-config.xml b/testng-ant/src/test/resources/ant/build-reporter-config.xml new file mode 100644 index 0000000..e69c981 --- /dev/null +++ b/testng-ant/src/test/resources/ant/build-reporter-config.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/testng-ant/src/test/resources/ant/build-simple.xml b/testng-ant/src/test/resources/ant/build-simple.xml new file mode 100644 index 0000000..83c9d08 --- /dev/null +++ b/testng-ant/src/test/resources/ant/build-simple.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/testng-ant/testng-ant-build.gradle.kts b/testng-ant/testng-ant-build.gradle.kts new file mode 100644 index 0000000..f918ec8 --- /dev/null +++ b/testng-ant/testng-ant-build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("testng.java-library") +} + +dependencies { + api("org.apache.ant:ant:_") + + implementation(projects.testngCore) + testImplementation(projects.testngAsserts) + testImplementation("org.apache.ant:ant-testutil:_") +} diff --git a/testng-api/testng-api-build.gradle.kts b/testng-api/testng-api-build.gradle.kts new file mode 100644 index 0000000..ce14b05 --- /dev/null +++ b/testng-api/testng-api-build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("testng.java-platform") +} + +// This is a convenience artifact to add dependencies to all the API jars +javaPlatform.allowDependencies() + +dependencies { + api(projects.testngAsserts) + api(projects.testngCoreApi) +} diff --git a/testng-asserts/src/main/java/org/testng/Assert.java b/testng-asserts/src/main/java/org/testng/Assert.java new file mode 100644 index 0000000..315e5b7 --- /dev/null +++ b/testng-asserts/src/main/java/org/testng/Assert.java @@ -0,0 +1,2213 @@ +package org.testng; + +import static org.testng.internal.EclipseInterface.ASSERT_EQUAL_LEFT; +import static org.testng.internal.EclipseInterface.ASSERT_LEFT2; +import static org.testng.internal.EclipseInterface.ASSERT_MIDDLE; +import static org.testng.internal.EclipseInterface.ASSERT_RIGHT; +import static org.testng.internal.EclipseInterface.ASSERT_UNEQUAL_LEFT; + +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import org.testng.collections.Lists; + +/** + * Assertion tool class. Presents assertion methods with a more natural parameter order. The order + * is always actualValue, expectedValue [, message]. + * + *

Important: Assertion methods comparing two values for equality, such as {@code + * assertEquals}, are only intended to test equality for an actual and an (un-)expected + * result value. They are not designed for testing whether a class correctly implements {@code + * equals(Object)}. For example {@code assertEquals} might return fast when provided with the same + * object as actual and expected value without calling {@code equals(Object)} at all. Such tests + * trying to verify the {@code equals(Object)} implementation should instead be written explicitly + * and {@link #assertTrue(boolean) assertTrue} or {@link #assertFalse(boolean) assertFalse} should + * be used to verify the result, e.g.: {@code assertTrue(var.equals(var))}, {@code + * assertFalse(var.equals(null))}. + * + * @author Alexandru Popescu + */ +public class Assert { + + public static final String ARRAY_MISMATCH_TEMPLATE = + "arrays differ firstly at element [%d]; " + "expected value is <%s> but was <%s>. %s"; + + /** Protect constructor since it is a static only class */ + protected Assert() { + // hide constructor + } + + /** + * Asserts that a condition is true. If it isn't, an AssertionError, with the given message, is + * thrown. + * + * @param condition the condition to evaluate + * @param message the assertion error message + */ + public static void assertTrue(boolean condition, String message) { + if (!condition) { + failNotEquals(condition, Boolean.TRUE, message); + } + } + + /** + * Asserts that a condition is true. If it isn't, an AssertionError is thrown. + * + * @param condition the condition to evaluate + */ + public static void assertTrue(boolean condition) { + assertTrue(condition, null); + } + + /** + * Asserts that a condition is false. If it isn't, an AssertionError, with the given message, is + * thrown. + * + * @param condition the condition to evaluate + * @param message the assertion error message + */ + public static void assertFalse(boolean condition, String message) { + if (condition) { + failNotEquals(condition, Boolean.FALSE, message); // TESTNG-81 + } + } + + /** + * Asserts that a condition is false. If it isn't, an AssertionError is thrown. + * + * @param condition the condition to evaluate + */ + public static void assertFalse(boolean condition) { + assertFalse(condition, null); + } + + /** + * Fails a test with the given message and wrapping the original exception. + * + * @param message the assertion error message + * @param realCause the original exception + */ + public static void fail(String message, Throwable realCause) { + AssertionError ae = new AssertionError(message); + ae.initCause(realCause); + + throw ae; + } + + /** + * Fails a test with the given message. + * + * @param message the assertion error message + */ + public static void fail(String message) { + throw new AssertionError(message); + } + + /** Fails a test with no message. */ + public static void fail() { + fail(null); + } + + /** + * Asserts that two objects are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(Object actual, Object expected, String message) { + if (expected != null && expected.getClass().isArray()) { + assertArrayEquals(actual, expected, message); + return; + } + assertEqualsImpl(actual, expected, message); + } + + private static boolean areEqual(Object actual, Object expected) { + if (expected != null && expected.getClass().isArray()) { + return areArraysEqual(actual, expected); + } + return areEqualImpl(actual, expected); + } + + /** + * Differs from {@link #assertEquals(Object, Object, String)} by not taking arrays into special + * consideration hence comparing them by reference. Intended to be called directly to test + * equality of collections content. + */ + private static void assertEqualsImpl(Object actual, Object expected, String message) { + boolean equal = areEqualImpl(actual, expected); + if (!equal) { + failNotEquals(actual, expected, message); + } + } + + private static void assertNotEqualsImpl(Object actual, Object expected, String message) { + boolean notEqual = areNotEqualImpl(actual, expected); + if (!notEqual) { + failEquals(actual, expected, message); + } + } + + private static boolean areNotEqualImpl(Object actual, Object expected) { + if (expected == null) { + return actual != null; + } + // expected != null && actual == null + if (actual == null) { + return true; + } + return !expected.equals(actual); + } + + private static boolean areEqualImpl(Object actual, Object expected) { + if ((expected == null) && (actual == null)) { + return true; + } + // Only one of them is null + if (expected == null || actual == null) { + return false; + } + return expected.equals(actual) && actual.equals(expected); + } + + /** returns not equal reason or null if equal */ + private static String getArrayNotEqualReason(Object actual, Object expected) { + if (Objects.equals(actual, expected)) { + return null; + } + if (null == expected) { + return "expected a null array, but not null found"; + } + if (null == actual) { + return "expected not null array, but null found"; + } + if (!actual.getClass().isArray()) { + return "not an array"; + } + int expectedLength = Array.getLength(expected); + if (expectedLength != Array.getLength(actual)) { + return "array lengths are not the same"; + } + for (int i = 0; i < expectedLength; i++) { + Object _actual = Array.get(actual, i); + Object _expected = Array.get(expected, i); + if (!areEqual(_actual, _expected)) { + return "(values at index " + i + " are not the same)"; + } + } + return null; + } + + private static boolean areArraysEqual(Object actual, Object expected) { + return getArrayNotEqualReason(actual, expected) == null; + } + + private static void assertArrayEquals(Object actual, Object expected, String message) { + String reason = getArrayNotEqualReason(actual, expected); + if (null != reason) { + failNotEquals(actual, expected, message == null ? "" : message + " (" + message + ")"); + } + } + + private static void assertArrayNotEquals(Object actual, Object expected, String message) { + String reason = getArrayNotEqualReason(actual, expected); + if (null == reason) { + failEquals(actual, expected, message); + } + } + + /** + * Asserts that two arrays contain the same elements in the same order. If they do not, an + * AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(byte[] actual, byte[] expected) { + assertEquals(actual, expected, ""); + } + + /** + * Asserts that two arrays contain the same elements in the same order. If they do not, an + * AssertionError, with the given message, is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(byte[] actual, byte[] expected, String message) { + if (checkRefEqualityAndLength(actual, expected, message)) { + return; + } + + for (int i = 0; i < expected.length; i++) { + if (expected[i] != actual[i]) { + fail( + String.format( + ARRAY_MISMATCH_TEMPLATE, + i, + Byte.toString(expected[i]), + Byte.toString(actual[i]), + message)); + } + } + } + + /** + * Asserts that two arrays contain the same elements in the same order. If they do not, an + * AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(short[] actual, short[] expected) { + assertEquals(actual, expected, ""); + } + + /** + * Asserts that two arrays contain the same elements in the same order. If they do not, an + * AssertionError, with the given message, is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(short[] actual, short[] expected, String message) { + if (checkRefEqualityAndLength(actual, expected, message)) { + return; + } + + for (int i = 0; i < expected.length; i++) { + if (expected[i] != actual[i]) { + fail( + String.format( + ARRAY_MISMATCH_TEMPLATE, + i, + Short.toString(expected[i]), + Short.toString(actual[i]), + message)); + } + } + } + + /** + * Asserts that two arrays contain the same elements in the same order. If they do not, an + * AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(int[] actual, int[] expected) { + assertEquals(actual, expected, ""); + } + + /** + * Asserts that two arrays contain the same elements in the same order. If they do not, an + * AssertionError, with the given message, is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(int[] actual, int[] expected, String message) { + if (checkRefEqualityAndLength(actual, expected, message)) { + return; + } + + for (int i = 0; i < expected.length; i++) { + if (expected[i] != actual[i]) { + fail( + String.format( + ARRAY_MISMATCH_TEMPLATE, + i, + Integer.toString(expected[i]), + Integer.toString(actual[i]), + message)); + } + } + } + + /** + * Asserts that two arrays contain the same elements in the same order. If they do not, an + * AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(boolean[] actual, boolean[] expected) { + assertEquals(actual, expected, ""); + } + + /** + * Asserts that two arrays contain the same elements in the same order. If they do not, an + * AssertionError, with the given message, is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(boolean[] actual, boolean[] expected, String message) { + if (checkRefEqualityAndLength(actual, expected, message)) { + return; + } + + for (int i = 0; i < expected.length; i++) { + if (expected[i] != actual[i]) { + fail( + String.format( + ARRAY_MISMATCH_TEMPLATE, + i, + Boolean.toString(expected[i]), + Boolean.toString(actual[i]), + message)); + } + } + } + + /** + * Asserts that two arrays contain the same elements in the same order. If they do not, an + * AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(char[] actual, char[] expected) { + assertEquals(actual, expected, ""); + } + + /** + * Asserts that two arrays contain the same elements in the same order. If they do not, an + * AssertionError, with the given message, is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(char[] actual, char[] expected, String message) { + if (checkRefEqualityAndLength(actual, expected, message)) { + return; + } + + for (int i = 0; i < expected.length; i++) { + if (expected[i] != actual[i]) { + fail( + String.format( + ARRAY_MISMATCH_TEMPLATE, + i, + Character.toString(expected[i]), + Character.toString(actual[i]), + message)); + } + } + } + + /** + * Asserts that two arrays contain the same elements in the same order. If they do not, an + * AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(float[] actual, float[] expected) { + assertEquals(actual, expected, ""); + } + + /** + * Asserts that two arrays contain the same elements in the same order. If they do not, an + * AssertionError, with the given message, is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(float[] actual, float[] expected, String message) { + if (checkRefEqualityAndLength(actual, expected, message)) { + return; + } + + for (int i = 0; i < expected.length; i++) { + assertEquals( + actual[i], + expected[i], + String.format( + ARRAY_MISMATCH_TEMPLATE, + i, + Float.toString(expected[i]), + Float.toString(actual[i]), + message)); + } + } + + /** + * Asserts that two arrays contain the equal elements concerning a delta in the same order. If + * they do not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param delta the absolute tolerable difference between the actual and expected values + */ + public static void assertEquals(float[] actual, float[] expected, float delta) { + assertEquals(actual, expected, delta, ""); + } + + /** + * Asserts that two arrays contain the equal elements concerning a delta in the same order. If + * they do not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param delta the absolute tolerable difference between the actual and expected values + * @param message the assertion error message + */ + public static void assertEquals(float[] actual, float[] expected, float delta, String message) { + if (checkRefEqualityAndLength(actual, expected, message)) { + return; + } + + for (int i = 0; i < expected.length; i++) { + assertEquals( + actual[i], + expected[i], + delta, + String.format( + ARRAY_MISMATCH_TEMPLATE, + i, + Float.toString(expected[i]), + Float.toString(actual[i]), + message)); + } + } + + /** + * Asserts that two arrays contain the same elements in the same order. If they do not, an + * AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(double[] actual, double[] expected) { + assertEquals(actual, expected, ""); + } + + /** + * Asserts that two arrays contain the same elements in the same order. If they do not, an + * AssertionError, with the given message, is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(double[] actual, double[] expected, String message) { + if (checkRefEqualityAndLength(actual, expected, message)) { + return; + } + + for (int i = 0; i < expected.length; i++) { + assertEquals( + actual[i], + expected[i], + String.format( + ARRAY_MISMATCH_TEMPLATE, + i, + Double.toString(expected[i]), + Double.toString(actual[i]), + message)); + } + } + + /** + * Asserts that two arrays contain the equal elements concerning a delta in the same order. If + * they do not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param delta the absolute tolerable difference between the actual and expected values + */ + public static void assertEquals(double[] actual, double[] expected, double delta) { + assertEquals(actual, expected, delta, ""); + } + + /** + * Asserts that two arrays contain the equal elements concerning a delta in the same order. If + * they do not, an AssertionError, with the given message, is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param delta the absolute tolerable difference between the actual and expected values + * @param message the assertion error message + */ + public static void assertEquals( + double[] actual, double[] expected, double delta, String message) { + if (checkRefEqualityAndLength(actual, expected, message)) { + return; + } + + for (int i = 0; i < expected.length; i++) { + assertEquals( + actual[i], + expected[i], + delta, + String.format( + ARRAY_MISMATCH_TEMPLATE, + i, + Double.toString(expected[i]), + Double.toString(actual[i]), + message)); + } + } + + /** + * Asserts that two arrays contain the same elements in the same order. If they do not, an + * AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(long[] actual, long[] expected) { + assertEquals(actual, expected, ""); + } + + /** + * Asserts that two arrays contain the same elements in the same order. If they do not, an + * AssertionError, with the given message, is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(long[] actual, long[] expected, String message) { + if (checkRefEqualityAndLength(actual, expected, message)) { + return; + } + + for (int i = 0; i < expected.length; i++) { + if (expected[i] != actual[i]) { + fail( + String.format( + ARRAY_MISMATCH_TEMPLATE, + i, + Long.toString(expected[i]), + Long.toString(actual[i]), + message)); + } + } + } + + /** + * This methods check referential equality of given arguments as well as references length + * (assuming they are arrays). Successful execution of this method guaranties arrays length + * equality. + * + * @param actualArray array of elements + * @param expectedArray array of elements + * @param message the assertion error message + * @return {@code true} if {@code actualArray} and {@code expectedArray} are the same, {@code + * false} otherwise. If references are different and arrays length are different {@link + * AssertionError} is thrown. + */ + private static boolean checkRefEqualityAndLength( + Object actualArray, Object expectedArray, String message) { + if (expectedArray == actualArray) { + return true; + } + if (null == expectedArray) { + fail("expectedArray a null array, but not null found. " + message); + } + if (null == actualArray) { + fail("expectedArray not null array, but null found. " + message); + } + + assertEquals( + Array.getLength(actualArray), + Array.getLength(expectedArray), + "arrays don't have the same size. " + message); + return false; + } + + /** + * Asserts that two objects are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(Object actual, Object expected) { + assertEquals(actual, expected, null); + } + + /** + * Asserts that two Strings are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(String actual, String expected, String message) { + assertEquals((Object) actual, (Object) expected, message); + } + + /** + * Asserts that two Strings are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(String actual, String expected) { + assertEquals(actual, expected, null); + } + + private static boolean areEqual(double actual, double expected, double delta) { + // handle infinity specially since subtracting to infinite values gives NaN and the + // the following test fails + if (Double.isInfinite(expected)) { + if (!(expected == actual)) { + return false; + } + } else if (Double.isNaN(expected)) { + if (!Double.isNaN(actual)) { + return false; + } + } else if (!(Math.abs(expected - actual) <= delta)) { + return false; + } + return true; + } + + /** + * Asserts that two doubles are equal concerning a delta. If they are not, an AssertionError, with + * the given message, is thrown. If the expected value is infinity then the delta value is + * ignored. + * + * @param actual the actual value + * @param expected the expected value + * @param delta the absolute tolerable difference between the actual and expected values + * @param message the assertion error message + */ + public static void assertEquals(double actual, double expected, double delta, String message) { + if (!areEqual(actual, expected, delta)) { + failNotEquals(actual, expected, message); + } + } + + /** + * Asserts that two doubles are equal concerning a delta. If they are not, an AssertionError is + * thrown. If the expected value is infinity then the delta value is ignored. + * + * @param actual the actual value + * @param expected the expected value + * @param delta the absolute tolerable difference between the actual and expected values + */ + public static void assertEquals(double actual, double expected, double delta) { + assertEquals(actual, expected, delta, null); + } + + /** + * Asserts that two doubles are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(double actual, double expected, String message) { + if (Double.isNaN(expected)) { + if (!Double.isNaN(actual)) { + failNotEquals(actual, expected, message); + } + } else if (actual != expected) { + failNotEquals(actual, expected, message); + } + } + + /** + * Asserts that two doubles are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(Double actual, double expected, String message) { + assertEquals(actual, Double.valueOf(expected), message); + } + + /** + * Asserts that two doubles are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(double actual, Double expected, String message) { + assertEquals(Double.valueOf(actual), expected, message); + } + + /** + * Asserts that two doubles are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(double actual, double expected) { + assertEquals(actual, expected, null); + } + + /** + * Asserts that two doubles are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(Double actual, double expected) { + assertEquals(actual, Double.valueOf(expected), null); + } + + /** + * Asserts that two doubles are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(double actual, Double expected) { + assertEquals(Double.valueOf(actual), expected, null); + } + + private static boolean areEqual(float actual, float expected, float delta) { + // handle infinity specially since subtracting to infinite values gives NaN and the + // the following test fails + if (Float.isInfinite(expected)) { + if (!(expected == actual)) { + return false; + } + } else if (Float.isNaN(expected)) { + if (!Float.isNaN(actual)) { + return false; + } + } else if (!(Math.abs(expected - actual) <= delta)) { + return false; + } + return true; + } + + /** + * Asserts that two floats are equal concerning a delta. If they are not, an AssertionError, with + * the given message, is thrown. If the expected value is infinity then the delta value is + * ignored. + * + * @param actual the actual value + * @param expected the expected value + * @param delta the absolute tolerable difference between the actual and expected values + * @param message the assertion error message + */ + public static void assertEquals(float actual, float expected, float delta, String message) { + if (!areEqual(actual, expected, delta)) { + failNotEquals(actual, expected, message); + } + } + + /** + * Asserts that two floats are equal concerning a delta. If they are not, an AssertionError is + * thrown. If the expected value is infinity then the delta value is ignored. + * + * @param actual the actual value + * @param expected the expected value + * @param delta the absolute tolerable difference between the actual and expected values + */ + public static void assertEquals(float actual, float expected, float delta) { + assertEquals(actual, expected, delta, null); + } + + /** + * Asserts that two floats are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(float actual, float expected, String message) { + if (Float.isNaN(expected)) { + if (!Float.isNaN(actual)) { + failNotEquals(actual, expected, message); + } + } else if (actual != expected) { + failNotEquals(actual, expected, message); + } + } + + /** + * Asserts that two floats are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(Float actual, float expected, String message) { + assertEquals(actual, Float.valueOf(expected), message); + } + + /** + * Asserts that two floats are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(float actual, Float expected, String message) { + assertEquals(Float.valueOf(actual), expected, message); + } + + /** + * Asserts that two floats are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(float actual, float expected) { + assertEquals(actual, expected, null); + } + + /** + * Asserts that two floats are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(Float actual, float expected) { + assertEquals(actual, Float.valueOf(expected), null); + } + + /** + * Asserts that two floats are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(float actual, Float expected) { + assertEquals(Float.valueOf(actual), expected, null); + } + + /** + * Asserts that two longs are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(long actual, long expected, String message) { + assertEquals(Long.valueOf(actual), Long.valueOf(expected), message); + } + + /** + * Asserts that two longs are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(Long actual, long expected, String message) { + assertEquals(actual, Long.valueOf(expected), message); + } + + /** + * Asserts that two longs are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(long actual, Long expected, String message) { + assertEquals(Long.valueOf(actual), expected, message); + } + + /** + * Asserts that two longs are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(long actual, long expected) { + assertEquals(actual, expected, null); + } + /** + * Asserts that two longs are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(Long actual, long expected) { + assertEquals(actual, Long.valueOf(expected), null); + } + + /** + * Asserts that two longs are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(long actual, Long expected) { + assertEquals(Long.valueOf(actual), expected, null); + } + + /** + * Asserts that two booleans are equal. If they are not, an AssertionError, with the given + * message, is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(boolean actual, boolean expected, String message) { + assertEquals(Boolean.valueOf(actual), Boolean.valueOf(expected), message); + } + + /** + * Asserts that two booleans are equal. If they are not, an AssertionError, with the given + * message, is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(Boolean actual, boolean expected, String message) { + assertEquals(actual, Boolean.valueOf(expected), message); + } + + /** + * Asserts that two booleans are equal. If they are not, an AssertionError, with the given + * message, is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(boolean actual, Boolean expected, String message) { + assertEquals(Boolean.valueOf(actual), expected, message); + } + + /** + * Asserts that two booleans are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(boolean actual, boolean expected) { + assertEquals(actual, expected, null); + } + + /** + * Asserts that two booleans are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(Boolean actual, boolean expected) { + assertEquals(actual, Boolean.valueOf(expected), null); + } + + /** + * Asserts that two booleans are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(boolean actual, Boolean expected) { + assertEquals(Boolean.valueOf(actual), expected, null); + } + + /** + * Asserts that two bytes are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(byte actual, byte expected, String message) { + assertEquals(Byte.valueOf(actual), Byte.valueOf(expected), message); + } + + /** + * Asserts that two bytes are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(Byte actual, byte expected, String message) { + assertEquals(actual, Byte.valueOf(expected), message); + } + + /** + * Asserts that two bytes are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(byte actual, Byte expected, String message) { + assertEquals(Byte.valueOf(actual), expected, message); + } + + /** + * Asserts that two bytes are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(byte actual, byte expected) { + assertEquals(actual, expected, null); + } + + /** + * Asserts that two bytes are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(Byte actual, byte expected) { + assertEquals(actual, Byte.valueOf(expected), null); + } + + /** + * Asserts that two bytes are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(byte actual, Byte expected) { + assertEquals(Byte.valueOf(actual), expected, null); + } + + /** + * Asserts that two chars are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(char actual, char expected, String message) { + assertEquals(Character.valueOf(actual), Character.valueOf(expected), message); + } + + /** + * Asserts that two chars are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(Character actual, char expected, String message) { + assertEquals(actual, Character.valueOf(expected), message); + } + + /** + * Asserts that two chars are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(char actual, Character expected, String message) { + assertEquals(Character.valueOf(actual), expected, message); + } + + /** + * Asserts that two chars are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(char actual, char expected) { + assertEquals(actual, expected, null); + } + + /** + * Asserts that two chars are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(Character actual, char expected) { + assertEquals(actual, Character.valueOf(expected), null); + } + + /** + * Asserts that two chars are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(char actual, Character expected) { + assertEquals(Character.valueOf(actual), expected, null); + } + + /** + * Asserts that two shorts are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(short actual, short expected, String message) { + assertEquals(Short.valueOf(actual), Short.valueOf(expected), message); + } + + /** + * Asserts that two shorts are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(Short actual, short expected, String message) { + assertEquals(actual, Short.valueOf(expected), message); + } + + /** + * Asserts that two shorts are equal. If they are not, an AssertionError, with the given message, + * is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(short actual, Short expected, String message) { + assertEquals(Short.valueOf(actual), expected, message); + } + + /** + * Asserts that two shorts are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(short actual, short expected) { + assertEquals(actual, expected, null); + } + + /** + * Asserts that two shorts are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(Short actual, short expected) { + assertEquals(actual, Short.valueOf(expected), null); + } + + /** + * Asserts that two shorts are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(short actual, Short expected) { + assertEquals(Short.valueOf(actual), expected, null); + } + + /** + * Asserts that two ints are equal. If they are not, an AssertionError, with the given message, is + * thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(int actual, int expected, String message) { + assertEquals(Integer.valueOf(actual), Integer.valueOf(expected), message); + } + + /** + * Asserts that two ints are equal. If they are not, an AssertionError, with the given message, is + * thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(Integer actual, int expected, String message) { + assertEquals(actual, Integer.valueOf(expected), message); + } + + /** + * Asserts that two ints are equal. If they are not, an AssertionError, with the given message, is + * thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(int actual, Integer expected, String message) { + assertEquals(Integer.valueOf(actual), expected, message); + } + + /** + * Asserts that two ints are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(int actual, int expected) { + assertEquals(actual, expected, null); + } + + /** + * Asserts that two ints are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(Integer actual, int expected) { + assertEquals(actual, Integer.valueOf(expected), null); + } + + /** + * Asserts that two ints are equal. If they are not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(int actual, Integer expected) { + assertEquals(Integer.valueOf(actual), expected, null); + } + + /** + * Asserts that an object isn't null. If it is, an AssertionError is thrown. + * + * @param object the assertion object + */ + public static void assertNotNull(Object object) { + assertNotNull(object, null); + } + + /** + * Asserts that an object isn't null. If it is, an AssertionError, with the given message, is + * thrown. + * + * @param object the assertion object + * @param message the assertion error message + */ + public static void assertNotNull(Object object, String message) { + if (object == null) { + String formatted = ""; + if (message != null) { + formatted = message + " "; + } + fail(formatted + "expected object to not be null"); + } + } + + /** + * Asserts that an object is null. If it is not, an AssertionError, with the given message, is + * thrown. + * + * @param object the assertion object + */ + public static void assertNull(Object object) { + assertNull(object, null); + } + + /** + * Asserts that an object is null. If it is not, an AssertionError, with the given message, is + * thrown. + * + * @param object the assertion object + * @param message the assertion error message + */ + public static void assertNull(Object object, String message) { + if (object != null) { + failNotSame(object, null, message); + } + } + + /** + * Asserts that two objects refer to the same object. If they do not, an AssertionError, with the + * given message, is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertSame(Object actual, Object expected, String message) { + if (expected == actual) { + return; + } + failNotSame(actual, expected, message); + } + + /** + * Asserts that two objects refer to the same object. If they do not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertSame(Object actual, Object expected) { + assertSame(actual, expected, null); + } + + /** + * Asserts that two objects do not refer to the same objects. If they do, an AssertionError, with + * the given message, is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertNotSame(Object actual, Object expected, String message) { + if (expected == actual) { + failSame(actual, expected, message); + } + } + + /** + * Asserts that two objects do not refer to the same object. If they do, an AssertionError is + * thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertNotSame(Object actual, Object expected) { + assertNotSame(actual, expected, null); + } + + private static void failSame(Object actual, Object expected, String message) { + String formatted = ""; + if (message != null) { + formatted = message + " "; + } + fail(formatted + ASSERT_LEFT2 + expected + ASSERT_MIDDLE + actual + ASSERT_RIGHT); + } + + private static void failNotSame(Object actual, Object expected, String message) { + String formatted = ""; + if (message != null) { + formatted = message + " "; + } + fail(formatted + ASSERT_EQUAL_LEFT + expected + ASSERT_MIDDLE + actual + ASSERT_RIGHT); + } + + private static void failNotEquals(Object actual, Object expected, String message) { + fail(format(actual, expected, message, true)); + } + + private static void failEquals(Object actual, Object expected, String message) { + fail(format(actual, expected, message, false)); + } + + static String format(Object actual, Object expected, String message, boolean isAssertEquals) { + String formatted = ""; + if (null != message) { + formatted = message + " "; + } + if (isAssertEquals) { + // if equality is asserted but inequality is found + return formatted + ASSERT_EQUAL_LEFT + expected + ASSERT_MIDDLE + actual + ASSERT_RIGHT; + } + // if inequality is asserted but equality is found + return formatted + ASSERT_UNEQUAL_LEFT + expected + ASSERT_MIDDLE + actual + ASSERT_RIGHT; + } + + /** + * Asserts that two collections contain the same elements in the same order. If they do not, an + * AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(Collection actual, Collection expected) { + assertEquals(actual, expected, null); + } + + /** + * Asserts that two collections contain the same elements in the same order. If they do not, an + * AssertionError, with the given message, is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(Collection actual, Collection expected, String message) { + if (actual == expected) { // We don't use Objects.equals here because order is checked + return; + } + + if (actual == null || expected == null) { + if (message != null) { + fail(message); + } else { + fail("Collections not equal: expected: " + expected + " and actual: " + actual); + } + } + + assertEquals( + actual.size(), + expected.size(), + (message == null ? "" : message + ": ") + "lists don't have the same size"); + + Iterator actIt = actual.iterator(); + Iterator expIt = expected.iterator(); + int i = -1; + while (actIt.hasNext() && expIt.hasNext()) { + i++; + Object e = expIt.next(); + Object a = actIt.next(); + String explanation = "Lists differ at element [" + i + "]: " + e + " != " + a; + String errorMessage = message == null ? explanation : message + ": " + explanation; + assertEqualsImpl(a, e, errorMessage); + } + } + + /** + * Asserts that two iterators return the same elements in the same order. If they do not, an + * AssertionError is thrown. Please note that this assert iterates over the elements and modifies + * the state of the iterators. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(Iterator actual, Iterator expected) { + assertEquals(actual, expected, null); + } + + /** + * Asserts that two iterators return the same elements in the same order. If they do not, an + * AssertionError, with the given message, is thrown. Please note that this assert iterates over + * the elements and modifies the state of the iterators. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(Iterator actual, Iterator expected, String message) { + if (actual == expected) { // We don't use Objects.equals here because order is checked + return; + } + if (actual == null || expected == null) { + String msg = + message != null + ? message + : "Iterators not equal: expected: " + expected + " and actual: " + actual; + fail(msg); + } + + int i = -1; + while (actual.hasNext() && expected.hasNext()) { + + i++; + Object e = expected.next(); + Object a = actual.next(); + String explanation = "Iterators differ at element [" + i + "]: " + e + " != " + a; + String errorMessage = message == null ? explanation : message + ": " + explanation; + + assertEqualsImpl(a, e, errorMessage); + } + + if (actual.hasNext()) { + + String explanation = "Actual iterator returned more elements than the expected iterator."; + String errorMessage = message == null ? explanation : message + ": " + explanation; + fail(errorMessage); + + } else if (expected.hasNext()) { + + String explanation = "Expected iterator returned more elements than the actual iterator."; + String errorMessage = message == null ? explanation : message + ": " + explanation; + fail(errorMessage); + } + } + + /** + * Asserts that two iterables return iterators with the same elements in the same order. If they + * do not, an AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(Iterable actual, Iterable expected) { + assertEquals(actual, expected, null); + } + + /** + * Asserts that two iterables return iterators with the same elements in the same order. If they + * do not, an AssertionError, with the given message, is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(Iterable actual, Iterable expected, String message) { + if (actual == expected) { // We don't use Objects.equals here because order is checked + return; + } + + if (actual == null || expected == null) { + if (message != null) { + fail(message); + } else { + fail("Iterables not equal: expected: " + expected + " and actual: " + actual); + } + } + + Iterator actIt = actual.iterator(); + Iterator expIt = expected.iterator(); + + assertEquals(actIt, expIt, message); + } + + /** + * Asserts that two arrays contain the same elements in the same order. If they do not, an + * AssertionError, with the given message, is thrown. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEquals(Object[] actual, Object[] expected, String message) { + if (Arrays.equals(actual, expected)) { + return; + } + + if ((actual == null && expected != null) || (actual != null && expected == null)) { + if (message != null) { + fail(message); + } else { + fail( + "Arrays not equal: expected: " + + Arrays.toString(expected) + + " and actual: " + + Arrays.toString(actual)); + } + } + if (actual.length != expected.length) { + failAssertNoEqual( + "Arrays do not have the same size:" + actual.length + " != " + expected.length, message); + } + + for (int i = 0; i < expected.length; i++) { + Object e = expected[i]; + Object a = actual[i]; + String explanation = "Arrays differ at element [" + i + "]: " + e + " != " + a; + String errorMessage = message == null ? explanation : message + ": " + explanation; + if (a == null && e == null) { + continue; + } + if ((a == null && e != null) || (a != null && e == null)) { + failNotEquals(a, e, message); + } + // Compare by value for multi-dimensional array. + if (e.getClass().isArray()) { + assertEquals(a, e, errorMessage); + } else { + assertEqualsImpl(a, e, errorMessage); + } + } + } + + /** + * Asserts that two arrays contain the same elements in no particular order. If they do not, an + * {@code AssertionError}, with the given message, is thrown. The arrays are not compared + * 'deeply', that means, if the elements are arrays as well their {@code equals} method is used + * which only checks for reference equality. + * + * @param actual the actual value + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertEqualsNoOrder(Object[] actual, Object[] expected, String message) { + if (actual == expected) { // We don't use Arrays.equals here because order is not checked + return; + } + + if (actual == null || expected == null) { + failAssertNoEqual( + "Arrays not equal: expected: " + + Arrays.toString(expected) + + " and actual: " + + Arrays.toString(actual), + message); + } + + if (actual.length != expected.length) { + failAssertNoEqual( + "Arrays do not have the same size:" + actual.length + " != " + expected.length, message); + } + + List actualCollection = Lists.newArrayList(actual); + for (Object o : expected) { + actualCollection.remove(o); + } + if (!actualCollection.isEmpty()) { + failAssertNoEqual( + "Arrays not equal: expected: " + + Arrays.toString(expected) + + " and actual: " + + Arrays.toString(actual), + message); + } + } + + public static void assertEqualsNoOrder( + Collection actual, Collection expected, String message) { + List actualCollection = Lists.newArrayList(actual); + actualCollection.removeAll(expected); + if (!actualCollection.isEmpty()) { + failAssertNoEqual( + "Collections not equal: expected: " + expected + " and actual: " + actual, message); + } + } + + public static void assertEqualsNoOrder(Iterator actual, Iterator expected, String message) { + List actualCollection = Lists.newArrayList(actual); + actualCollection.removeAll(Lists.newArrayList(expected)); + if (!actualCollection.isEmpty()) { + failAssertNoEqual( + "Iterators not equal: expected: " + + toString(expected) + + " and actual: " + + toString(actual), + message); + } + } + + private static String toString(Iterator iterator) { + if (iterator == null) { + return null; + } + Iterable iterable = () -> (Iterator) iterator; + return StreamSupport.stream(iterable.spliterator(), false) + .map(Object::toString) + .collect(Collectors.joining(", ")); + } + + private static void failAssertNoEqual(String defaultMessage, String message) { + if (message != null) { + fail(message); + } else { + fail(defaultMessage); + } + } + + /** + * Asserts that two arrays contain the same elements in the same order. If they do not, an + * AssertionError is thrown. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEquals(Object[] actual, Object[] expected) { + assertEquals(actual, expected, null); + } + + /** + * Asserts that two arrays contain the same elements in no particular order. If they do not, an + * {@code AssertionError} is thrown. The arrays are not compared 'deeply', that means, if the + * elements are arrays as well their {@code equals} method is used which only checks for reference + * equality. + * + * @param actual the actual value + * @param expected the expected value + */ + public static void assertEqualsNoOrder(Object[] actual, Object[] expected) { + assertEqualsNoOrder(actual, expected, null); + } + + public static void assertEqualsNoOrder(Collection actual, Collection expected) { + assertEqualsNoOrder(actual, expected, null); + } + + public static void assertEqualsNoOrder(Iterator actual, Iterator expected) { + assertEqualsNoOrder(actual, expected, null); + } + + /** + * Asserts that two sets are equal. + * + * @param actual The actual value + * @param expected The expected value + */ + public static void assertEquals(Set actual, Set expected) { + assertEquals(actual, expected, null); + } + + /** returns not equal reason or null if equal */ + private static String getNotEqualReason(Collection actual, Collection expected) { + if (actual == expected) { // We don't use Arrays.equals here because order is checked + return null; + } + + if (actual == null || expected == null) { + // Keep the back compatible + return "Collections not equal: expected: " + expected + " and actual: " + actual; + } + + if (!Objects.equals(actual, expected)) { + return "Collections differ: expected " + expected + " but got " + actual; + } + + return getNotEqualReason(actual.iterator(), expected.iterator()); + } + + private static String getNotEqualReason(Iterator actual, Iterator expected) { + if (actual == expected) { // We don't use Arrays.equals here because order is checked + return null; + } + + if (actual == null || expected == null) { + // Keep the back compatible + return "Iterators not equal: expected: " + + toString(expected) + + " and actual: " + + toString(actual); + } + + while (actual.hasNext() && expected.hasNext()) { + if (!Objects.equals(actual.next(), expected.next())) { + return "Iterators not same element order: expected: " + + toString(expected) + + " and actual: " + + toString(actual); + } + } + return null; + } + + private static String getNotEqualReason(Set actual, Set expected) { + if (actual == expected) { + return null; + } + + if (actual == null || expected == null) { + // Keep the back compatible + return "Sets not equal: expected: " + expected + " and actual: " + actual; + } + + if (!Objects.equals(actual, expected)) { + return "Sets differ: expected " + expected + " but got " + actual; + } + return null; + } + + /** + * Assert set equals + * + * @param actual The actual value + * @param expected The expected value + * @param message The message + */ + public static void assertEquals(Set actual, Set expected, String message) { + String notEqualReason = getNotEqualReason(actual, expected); + if (null != notEqualReason) { + // Keep the back compatible + if (message == null) { + fail(notEqualReason); + } else { + fail(message); + } + } + } + + /** returns not equal deep reason or null if equal */ + private static String getNotEqualDeepReason(Set actual, Set expected) { + if (Objects.equals(actual, expected)) { + return null; + } + + if (actual == null || expected == null) { + // Keep the back compatible + return "Sets not equal: expected: " + expected + " and actual: " + actual; + } + + if (expected.size() != actual.size()) { + return "Sets not equal: expected: " + expected + " and actual: " + actual; + } + + Iterator actualIterator = actual.iterator(); + Iterator expectedIterator = expected.iterator(); + while (expectedIterator.hasNext()) { + Object expectedValue = expectedIterator.next(); + Object value = actualIterator.next(); + if (expectedValue.getClass().isArray()) { + String arrayNotEqualReason = getArrayNotEqualReason(value, expectedValue); + if (arrayNotEqualReason != null) { + return arrayNotEqualReason; + } + } else { + if (!areEqualImpl(value, expected)) { + return "Sets not equal: expected: " + expectedValue + " and actual: " + value; + } + } + } + return null; + } + + public static void assertEqualsDeep(Set actual, Set expected, String message) { + String notEqualDeepReason = getNotEqualDeepReason(actual, expected); + if (notEqualDeepReason != null) { + if (message == null) { + fail(notEqualDeepReason); + } else { + fail(message); + } + } + } + + public static void assertEquals(Map actual, Map expected) { + assertEquals(actual, expected, null); + } + + private static String getNotEqualReason(Map actual, Map expected) { + if (Objects.equals(actual, expected)) { + return null; + } + + if (actual == null || expected == null) { + return "Maps not equal: expected: " + expected + " and actual: " + actual; + } + + if (actual.size() != expected.size()) { + return "Maps do not have the same size:" + actual.size() + " != " + expected.size(); + } + + Set entrySet = actual.entrySet(); + for (Object anEntrySet : entrySet) { + Map.Entry entry = (Map.Entry) anEntrySet; + Object key = entry.getKey(); + Object value = entry.getValue(); + Object expectedValue = expected.get(key); + String assertMessage = + "Maps do not match for key:" + key + " actual:" + value + " expected:" + expectedValue; + if (!areEqualImpl(value, expectedValue)) { + return assertMessage; + } + } + return null; + } + + /** + * Asserts that two maps are equal. + * + * @param actual The actual value + * @param expected The expected value + * @param message The message + */ + public static void assertEquals(Map actual, Map expected, String message) { + String notEqualReason = getNotEqualReason(actual, expected); + if (notEqualReason != null) { + if (message == null) { + fail(notEqualReason); + } else { + fail(message); + } + } + } + + public static void assertEqualsDeep(Map actual, Map expected) { + assertEqualsDeep(actual, expected, null); + } + + /** returns not equal deep reason or null if equal */ + private static String getNotEqualDeepReason(Map actual, Map expected) { + if (Objects.equals(actual, expected)) { + return null; + } + + if (actual == null || expected == null) { + return "Maps not equal: expected: " + expected + " and actual: " + actual; + } + + if (actual.size() != expected.size()) { + return "Maps do not have the same size:" + actual.size() + " != " + expected.size(); + } + + Set entrySet = actual.entrySet(); + for (Object anEntrySet : entrySet) { + Map.Entry entry = (Map.Entry) anEntrySet; + Object key = entry.getKey(); + Object value = entry.getValue(); + Object expectedValue = expected.get(key); + String assertMessage = + "Maps do not match for key:" + key + " actual:" + value + " expected:" + expectedValue; + if (expectedValue.getClass().isArray()) { + if (!areArraysEqual(value, expectedValue)) { + return assertMessage; + } + } else { + if (!areEqualImpl(value, expectedValue)) { + return assertMessage; + } + } + } + return null; + } + + public static void assertEqualsDeep(Map actual, Map expected, String message) { + String notEqualDeepReason = getNotEqualDeepReason(actual, expected); + if (notEqualDeepReason != null) { + if (message == null) { + fail(notEqualDeepReason); + } else { + fail(message); + } + } + } + + ///// + // assertNotEquals + // + + public static void assertNotEquals(Object actual, Object expected, String message) { + if (expected != null && expected.getClass().isArray()) { + assertArrayNotEquals(actual, expected, message); + return; + } + assertNotEqualsImpl(actual, expected, message); + } + + public static void assertNotEquals(Object[] actual, Object[] expected, String message) { + assertArrayNotEquals(actual, expected, message); + } + + public static void assertNotEquals(Iterator actual, Iterator expected, String message) { + String notEqualReason = getNotEqualReason(actual, expected); + if (notEqualReason == null) { + Assert.fail(format(actual, expected, message, false)); + } + } + + public static void assertNotEquals(Collection actual, Collection expected, String message) { + String notEqualReason = getNotEqualReason(actual, expected); + if (notEqualReason == null) { + Assert.fail(format(actual, expected, message, false)); + } + } + + public static void assertNotEquals(Object actual, Object expected) { + assertNotEquals(actual, expected, null); + } + + public static void assertNotEquals(Collection actual, Collection expected) { + assertNotEquals(actual, expected, null); + } + + public static void assertNotEquals(Iterator actual, Iterator expected) { + assertNotEquals(actual, expected, null); + } + + static void assertNotEquals(String actual, String expected, String message) { + assertNotEquals((Object) actual, (Object) expected, message); + } + + static void assertNotEquals(String actual, String expectec) { + assertNotEquals(actual, expectec, null); + } + + static void assertNotEquals(long actual, long expected, String message) { + assertNotEquals(Long.valueOf(actual), Long.valueOf(expected), message); + } + + static void assertNotEquals(long actual, long expected) { + assertNotEquals(actual, expected, null); + } + + static void assertNotEquals(boolean actual, boolean expected, String message) { + assertNotEquals(Boolean.valueOf(actual), Boolean.valueOf(expected), message); + } + + static void assertNotEquals(boolean actual, boolean expected) { + assertNotEquals(actual, expected, null); + } + + static void assertNotEquals(byte actual, byte expected, String message) { + assertNotEquals(Byte.valueOf(actual), Byte.valueOf(expected), message); + } + + static void assertNotEquals(byte actual, byte expected) { + assertNotEquals(actual, expected, null); + } + + static void assertNotEquals(char actual, char expected, String message) { + assertNotEquals(Character.valueOf(actual), Character.valueOf(expected), message); + } + + static void assertNotEquals(char actual, char expected) { + assertNotEquals(actual, expected, null); + } + + static void assertNotEquals(short actual, short expected, String message) { + assertNotEquals(Short.valueOf(actual), Short.valueOf(expected), message); + } + + static void assertNotEquals(short actual, short expected) { + assertNotEquals(actual, expected, null); + } + + static void assertNotEquals(int actual, int expected, String message) { + assertNotEquals(Integer.valueOf(actual), Integer.valueOf(expected), message); + } + + static void assertNotEquals(int actual, int expected) { + assertNotEquals(actual, expected, null); + } + + public static void assertNotEquals(float actual, float expected, float delta, String message) { + if (areEqual(actual, expected, delta)) { + Assert.fail(format(actual, expected, message, false)); + } + } + + public static void assertNotEquals(float actual, float expected, float delta) { + assertNotEquals(actual, expected, delta, null); + } + + public static void assertNotEquals(double actual, double expected, double delta, String message) { + if (areEqual(actual, expected, delta)) { + Assert.fail(format(actual, expected, message, false)); + } + } + + public static void assertNotEquals(Set actual, Set expected) { + assertNotEquals(actual, expected, null); + } + + public static void assertNotEquals(Set actual, Set expected, String message) { + String notEqualReason = getNotEqualReason(actual, expected); + if (notEqualReason == null) { + Assert.fail(format(actual, expected, message, false)); + } + } + + public static void assertNotEqualsDeep(Set actual, Set expected) { + assertNotEqualsDeep(actual, expected, null); + } + + public static void assertNotEqualsDeep(Set actual, Set expected, String message) { + String notEqualDeepReason = getNotEqualDeepReason(actual, expected); + if (notEqualDeepReason == null) { + Assert.fail(format(actual, expected, message, false)); + } + } + + public static void assertNotEquals(Map actual, Map expected) { + assertNotEquals(actual, expected, null); + } + + public static void assertNotEquals(Map actual, Map expected, String message) { + String notEqualReason = getNotEqualReason(actual, expected); + if (notEqualReason == null) { + Assert.fail(format(actual, expected, message, false)); + } + } + + public static void assertNotEqualsDeep(Map actual, Map expected) { + assertNotEqualsDeep(actual, expected, null); + } + + public static void assertNotEqualsDeep(Map actual, Map expected, String message) { + String notEqualDeepReason = getNotEqualDeepReason(actual, expected); + if (notEqualDeepReason == null) { + Assert.fail(format(actual, expected, message, false)); + } + } + + public static void assertNotEquals(double actual, double expected, double delta) { + assertNotEquals(actual, expected, delta, null); + } + + /** + * This interface facilitates the use of {@link #expectThrows} from Java 8. It allows method + * references to both void and non-void methods to be passed directly into expectThrows without + * wrapping, even if they declare checked exceptions. + * + *

This interface is not meant to be implemented directly. + */ + public interface ThrowingRunnable { + + void run() throws Throwable; + } + + /** + * Asserts that {@code runnable} throws an exception when invoked. If it does not, an {@link + * AssertionError} is thrown. + * + * @param runnable A function that is expected to throw an exception when invoked + * @since 6.9.5 + */ + public static void assertThrows(ThrowingRunnable runnable) { + assertThrows(Throwable.class, runnable); + } + + /** + * Asserts that {@code runnable} throws an exception of type {@code throwableClass} when executed. + * If it does not throw an exception, an {@link AssertionError} is thrown. If it throws the wrong + * type of exception, an {@code AssertionError} is thrown describing the mismatch; the exception + * that was actually thrown can be obtained by calling {@link AssertionError#getCause}. + * + * @param throwableClass the expected type of the exception + * @param the expected type of the exception + * @param runnable A function that is expected to throw an exception when invoked + * @since 6.9.5 + */ + @SuppressWarnings("ThrowableResultOfMethodCallIgnored") + public static void assertThrows( + Class throwableClass, ThrowingRunnable runnable) { + expectThrows(throwableClass, runnable); + } + + /** + * Asserts that {@code runnable} throws an exception of type {@code throwableClass} when executed + * and returns the exception. If {@code runnable} does not throw an exception, an {@link + * AssertionError} is thrown. If it throws the wrong type of exception, an {@code AssertionError} + * is thrown describing the mismatch; the exception that was actually thrown can be obtained by + * calling {@link AssertionError#getCause}. + * + * @param throwableClass the expected type of the exception + * @param the expected type of the exception + * @param runnable A function that is expected to throw an exception when invoked + * @return The exception thrown by {@code runnable} + * @since 6.9.5 + */ + public static T expectThrows( + Class throwableClass, ThrowingRunnable runnable) { + try { + runnable.run(); + } catch (Throwable t) { + if (throwableClass.isInstance(t)) { + return throwableClass.cast(t); + } else { + String mismatchMessage = + String.format( + "Expected %s to be thrown, but %s was thrown", + throwableClass.getSimpleName(), t.getClass().getSimpleName()); + + throw new AssertionError(mismatchMessage, t); + } + } + String message = + String.format( + "Expected %s to be thrown, but nothing was thrown", throwableClass.getSimpleName()); + throw new AssertionError(message); + } +} diff --git a/testng-asserts/src/main/java/org/testng/FileAssert.java b/testng-asserts/src/main/java/org/testng/FileAssert.java new file mode 100644 index 0000000..5c80dd3 --- /dev/null +++ b/testng-asserts/src/main/java/org/testng/FileAssert.java @@ -0,0 +1,359 @@ +package org.testng; + +import java.io.File; +import java.io.IOException; + +/** + * Assertion tool for File centric assertions. Conceptually, this is an extension of {@link Assert}. + * Presents assertion methods with a more natural parameter order. The order is always + * actualValue, expectedValue [, message]. + * + * @author Paul Mendelon + * @since 5.6 + */ +public class FileAssert { + + /** Protect this constructor since it is a static only class. */ + private FileAssert() { + // Hide this constructor. + } + + /** + * Asserts that a {@code tstvalue} is a proper directory. If it isn't, an AssertionError with the + * given message is thrown. + * + * @param tstvalue the file to evaluate + * @param message the assertion error message + */ + public static void assertDirectory(File tstvalue, String message) { + boolean condition = false; + try { + condition = tstvalue.isDirectory(); + } catch (SecurityException e) { + failSecurity(e, tstvalue, fileType(tstvalue), "Directory", message); + } + if (!condition) { + failFile(tstvalue, fileType(tstvalue), "Directory", message); + } + } + + public static void assertDirectory(File tstvalue) { + assertDirectory(tstvalue, null); + } + + /** + * Asserts that a {@code tstvalue} is a proper file. If it isn't, an AssertionError with the given + * message is thrown. + * + * @param tstvalue the file to evaluate + * @param message the assertion error message + */ + public static void assertFile(File tstvalue, String message) { + boolean condition = false; + try { + condition = tstvalue.isFile(); + } catch (SecurityException e) { + failSecurity(e, tstvalue, fileType(tstvalue), "File", message); + } + if (!condition) { + failFile(tstvalue, fileType(tstvalue), "File", message); + } + } + + /** + * @param tstvalue The actual file + * @see #assertFile(File, String) + */ + public static void assertFile(File tstvalue) { + assertFile(tstvalue, null); + } + + /** + * Asserts that a {@code tstvalue} is a file of exactly {@code expected} characters or a directory + * of exactly {@code expected} entries. If it isn't, an AssertionError with the given message is + * thrown. + * + * @param tstvalue the file to evaluate + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertLength(File tstvalue, long expected, String message) { + long actual = -1L; + try { + actual = tstvalue.isDirectory() ? tstvalue.list().length : tstvalue.length(); + } catch (SecurityException e) { + failSecurity(e, tstvalue, String.valueOf(actual), String.valueOf(expected), message); + } + if (actual != expected) { + failFile(tstvalue, String.valueOf(actual), String.valueOf(expected), message); + } + } + + /** + * @param tstvalue The actual file + * @param expected The expected length + * @see #assertLength(File, long, String) + */ + public static void assertLength(File tstvalue, long expected) { + assertLength(tstvalue, expected, null); + } + + /** + * Asserts that a {@code tstvalue} is a file of at least {@code expected} characters or a + * directory of at least {@code expected} entries. If it isn't, an AssertionError with the given + * message is thrown. + * + * @param tstvalue the file to evaluate + * @param expected the expected value + * @param message the assertion error message + */ + public static void assertMinLength(File tstvalue, long expected, String message) { + long actual = -1L; + try { + actual = tstvalue.isDirectory() ? tstvalue.list().length : tstvalue.length(); + } catch (SecurityException e) { + failSecurity( + e, tstvalue, String.valueOf(actual), "at least " + String.valueOf(expected), message); + } + if (actual < expected) { + failFile(tstvalue, String.valueOf(actual), "at least " + String.valueOf(expected), message); + } + } + + /** + * @param tstvalue The actual file + * @param expected The expected min length + * @see #assertMinLength(File, long, String) + */ + public static void assertMinLength(File tstvalue, long expected) { + assertMinLength(tstvalue, expected, null); + } + + /** + * Asserts that a {@code tstvalue} is a file of at most {@code expected} characters or a directory + * of at most {@code expected} entries. If it isn't, an AssertionError with the given message is + * thrown. + * + * @param tstvalue the file to evaluate + * @param expected The expected max length + * @param message the assertion error message + */ + public static void assertMaxLength(File tstvalue, long expected, String message) { + long actual = -1L; + try { + actual = tstvalue.isDirectory() ? tstvalue.list().length : tstvalue.length(); + } catch (SecurityException e) { + failSecurity( + e, tstvalue, String.valueOf(actual), "at most " + String.valueOf(expected), message); + } + if (actual > expected) { + failFile(tstvalue, String.valueOf(actual), "at most " + String.valueOf(expected), message); + } + } + + /** + * @param tstvalue The actual file + * @param expected The expected length + * @see #assertMaxLength(File, long, String) + */ + public static void assertMaxLength(File tstvalue, long expected) { + assertMaxLength(tstvalue, expected, null); + } + + /** + * Asserts that a {@code tstvalue} is readable. If it isn't, an AssertionError with the given + * message is thrown. + * + * @param tstvalue the file to evaluate + * @param message the assertion error message + */ + public static void assertReadable(File tstvalue, String message) { + boolean condition = false; + try { + condition = tstvalue.canRead(); + } catch (SecurityException e) { + failSecurity(e, tstvalue, fileAccess(tstvalue), "Read Access", message); + } + if (!condition) { + failFile(tstvalue, fileAccess(tstvalue), "Read Access", message); + } + } + + /** + * @param tstvalue The actual file + * @see #assertReadable(File, String) + */ + public static void assertReadable(File tstvalue) { + assertReadable(tstvalue, null); + } + + /** + * Asserts that a {@code tstvalue} is writeable. If it isn't, an AssertionError with the given + * message is thrown. + * + * @param tstvalue the file to evaluate + * @param message the assertion error message + */ + public static void assertWriteable(File tstvalue, String message) { + boolean condition = false; + try { + condition = tstvalue.canWrite(); + } catch (SecurityException e) { + failSecurity(e, tstvalue, fileAccess(tstvalue), "Write Access", message); + } + if (!condition) { + failFile(tstvalue, fileAccess(tstvalue), "Write Access", message); + } + } + + /** + * @param tstvalue The actual file + * @see #assertWriteable(File, String) + */ + public static void assertWriteable(File tstvalue) { + assertReadable(tstvalue, null); + } + + /** + * Asserts that a {@code tstvalue} is readable and writeable. If it isn't, an AssertionError with + * the given message is thrown. + * + * @param tstvalue the file to evaluate + * @param message the assertion error message + */ + public static void assertReadWrite(File tstvalue, String message) { + boolean condition = false; + try { + condition = tstvalue.canRead() && tstvalue.canWrite(); + } catch (SecurityException e) { + failSecurity(e, tstvalue, fileAccess(tstvalue), "Read/Write Access", message); + } + if (!condition) { + failFile(tstvalue, fileAccess(tstvalue), "Read/Write Access", message); + } + } + + /** + * @param tstvalue The actual file + * @see #assertReadWrite(File, String) + */ + public static void assertReadWrite(File tstvalue) { + assertReadWrite(tstvalue, null); + } + + /** + * Fails a test with the given message and wrapping the original exception. + * + * @param message the assertion error message + * @param realCause the original exception + */ + public static void fail(String message, Throwable realCause) { + AssertionError ae = new AssertionError(message); + ae.initCause(realCause); + + throw ae; + } + + /** + * Fails a test with the given message. + * + * @param message the assertion error message + */ + public static void fail(String message) { + throw new AssertionError(message); + } + + /** Fails a test with no message. */ + public static void fail() { + fail(null); + } + + /** Formats failure for file assertions. */ + private static void failFile(File path, String actual, String expected, String message) { + String formatted = ""; + if (message != null) { + formatted = message + " "; + } + fail( + formatted + + "expected <" + + expected + + "> but was <" + + toString(path) + + ">" + + (expected != null ? "<" + expected + ">" : "")); + } + + /** + * @param tstvalue + * @param string + * @param string2 + * @param message + */ + private static void failSecurity( + Exception e, File path, String actual, String expected, String message) { + String formatted = ""; + if (message != null) { + formatted = message + " "; + } + fail( + formatted + + "expected <" + + expected + + "> but was <" + + toString(path) + + ">" + + "<" + + (e != null && e.getMessage() != null && e.getMessage().length() > 0 + ? e.getMessage() + : "not authorized by JVM") + + ">"); + } + + /** String representation of what sort of file {@code path} is. */ + private static String fileType(File path) { + try { + if (!path.exists()) { + return "Nonexistent"; + } + if (path.isDirectory()) { + return "Directory"; + } + if (path.isFile()) { + return "File"; + } + return "Special File"; + } catch (SecurityException e) { + return "Unauthorized"; + } + } + + /** String representation of read and write permissions of {@code path}. */ + private static String fileAccess(File path) { + try { + if (!path.exists()) { + return "Nonexistent"; + } + if (path.canRead() && path.canWrite()) { + return "Read and Write Access"; + } + if (path.canRead()) { + return "Read but not Write Access"; + } + if (path.canWrite()) { + return "Write but not Read Access"; + } + return "Neither Read nor Write Access"; + } catch (SecurityException e) { + return "Unauthorized"; + } + } + + private static String toString(File path) { + try { + return path.getCanonicalPath(); + } catch (IOException e) { + return path.getAbsolutePath(); + } + } +} diff --git a/testng-asserts/src/main/java/org/testng/asserts/Assertion.java b/testng-asserts/src/main/java/org/testng/asserts/Assertion.java new file mode 100644 index 0000000..a3778de --- /dev/null +++ b/testng-asserts/src/main/java/org/testng/asserts/Assertion.java @@ -0,0 +1,763 @@ +package org.testng.asserts; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** An assert class with various hooks allowing its behavior to be modified by subclasses. */ +public class Assertion implements IAssertLifecycle { + protected void doAssert(IAssert assertCommand) { + onBeforeAssert(assertCommand); + try { + executeAssert(assertCommand); + onAssertSuccess(assertCommand); + } catch (AssertionError ex) { + onAssertFailure(assertCommand, ex); + throw ex; + } finally { + onAfterAssert(assertCommand); + } + } + + /** Run the assert command in parameter. Meant to be overridden by subclasses. */ + @Override + public void executeAssert(IAssert assertCommand) { + assertCommand.doAssert(); + } + + /** Invoked when an assert succeeds. Meant to be overridden by subclasses. */ + @Override + public void onAssertSuccess(IAssert assertCommand) {} + + @Override + public void onAssertFailure(IAssert assertCommand, AssertionError ex) {} + + /** Invoked before an assert is run. Meant to be overridden by subclasses. */ + @Override + public void onBeforeAssert(IAssert assertCommand) {} + + /** Invoked after an assert is run. Meant to be overridden by subclasses. */ + @Override + public void onAfterAssert(IAssert assertCommand) {} + + private abstract static class SimpleAssert implements IAssert { + private final T actual; + private final T expected; + private final String m_message; + + public SimpleAssert(String message) { + this(null, null, message); + } + + public SimpleAssert(T actual, T expected) { + this(actual, expected, null); + } + + public SimpleAssert(T actual, T expected, String message) { + this.actual = actual; + this.expected = expected; + m_message = message; + } + + @Override + public String getMessage() { + return m_message; + } + + @Override + public T getActual() { + return actual; + } + + @Override + public T getExpected() { + return expected; + } + + @Override + public abstract void doAssert(); + } + + public void assertTrue(final boolean condition, final String message) { + doAssert( + new SimpleAssert(condition, Boolean.TRUE, message) { + @Override + public void doAssert() { + org.testng.Assert.assertTrue(condition, message); + } + }); + } + + public void assertTrue(final boolean condition) { + doAssert( + new SimpleAssert(condition, Boolean.TRUE) { + @Override + public void doAssert() { + org.testng.Assert.assertTrue(condition); + } + }); + } + + public void assertFalse(final boolean condition, final String message) { + doAssert( + new SimpleAssert(condition, Boolean.FALSE, message) { + @Override + public void doAssert() { + org.testng.Assert.assertFalse(condition, message); + } + }); + } + + public void assertFalse(final boolean condition) { + doAssert( + new SimpleAssert(condition, Boolean.FALSE) { + @Override + public void doAssert() { + org.testng.Assert.assertFalse(condition); + } + }); + } + + public void fail(final String message, final Throwable realCause) { + doAssert( + new SimpleAssert(message) { + @Override + public void doAssert() { + org.testng.Assert.fail(message, realCause); + } + }); + } + + public void fail(final String message) { + doAssert( + new SimpleAssert(message) { + @Override + public void doAssert() { + org.testng.Assert.fail(message); + } + }); + } + + public void fail() { + doAssert( + new SimpleAssert(null) { + @Override + public void doAssert() { + org.testng.Assert.fail(); + } + }); + } + + public void assertEquals(final T actual, final T expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected, message); + } + }); + } + + public void assertEquals(final T actual, final T expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected); + } + }); + } + + public void assertEquals(final String actual, final String expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected, message); + } + }); + } + + public void assertEquals(final String actual, final String expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected); + } + }); + } + + public void assertEquals( + final double actual, final double expected, final double delta, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected, delta, message); + } + }); + } + + public void assertEquals(final double actual, final double expected, final double delta) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected, delta); + } + }); + } + + public void assertEquals( + final float actual, final float expected, final float delta, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected, delta, message); + } + }); + } + + public void assertEquals(final float actual, final float expected, final float delta) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected, delta); + } + }); + } + + public void assertEquals(final long actual, final long expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected, message); + } + }); + } + + public void assertEquals(final long actual, final long expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected); + } + }); + } + + public void assertEquals(final boolean actual, final boolean expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected, message); + } + }); + } + + public void assertEquals(final boolean actual, final boolean expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected); + } + }); + } + + public void assertEquals(final byte actual, final byte expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected, message); + } + }); + } + + public void assertEquals(final byte actual, final byte expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected); + } + }); + } + + public void assertEquals(final char actual, final char expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected, message); + } + }); + } + + public void assertEquals(final char actual, final char expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected); + } + }); + } + + public void assertEquals(final short actual, final short expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected, message); + } + }); + } + + public void assertEquals(final short actual, final short expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected); + } + }); + } + + public void assertEquals(final int actual, final int expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected, message); + } + }); + } + + public void assertEquals(final int actual, final int expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected); + } + }); + } + + public void assertNotNull(final Object object) { + doAssert( + new SimpleAssert(object, null) { + @Override + public void doAssert() { + org.testng.Assert.assertNotNull(object); + } + }); + } + + public void assertNotNull(final Object object, final String message) { + doAssert( + new SimpleAssert(object, null, message) { + @Override + public void doAssert() { + org.testng.Assert.assertNotNull(object, message); + } + }); + } + + public void assertNull(final Object object) { + doAssert( + new SimpleAssert(object, null) { + @Override + public void doAssert() { + org.testng.Assert.assertNull(object); + } + }); + } + + public void assertNull(final Object object, final String message) { + doAssert( + new SimpleAssert(object, null, message) { + @Override + public void doAssert() { + org.testng.Assert.assertNull(object, message); + } + }); + } + + public void assertSame(final Object actual, final Object expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertSame(actual, expected, message); + } + }); + } + + public void assertSame(final Object actual, final Object expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertSame(actual, expected); + } + }); + } + + public void assertNotSame(final Object actual, final Object expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertNotSame(actual, expected, message); + } + }); + } + + public void assertNotSame(final Object actual, final Object expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertNotSame(actual, expected); + } + }); + } + + public void assertEquals(final Collection actual, final Collection expected) { + doAssert( + new SimpleAssert>(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected); + } + }); + } + + public void assertEquals( + final Collection actual, final Collection expected, final String message) { + doAssert( + new SimpleAssert>(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected, message); + } + }); + } + + public void assertEquals(final Object[] actual, final Object[] expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected, message); + } + }); + } + + public void assertEqualsNoOrder( + final Object[] actual, final Object[] expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertEqualsNoOrder(actual, expected, message); + } + }); + } + + public void assertEquals(final Object[] actual, final Object[] expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected); + } + }); + } + + public void assertEqualsNoOrder(final Object[] actual, final Object[] expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertEqualsNoOrder(actual, expected); + } + }); + } + + public void assertEquals(final byte[] actual, final byte[] expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected); + } + }); + } + + public void assertEquals(final byte[] actual, final byte[] expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected, message); + } + }); + } + + public void assertEquals(final Set actual, final Set expected) { + doAssert( + new SimpleAssert>(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected); + } + }); + } + + public void assertEquals(final Set actual, final Set expected, final String message) { + doAssert( + new SimpleAssert>(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected, message); + } + }); + } + + public void assertEquals(final Map actual, final Map expected) { + doAssert( + new SimpleAssert>(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertEquals(actual, expected); + } + }); + } + + public void assertNotEquals(final Object actual, final Object expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected, message); + } + }); + } + + public void assertNotEquals(final Object actual, final Object expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected); + } + }); + } + + public void assertNotEquals(final String actual, final String expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected, message); + } + }); + } + + public void assertNotEquals(final String actual, final String expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected); + } + }); + } + + public void assertNotEquals(final long actual, final long expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected, message); + } + }); + } + + public void assertNotEquals(final long actual, final long expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected); + } + }); + } + + public void assertNotEquals(final boolean actual, final boolean expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected, message); + } + }); + } + + public void assertNotEquals(final boolean actual, final boolean expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected); + } + }); + } + + public void assertNotEquals(final byte actual, final byte expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected, message); + } + }); + } + + public void assertNotEquals(final byte actual, final byte expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected); + } + }); + } + + public void assertNotEquals(final char actual, final char expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected, message); + } + }); + } + + public void assertNotEquals(final char actual, final char expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected); + } + }); + } + + public void assertNotEquals(final short actual, final short expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected, message); + } + }); + } + + public void assertNotEquals(final short actual, final short expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected); + } + }); + } + + public void assertNotEquals(final int actual, final int expected, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected, message); + } + }); + } + + public void assertNotEquals(final int actual, final int expected) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected); + } + }); + } + + public void assertNotEquals( + final float actual, final float expected, final float delta, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected, delta, message); + } + }); + } + + public void assertNotEquals(final float actual, final float expected, final float delta) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected, delta); + } + }); + } + + public void assertNotEquals( + final double actual, final double expected, final double delta, final String message) { + doAssert( + new SimpleAssert(actual, expected, message) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected, delta, message); + } + }); + } + + public void assertNotEquals(final double actual, final double expected, final double delta) { + doAssert( + new SimpleAssert(actual, expected) { + @Override + public void doAssert() { + org.testng.Assert.assertNotEquals(actual, expected, delta); + } + }); + } + + /** + * * Override this method should you want to change the default way Throwable objects are logged. + * + * @param error Throwable of the Assertion + * @return default throwable formatted message for TestNG + */ + protected String getErrorDetails(Throwable error) { + StringBuilder sb = new StringBuilder(); + sb.append(error.getMessage()); + Throwable cause = error.getCause(); + while (cause != null) { + sb.append(" ").append(cause.getMessage()); + cause = cause.getCause(); + } + return sb.toString(); + } +} diff --git a/testng-asserts/src/main/java/org/testng/asserts/IAssert.java b/testng-asserts/src/main/java/org/testng/asserts/IAssert.java new file mode 100644 index 0000000..f6b3d42 --- /dev/null +++ b/testng-asserts/src/main/java/org/testng/asserts/IAssert.java @@ -0,0 +1,11 @@ +package org.testng.asserts; + +public interface IAssert { + String getMessage(); + + void doAssert(); + + T getActual(); + + T getExpected(); +} diff --git a/testng-asserts/src/main/java/org/testng/asserts/IAssertLifecycle.java b/testng-asserts/src/main/java/org/testng/asserts/IAssertLifecycle.java new file mode 100644 index 0000000..07399ce --- /dev/null +++ b/testng-asserts/src/main/java/org/testng/asserts/IAssertLifecycle.java @@ -0,0 +1,40 @@ +package org.testng.asserts; + +/** Life cycle methods for the assertion class. */ +public interface IAssertLifecycle { + /** + * Run the assert command in parameter. + * + * @param assertCommand The assertion + */ + void executeAssert(IAssert assertCommand); + + /** + * Invoked when an assert succeeds. + * + * @param assertCommand The assertion + */ + void onAssertSuccess(IAssert assertCommand); + + /** + * Invoked when an assert fails. + * + * @param assertCommand The assertion + * @param ex The error + */ + void onAssertFailure(IAssert assertCommand, AssertionError ex); + + /** + * Invoked before an assert is run. + * + * @param assertCommand The assertion + */ + void onBeforeAssert(IAssert assertCommand); + + /** + * Invoked after an assert is run. + * + * @param assertCommand The assertion + */ + void onAfterAssert(IAssert assertCommand); +} diff --git a/testng-asserts/src/main/java/org/testng/asserts/LoggingAssert.java b/testng-asserts/src/main/java/org/testng/asserts/LoggingAssert.java new file mode 100644 index 0000000..d54cdcb --- /dev/null +++ b/testng-asserts/src/main/java/org/testng/asserts/LoggingAssert.java @@ -0,0 +1,19 @@ +package org.testng.asserts; + +import java.util.List; +import org.testng.collections.Lists; + +/** Log the messages of all the assertions that get run. */ +public class LoggingAssert extends Assertion { + + private List m_messages = Lists.newArrayList(); + + @Override + public void onBeforeAssert(IAssert a) { + m_messages.add("Test:" + a.getMessage()); + } + + public List getMessages() { + return m_messages; + } +} diff --git a/testng-asserts/src/main/java/org/testng/asserts/SoftAssert.java b/testng-asserts/src/main/java/org/testng/asserts/SoftAssert.java new file mode 100644 index 0000000..3746eb0 --- /dev/null +++ b/testng-asserts/src/main/java/org/testng/asserts/SoftAssert.java @@ -0,0 +1,49 @@ +package org.testng.asserts; + +import java.util.Map; +import org.testng.collections.Maps; + +/** + * When an assertion fails, don't throw an exception but record the failure. Calling {@code + * assertAll()} will cause an exception to be thrown if at least one assertion failed. + */ +public class SoftAssert extends Assertion { + // LinkedHashMap to preserve the order + private final Map> m_errors = Maps.newLinkedHashMap(); + private static final String DEFAULT_SOFT_ASSERT_MESSAGE = "The following asserts failed:"; + + @Override + protected void doAssert(IAssert a) { + onBeforeAssert(a); + try { + a.doAssert(); + onAssertSuccess(a); + } catch (AssertionError ex) { + onAssertFailure(a, ex); + m_errors.put(ex, a); + } finally { + onAfterAssert(a); + } + } + + public void assertAll() { + assertAll(null); + } + + public void assertAll(String message) { + if (!m_errors.isEmpty()) { + StringBuilder sb = new StringBuilder(null == message ? DEFAULT_SOFT_ASSERT_MESSAGE : message); + boolean first = true; + for (AssertionError error : m_errors.keySet()) { + if (first) { + first = false; + } else { + sb.append(","); + } + sb.append("\n\t"); + sb.append(getErrorDetails(error)); + } + throw new AssertionError(sb.toString()); + } + } +} diff --git a/testng-asserts/src/main/java/org/testng/internal/EclipseInterface.java b/testng-asserts/src/main/java/org/testng/internal/EclipseInterface.java new file mode 100644 index 0000000..6fa81ba --- /dev/null +++ b/testng-asserts/src/main/java/org/testng/internal/EclipseInterface.java @@ -0,0 +1,18 @@ +package org.testng.internal; + +/** + * Symbols in this class are used by the Eclipse plug-in, do not modify them without updating the + * plug-in as well. + * + * @since Aug 25, 2012 + */ +public class EclipseInterface { + public static final Character OPENING_CHARACTER = '['; + public static final Character CLOSING_CHARACTER = ']'; + + public static final String ASSERT_EQUAL_LEFT = "expected " + OPENING_CHARACTER; + public static final String ASSERT_UNEQUAL_LEFT = "did not expect " + OPENING_CHARACTER; + public static final String ASSERT_LEFT2 = "expected not same " + OPENING_CHARACTER; + public static final String ASSERT_MIDDLE = CLOSING_CHARACTER + " but found " + OPENING_CHARACTER; + public static final String ASSERT_RIGHT = Character.toString(CLOSING_CHARACTER); +} diff --git a/testng-asserts/src/test/java/org/testng/AssertTest.java b/testng-asserts/src/test/java/org/testng/AssertTest.java new file mode 100644 index 0000000..59da30d --- /dev/null +++ b/testng-asserts/src/test/java/org/testng/AssertTest.java @@ -0,0 +1,665 @@ +package org.testng; + +import com.google.common.collect.ImmutableMap; +import java.util.*; +import org.testng.annotations.Test; +import org.testng.collections.Lists; +import org.testng.collections.Maps; +import org.testng.collections.Sets; +import testhelper.PerformanceUtils; + +public class AssertTest { + @Test + public void nullObjectArrayAssertEquals() { + Object[] expected = null; + Object[] actual = null; + Assert.assertEquals(actual, expected); + } + + @Test + public void nullObjectArrayAssertNoOrder() { + Object[] expected = null; + Object[] actual = null; + Assert.assertEqualsNoOrder(actual, expected); + } + + @Test + public void nullCollectionAssertEquals() { + Collection expected = null; + Collection actual = null; + Assert.assertEquals(actual, expected); + } + + @Test + public void nullSetAssertEquals() { + Set expected = null; + Set actual = null; + Assert.assertEquals(actual, expected); + } + + @Test + public void testCollectionAssertEquals() { + final Collection expected = + Sets.newHashSet(new Asymmetric(10, 'a'), new Asymmetric(11, 'b')); + final Collection actual = + Sets.newHashSet(new Asymmetric(10, 'a'), new Asymmetric(11, 'b')); + Assert.assertEquals(actual, expected); + } + + @Test(description = "GITHUB-2483", expectedExceptions = AssertionError.class) + public void testAsymetricNotEquals() { + AsymetricEquals equalsSame = AsymetricEquals.equalsSame(); + AsymetricEquals equalsAll = AsymetricEquals.equalsAll(); + // sanity + Assert.assertFalse(equalsSame.equals(equalsAll)); + Assert.assertTrue(equalsAll.equals(equalsSame)); + // actual check + Assert.assertNotEquals(equalsSame, equalsAll); + } + + @Test + public void testListAssertNotEquals() { + final Collection expected = + Lists.newArrayList(new Asymmetric(10, 'a'), new Asymmetric(11, 'b')); + final Collection actual = + Lists.newArrayList(new Asymmetric(11, 'b'), new Asymmetric(10, 'a')); + Assert.assertNotEquals(actual, expected); + } + + @Test + public void testSetAssertEquals() { + final Set expected = + Sets.newHashSet(new Asymmetric(10, 'a'), new Asymmetric(11, 'b')); + final Set actual = + Sets.newHashSet(new Asymmetric(10, 'a'), new Asymmetric(11, 'b')); + Assert.assertEquals(actual, expected); + } + + @Test + public void nullMapAssertEquals() { + Map expected = null; + Map actual = null; + Assert.assertEquals(actual, expected); + } + + @Test + public void setAssertEquals() { + Set expected = Sets.newHashSet("a", 1); + Set actual = Sets.newHashSet("a", 1); + + Assert.assertEquals(actual, expected); + } + + @Test + public void mapAssertEquals() { + Map expected = Maps.newHashMap(); + Map actual = Maps.newHashMap(); + + expected.put(null, "a"); + expected.put("a", "a"); + expected.put("b", "c"); + actual.put("b", "c"); + actual.put(null, "a"); + actual.put("a", "a"); + + Assert.assertEquals(actual, expected); + } + + @Test(description = "GITHUB-2211") + public void mapAssertEqualsWithMessage() { + String expected = "User provided message"; + String actual = ""; + try { + Assert.assertEquals(ImmutableMap.of("k", "v"), ImmutableMap.of(), expected); + } catch (AssertionError error) { + actual = error.getMessage(); + } + Assert.assertEquals(actual, expected); + } + + @Test + public void oneNullMapAssertEquals() { + Map expected = Maps.newHashMap(); + Map actual = null; + boolean failed = true; + try { + Assert.assertEquals(actual, expected); + failed = false; + } catch (AssertionError error) { + // do nothing + } + Assert.assertTrue(failed, "assertEquals did not fail"); + } + + @Test + public void oneNullSetAssertEquals() { + Set expected = null; + Set actual = Sets.newHashSet(); + boolean failed = true; + try { + Assert.assertEquals(actual, expected); + failed = false; + } catch (AssertionError error) { + // do nothing + } + Assert.assertTrue(failed, "assertEquals did not fail"); + } + + /** + * Testing comparison algorithm using big arrays. + * + * @see Issue #1384 – Huge performance + * issue between 6.5.2 and 6.11 + */ + @Test + public void compareLargeArrays() { + if (!PerformanceUtils.canMeasureAllocatedMemory()) { + throw new SkipException( + "PerformanceUtils.canMeasureAllocatedMemory returns false, skipping test"); + } + int length = 1024 * 100; + byte[] first = new byte[length]; + byte[] second = new byte[length]; + + Random rnd = new Random(); + rnd.nextBytes(first); + System.arraycopy(first, 0, second, 0, length); + + long before = PerformanceUtils.measureAllocatedMemory(); + Assert.assertEquals(first, second); + long memoryUsage = PerformanceUtils.measureAllocatedMemory() - before; + + // assertEquals() with primitive type arrays requires ~65Kb of memory + // assertEquals() with Object-type requires ~3Mb of memory when comparing 100Kb arrays. + // choosing 100Kb as a threshold + Assert.assertTrue( + memoryUsage < 100 * 1024, "Amount of used memory should be approximately 65Kb"); + } + + @Test + public void compareShortArrays() { + short[] actual = {Short.MIN_VALUE, 0, Short.MAX_VALUE}; + short[] expected = {Short.MIN_VALUE, 0, Short.MAX_VALUE}; + Assert.assertEquals(actual, expected); + } + + @Test + public void compareIntArrays() { + int[] actual = {Integer.MIN_VALUE, 0, Integer.MAX_VALUE}; + int[] expected = {Integer.MIN_VALUE, 0, Integer.MAX_VALUE}; + Assert.assertEquals(actual, expected); + } + + @Test + public void compareLongArrays() { + long[] actual = {Long.MIN_VALUE, 0, Long.MAX_VALUE}; + long[] expected = {Long.MIN_VALUE, 0, Long.MAX_VALUE}; + Assert.assertEquals(actual, expected); + } + + @Test + public void compareBooleanArrays() { + boolean[] actual = {true, false}; + boolean[] expected = {true, false}; + Assert.assertEquals(actual, expected); + } + + @Test + public void compareCharacterArrays() { + char[] actual = {'a', '1', '#'}; + char[] expected = {'a', '1', '#'}; + Assert.assertEquals(actual, expected); + } + + @Test + public void compareFloatArrays() { + float[] actual = { + (float) Math.PI, + (float) Math.E, + Float.MIN_VALUE, + Float.MIN_NORMAL, + Float.MAX_VALUE, + Float.POSITIVE_INFINITY, + Float.NEGATIVE_INFINITY + }; + float[] expected = { + (float) Math.PI, + (float) Math.E, + Float.MIN_VALUE, + Float.MIN_NORMAL, + Float.MAX_VALUE, + Float.POSITIVE_INFINITY, + Float.NEGATIVE_INFINITY + }; + Assert.assertEquals(actual, expected); + } + + @Test + public void compareFloatArraysWithDelta() { + float[] actual = {0.1f, 0.2f, 0.3f, 0.4f}; + float[] expected = {0.5f, 0.7f, 0.1f, 0.2f}; + Assert.assertEquals(actual, expected, 0.5f); + } + + @Test(expectedExceptions = AssertionError.class) + public void compareUnEqualFloatArraysWithDelta() { + float[] actual = {0.1f, 0.2f, 0.3f, 0.4f}; + float[] expected = {0.5f, 0.7f, 0.1f, 0.2f}; + Assert.assertEquals(actual, expected, 0.1f); + } + + @Test + public void compareFloatArraysWithNaNValues() { + Assert.assertEquals(new float[] {Float.NaN}, new float[] {Float.NaN}); + } + + @Test + public void compareFloatWithNaNValues() { + Assert.assertEquals(Float.NaN, Float.NaN); + } + + @Test + public void compareDoubleArrays() { + double[] actual = { + Math.PI, + Math.E, + Double.MIN_VALUE, + Double.MIN_NORMAL, + Double.MAX_VALUE, + Double.POSITIVE_INFINITY, + Double.NEGATIVE_INFINITY + }; + double[] expected = { + Math.PI, + Math.E, + Double.MIN_VALUE, + Double.MIN_NORMAL, + Double.MAX_VALUE, + Double.POSITIVE_INFINITY, + Double.NEGATIVE_INFINITY + }; + Assert.assertEquals(actual, expected); + } + + @Test + public void compareDoubleArraysWithNaNValues() { + Assert.assertEquals(new double[] {Double.NaN}, new double[] {Double.NaN}); + } + + @Test + public void compareDoubleWithNaNValues() { + Assert.assertEquals(Double.NaN, Double.NaN); + } + + @Test + public void compareDoubleArraysWithDelta() { + double[] actual = {0.1d, 0.2d, 0.3d, 0.4d}; + double[] expected = {0.5d, 0.7d, 0.1d, 0.2d}; + Assert.assertEquals(actual, expected, 0.5d); + } + + @Test(expectedExceptions = AssertionError.class) + public void compareUnEqualDoubleArraysWithDelta() { + double[] actual = {0.1d, 0.2d, 0.3d, 0.4d}; + double[] expected = {0.5d, 0.7d, 0.1d, 0.2d}; + Assert.assertEquals(actual, expected, 0.1d); + } + + @SuppressWarnings("serial") + @Test(expectedExceptions = AssertionError.class) + public void assertEqualsMapShouldFail() { + Map mapActual = + new HashMap() { + { + put("a", "1"); + } + }; + Map mapExpected = + new HashMap() { + { + put("a", "1"); + put("b", "2"); + } + }; + + Assert.assertEquals(mapActual, mapExpected); + } + + @Test(expectedExceptions = AssertionError.class) + public void assertEqualsSymmetricScalar() { + Assert.assertEquals(new Asymmetric(42, 'd'), new Contrived(42)); + } + + @Test(expectedExceptions = AssertionError.class) + public void assertEqualsSymmetricArrays() { + Object[] actual = {1, new Asymmetric(42, 'd'), "inDay"}; + Object[] expected = {1, new Contrived(42), "inDay"}; + Assert.assertEquals(actual, expected); + } + + @Test(expectedExceptions = AssertionError.class) + public void testAssertEqualsWithActualBrokenEqualsTrue() { + // BrokenEqualsTrue.equals(Object) always returns true, even for null + // However, Assert is not supposed to check equals(Object) implementation, so assertEquals + // should fail + Assert.assertEquals(new BrokenEqualsTrue(), null); + } + + @Test(expectedExceptions = AssertionError.class) + public void testAssertEqualsWithExpectedBrokenEqualsTrue() { + // BrokenEqualsTrue.equals(Object) always returns true, even for null + // However, Assert is not supposed to check equals(Object) implementation, so assertEquals + // should fail + Assert.assertEquals(null, new BrokenEqualsTrue()); + } + + @Test + public void testAssertEqualsWithNull() { + Object obj = null; + Assert.assertEquals(obj, obj); + } + + @Test( + description = "GITHUB-1935", + expectedExceptions = AssertionError.class, + expectedExceptionsMessageRegExp = "expected \\[y\\] but found \\[x\\]") + public void testInequalityMessage() { + Assert.assertEquals("x", "y"); + } + + @Test + public void testAssertEqualsNoOrder() { + String[] actual = {"a", "b"}; + String[] expected = {"b", "a"}; + Assert.assertEqualsNoOrder(actual, expected); + } + + @Test + public void testAssertEqualsNoOrderWithEmpty() { + Assert.assertEqualsNoOrder(new String[0], new String[0]); + } + + @Test + public void testAssertEqualsNoOrderWithDifferentElementType() { + Assert.assertEqualsNoOrder(new String[0], new Object[0]); + Object[] actual = {"a"}; + String[] expected = {"a"}; + Assert.assertEqualsNoOrder(actual, expected); + } + + @Test(expectedExceptions = AssertionError.class) + public void testAssertEqualsNoOrderWithDuplicate() { + String[] actual = {"a"}; + String[] expected = {"a", "a"}; + Assert.assertEqualsNoOrder(actual, expected); + } + + @Test + public void testAssertEqualsNoOrderWithBothNull() { + Assert.assertEqualsNoOrder((Object[]) null, (Object[]) null); + } + + @Test(expectedExceptions = AssertionError.class) + public void testAssertEqualsNoOrderWithActualNull() { + Assert.assertEqualsNoOrder(null, new String[0]); + } + + @Test(expectedExceptions = AssertionError.class) + public void testAssertEqualsNoOrderWithExpectedNull() { + Assert.assertEqualsNoOrder(new String[0], null); + } + + @Test(description = "GITHUB-2500", expectedExceptions = AssertionError.class) + public void testAssertEqualsNoOrderNotDeep() { + // assertEqualsNoOrder does not compare arrays deeply, so elements which are + // arrays as well are only compared for reference equality + String[][] actual = {{}}; + String[][] expected = {{}}; + Assert.assertEqualsNoOrder(actual, expected); + } + + @Test( + description = "GITHUB-2080", + expectedExceptions = AssertionError.class, + expectedExceptionsMessageRegExp = "test expected \\[true\\] but found \\[false\\]") + public void testAssertTrueMessage() { + Assert.assertTrue(false, "test"); + } + + @Test( + description = "GITHUB-2080", + expectedExceptions = AssertionError.class, + expectedExceptionsMessageRegExp = "test expected \\[false\\] but found \\[true\\]") + public void testAssertFalseMessage() { + Assert.assertFalse(true, "test"); + } + + @Test( + description = "GITHUB-2080", + expectedExceptions = AssertionError.class, + expectedExceptionsMessageRegExp = "did not expect \\[x\\] but found \\[x\\]") + public void testEqualityMessage() { + Assert.assertNotEquals("x", "x"); + } + + @Test( + description = "GITHUB-2118", + expectedExceptions = AssertionError.class, + expectedExceptionsMessageRegExp = "did not expect \\[1.5\\] but found \\[1.5\\]") + public void testAssertNotEqualsFloat() { + Assert.assertNotEquals(1.5f, 1.5f, 0.5f); + } + + @Test( + description = "GITHUB-2118", + expectedExceptions = AssertionError.class, + expectedExceptionsMessageRegExp = "did not expect \\[1.5\\] but found \\[1.5\\]") + public void testAssertNotEqualsDouble() { + Assert.assertNotEquals(1.5, 1.5, 0.5); + } + + @Test( + description = "GITHUB-2118", + expectedExceptions = AssertionError.class, + expectedExceptionsMessageRegExp = + "did not expect \\[\\[test\\]\\] but found \\[\\[test\\]\\]") + public void testAssertNotEqualsSet() { + Set set = new HashSet<>(); + set.add("test"); + Assert.assertNotEquals(set, set); + } + + @Test( + description = "GITHUB-2118", + expectedExceptions = AssertionError.class, + expectedExceptionsMessageRegExp = + "did not expect \\[\\[test\\]\\] but found \\[\\[test\\]\\]") + public void testAssertNotEqualsDeepSet() { + Set set = new HashSet<>(); + set.add("test"); + Assert.assertNotEqualsDeep(set, set); + } + + @Test( + description = "GITHUB-2118", + expectedExceptions = AssertionError.class, + expectedExceptionsMessageRegExp = + "did not expect \\[\\{key=value\\}\\] but found \\[\\{key=value\\}\\]") + public void testAssertNotEqualsMap() { + Map map = new HashMap<>(); + map.put("key", "value"); + Assert.assertNotEquals(map, map); + } + + @Test( + description = "GITHUB-2118", + expectedExceptions = AssertionError.class, + expectedExceptionsMessageRegExp = + "did not expect \\[\\{key=value\\}\\] but found \\[\\{key=value\\}\\]") + public void testAssertNotEqualsDeepMap() { + Map map = new HashMap<>(); + map.put("key", "value"); + Assert.assertNotEqualsDeep(map, map); + } + + @Test(description = "GITHUB-2486") + public void testAssertNotSame() { + Contrived object = new Contrived(2); + Contrived object2 = new Contrived(2); + Assert.assertNotSame(object2, object); + } + + @Test(description = "GITHUB-2486", expectedExceptions = AssertionError.class) + public void testAssertSame() { + Contrived object = new Contrived(2); + Contrived object2 = new Contrived(2); + Assert.assertSame(object2, object); + } + + @Test(description = "GITHUB-2490") + public void testAssertNotEqualsWithActualBrokenEqualsTrue() { + // BrokenEqualsTrue.equals(Object) always returns true, even for null + // However, Assert is not supposed to check equals(Object) implementation, so assertNotEquals + // should succeed + Assert.assertNotEquals(new BrokenEqualsTrue(), null); + } + + @Test(description = "GITHUB-2490") + public void testAssertNotEqualsWithExpectedBrokenEqualsTrue() { + // BrokenEqualsTrue.equals(Object) always returns true, even for null + // However, Assert is not supposed to check equals(Object) implementation, so assertNotEquals + // should succeed + Assert.assertNotEquals(null, new BrokenEqualsTrue()); + } + + @Test(description = "GITHUB-2490") + public void testAssertNotEqualsWithActualBrokenEqualsFalse() { + // BrokenEqualsFalse.equals(Object) always returns false; assertNotEquals should fail + Assert.assertNotEquals(new BrokenEqualsFalse(), null); + } + + @Test(description = "GITHUB-2490") + public void testAssertNotEqualsWithExpectedBrokenEqualsFalse() { + // BrokenEqualsFalse.equals(Object) always returns false; assertNotEquals should fail + Assert.assertNotEquals(null, new BrokenEqualsFalse()); + } + + @Test(description = "GITHUB-2490") + public void testAssertNotEqualsWithExpectedNullAndNewObject() { + Assert.assertNotEquals(new Object(), null); + } + + @Test + public void testAssertNotEqualsWithActualNullExceptedEmptyString() { + Assert.assertNotEquals(null, ""); + } + + @Test + public void testAssertNotEqualsWithActualEmptyStringExceptedNull() { + Assert.assertNotEquals("", null); + } + + @Test(expectedExceptions = AssertionError.class) + public void testAssertNotEqualsWithNull() { + Object obj = null; + Assert.assertNotEquals(obj, obj); + } + + class Contrived { + + int integer; + + Contrived(int integer) { + this.integer = integer; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Contrived)) return false; + + Contrived contrived = (Contrived) o; + + return integer == contrived.integer; + } + + @Override + public int hashCode() { + return integer; + } + } + + class Asymmetric extends Contrived { + + char character; + + Asymmetric(int integer, char character) { + super(integer); + this.character = character; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Asymmetric)) return false; + if (!super.equals(o)) return false; + + Asymmetric that = (Asymmetric) o; + + return character == that.character; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (int) character; + return result; + } + } + + static class AsymetricEquals { + + static AsymetricEquals equalsAll() { + return new AsymetricEquals(null); + } + + static AsymetricEquals equalsSame() { + return new AsymetricEquals(new Object()); + } + + Object value; + + private AsymetricEquals(Object value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof AsymetricEquals)) return false; + + AsymetricEquals that = (AsymetricEquals) o; + if (value == null) return true; + return value.equals(that.value); + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + } + + static class BrokenEqualsTrue { + @Override + public boolean equals(Object o) { + return true; // broken implementation + } + } + + static class BrokenEqualsFalse { + @Override + public boolean equals(Object o) { + return false; // broken implementation + } + } +} diff --git a/testng-asserts/src/test/java/test/assertion/AssertionTest.java b/testng-asserts/src/test/java/test/assertion/AssertionTest.java new file mode 100644 index 0000000..25825b3 --- /dev/null +++ b/testng-asserts/src/test/java/test/assertion/AssertionTest.java @@ -0,0 +1,58 @@ +package test.assertion; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.testng.asserts.LoggingAssert; + +public class AssertionTest { + private LoggingAssert m_assert; + private MyRawAssertion rawAssertion; + + @BeforeMethod + public void bm() { + m_assert = new LoggingAssert(); + rawAssertion = new MyRawAssertion(); + } + + @Test(expectedExceptions = AssertionError.class) + public void test1() { + m_assert.assertTrue(false, "new TestNG Assertion Failed"); + } + + @Test + public void test2() { + rawAssertion.assertTrue(true); + rawAssertion.myAssert("test", true, "Raw test"); + + assertThat(rawAssertion.getMethods()) + .containsExactly( + "onBeforeAssert", + "onAssertSuccess", + "onAfterAssert", + "onBeforeAssert", + "onAssertSuccess", + "onAfterAssert"); + } + + @Test(expectedExceptions = AssertionError.class, expectedExceptionsMessageRegExp = "Raw test .*") + public void test2_fails() { + try { + rawAssertion.assertTrue(true); + rawAssertion.myAssert("test", false, "Raw test"); + } catch (AssertionError error) { + + assertThat(rawAssertion.getMethods()) + .containsExactly( + "onBeforeAssert", + "onAssertSuccess", + "onAfterAssert", + "onBeforeAssert", + "onAssertFailure", + "onAfterAssert"); + + throw error; + } + } +} diff --git a/testng-asserts/src/test/java/test/assertion/MyRawAssertion.java b/testng-asserts/src/test/java/test/assertion/MyRawAssertion.java new file mode 100644 index 0000000..08b6ed2 --- /dev/null +++ b/testng-asserts/src/test/java/test/assertion/MyRawAssertion.java @@ -0,0 +1,66 @@ +package test.assertion; + +import java.util.ArrayList; +import java.util.List; +import org.testng.Assert; +import org.testng.asserts.Assertion; +import org.testng.asserts.IAssert; + +public class MyRawAssertion extends Assertion { + + private final List methods = new ArrayList<>(); + + @Override + public void onAssertSuccess(IAssert assertCommand) { + methods.add("onAssertSuccess"); + super.onAssertSuccess(assertCommand); + } + + @Override + public void onAssertFailure(IAssert assertCommand, AssertionError ex) { + methods.add("onAssertFailure"); + super.onAssertFailure(assertCommand, ex); + } + + @Override + public void onBeforeAssert(IAssert assertCommand) { + methods.add("onBeforeAssert"); + super.onBeforeAssert(assertCommand); + } + + @Override + public void onAfterAssert(IAssert assertCommand) { + methods.add("onAfterAssert"); + super.onAfterAssert(assertCommand); + } + + public List getMethods() { + return methods; + } + + public void myAssert(final String actual, final boolean expected, final String message) { + doAssert( + new IAssert() { + @Override + public String getMessage() { + return message; + } + + @Override + public void doAssert() { + Assert.assertNotNull(actual, message); + Assert.assertTrue(expected, message); + } + + @Override + public Object getActual() { + return actual; + } + + @Override + public Object getExpected() { + return expected; + } + }); + } +} diff --git a/testng-asserts/src/test/java/test/assertion/SoftAssertTest.java b/testng-asserts/src/test/java/test/assertion/SoftAssertTest.java new file mode 100644 index 0000000..23a4b91 --- /dev/null +++ b/testng-asserts/src/test/java/test/assertion/SoftAssertTest.java @@ -0,0 +1,98 @@ +package test.assertion; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collection; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.testng.asserts.IAssert; +import org.testng.asserts.SoftAssert; + +public class SoftAssertTest { + + @Test + public void testOnSucceedAndFailureCalled() { + final Collection succeed = new ArrayList<>(); + final Collection failures = new ArrayList<>(); + final SoftAssert sa = + new SoftAssert() { + @Override + public void onAssertSuccess(IAssert assertCommand) { + succeed.add(assertCommand); + } + + @Override + public void onAssertFailure(IAssert assertCommand, AssertionError ex) { + failures.add(assertCommand); + } + }; + sa.assertTrue(true); + sa.assertTrue(false); + Assert.assertEquals(succeed.size(), 1, succeed.toString()); + Assert.assertEquals(failures.size(), 1, failures.toString()); + } + + @DataProvider(name = "messages") + public Object[][] getMessages() { + String hasMessage = "msg"; + return new Object[][] {{hasMessage, hasMessage}, {null, "The following asserts failed:"}}; + } + + @Test(dataProvider = "messages") + public void testDefaultMessage(String actualMsg, String expectedMsg) { + try { + final SoftAssert sa = new SoftAssert(); + sa.assertTrue(false); + sa.assertAll(actualMsg); + Assert.fail(); + } catch (AssertionError exc) { + Assert.assertNotNull(exc.getMessage()); + Assert.assertTrue(exc.getMessage().startsWith(expectedMsg)); + } + } + + @Test + public void testAssertAllCount() { + String message = "My message"; + SoftAssert sa = new SoftAssert(); + sa.assertTrue(true); + sa.assertTrue(false, message); + try { + sa.assertAll(); + Assert.fail("Exception expected"); + } catch (AssertionError e) { + String[] lines = e.getMessage().split("\r?\n"); + Assert.assertEquals(lines.length, 2); + lines[1] = lines[1].replaceFirst(message, ""); + Assert.assertFalse(lines[1].contains(message)); + } + } + + private static final String hiddenMsg = "hidden msg"; + + @Test(description = "GITHUB-1778", dataProvider = "dp") + public void testRootCauseInSoftFail(Exception hiddenExc) { + try { + SoftAssert s = new SoftAssert(); + s.fail("soft assert with missing root cause", hiddenExc); + s.assertAll(); + } catch (AssertionError soft) { + assertThrowableContainsText(soft, hiddenMsg); + } + } + + @DataProvider(name = "dp") + public Object[][] getExceptions() { + return new Object[][] {{new Exception(hiddenMsg)}, {new Exception(new Exception(hiddenMsg))}}; + } + + private static void assertThrowableContainsText(Throwable hard, String text) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + hard.printStackTrace(pw); + String stackTrace = sw.toString(); + Assert.assertTrue(stackTrace.contains(text)); + } +} diff --git a/testng-asserts/src/test/java/test/asserttests/ArrayEqualityAssertTest.java b/testng-asserts/src/test/java/test/asserttests/ArrayEqualityAssertTest.java new file mode 100644 index 0000000..e6d8665 --- /dev/null +++ b/testng-asserts/src/test/java/test/asserttests/ArrayEqualityAssertTest.java @@ -0,0 +1,334 @@ +package test.asserttests; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertEqualsDeep; +import static org.testng.Assert.assertNotEquals; +import static org.testng.Assert.assertNotEqualsDeep; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.testng.annotations.Test; + +/** Tests different equality cases for nested collections and arrays. */ +public class ArrayEqualityAssertTest { + + @Test + public void arrayAssertEquals() { + assertEquals( + new int[] {42}, + new int[] {42}, + "arrays of primitives are compared by value in assertEquals"); + } + + @Test(expectedExceptions = AssertionError.class) + public void arrayAssertNotEquals() { + assertNotEquals( + new int[] {42}, + new int[] {42}, + "arrays of primitives are compared by value in assertNotEquals"); + } + + @Test + public void boxedArrayAssertEquals() { + assertEquals( + new Integer[] {42}, + new Integer[] {42}, + "arrays of wrapped values are compared by value in assertEquals"); + } + + @Test(expectedExceptions = AssertionError.class) + public void boxedArrayAssertNotEquals() { + assertNotEquals( + new Integer[] {42}, + new Integer[] {42}, + "arrays of wrapped values are compared by value in assertNotEquals"); + } + + @Test + public void mixedArraysAssertEquals() { + assertEquals( + new int[] {42}, + new Integer[] {42}, + "arrays of wrapped values are compared by value in assertEquals"); + assertEquals( + new Integer[] {42}, + new int[] {42}, + "arrays of wrapped values are compared by value in assertEquals"); + } + + @Test(expectedExceptions = AssertionError.class) + public void mixedArraysAssertNotEquals() { + assertNotEquals( + new int[] {42}, + new Integer[] {42}, + "arrays of wrapped values are compared by value in assertNotEquals"); + assertNotEquals( + new Integer[] {42}, + new int[] {42}, + "arrays of wrapped values are compared by value in assertNotEquals"); + } + + @Test(expectedExceptions = AssertionError.class) + public void arrayInsideListAssertEquals() { + List list = Arrays.asList(new int[] {42}); + List listCopy = Arrays.asList(new int[] {42}); + assertEquals(list, listCopy, "arrays inside lists are compared by reference in assertEquals"); + } + + @Test + public void arrayInsideListAssertNotEquals() { + List list = Arrays.asList(new int[] {42}); + List listCopy = Arrays.asList(new int[] {42}); + assertNotEquals( + list, listCopy, "arrays inside lists are compared by reference in assertNotEquals"); + } + + @Test(expectedExceptions = AssertionError.class) + public void arrayInsideMapAssertEquals() { + Map map = new HashMap<>(); + map.put("array", new int[] {42}); + Map mapCopy = new HashMap<>(); + mapCopy.put("array", new int[] {42}); + + // arrays inside maps are compared by reference in assertEquals(Map,Map) + assertEquals(map, mapCopy); + } + + @Test(expectedExceptions = AssertionError.class) + public void arrayInsideMapAssertEqualsWithMessage() { + Map map = new HashMap<>(); + map.put("array", new int[] {42}); + Map mapCopy = new HashMap<>(); + mapCopy.put("array", new int[] {42}); + + assertEquals( + map, + mapCopy, + "arrays inside maps are compared by reference in assertEquals(Map,Map,String)"); + } + + @Test + public void arrayInsideMapAssertNotEquals() { + Map map = new HashMap<>(); + map.put("array", new int[] {42}); + Map mapCopy = new HashMap<>(); + mapCopy.put("array", new int[] {42}); + + assertNotEquals( + map, mapCopy, "arrays inside maps are compared by reference in assertNotEquals"); + } + + @Test + public void arrayInsideMapAssertEqualsDeep() { + Map map = new HashMap<>(); + map.put("array", new int[] {42}); + Map mapCopy = new HashMap<>(); + mapCopy.put("array", new int[] {42}); + + // arrays inside maps are compared by value in assertEqualsDeep(Map,Map) + assertEqualsDeep(map, mapCopy); + } + + @Test + public void arrayInsideMapAssertEqualsDeepWithMessage() { + Map map = new HashMap<>(); + map.put("array", new int[] {42}); + Map mapCopy = new HashMap<>(); + mapCopy.put("array", new int[] {42}); + + assertEqualsDeep( + map, + mapCopy, + "arrays inside maps are compared by value in assertEqualsDeep(Map,Map,String)"); + } + + @Test(expectedExceptions = AssertionError.class) + public void arrayInsideMapAssertNotEqualsDeep() { + Map map = new HashMap<>(); + map.put("array", new int[] {42}); + Map mapCopy = new HashMap<>(); + mapCopy.put("array", new int[] {42}); + + assertNotEqualsDeep( + map, mapCopy, "arrays inside maps are compared by value in assertNotEqualsDeep"); + } + + @Test(expectedExceptions = AssertionError.class) + public void arrayInsideSetAssertEquals() { + Set set = new HashSet<>(); + set.add(new int[] {42}); + Set setCopy = new HashSet<>(); + setCopy.add(new int[] {42}); + + assertEquals(set, setCopy, "arrays inside sets are compared by reference in assertNotEquals"); + } + + @Test + public void arrayInsideSetAssertNotEquals() { + Set set = new HashSet<>(); + set.add(new int[] {42}); + Set setCopy = new HashSet<>(); + setCopy.add(new int[] {42}); + + assertNotEquals( + set, setCopy, "arrays inside sets are compared by reference in assertNotEquals"); + } + + @Test + public void arrayInsideSetAssertEqualsDeep() { + Set set = new HashSet<>(); + set.add(new int[] {42}); + Set setCopy = new HashSet<>(); + setCopy.add(new int[] {42}); + + assertEqualsDeep(set, setCopy, "arrays inside sets are compared by value in assertEqualsDeep"); + } + + @Test(expectedExceptions = AssertionError.class) + public void arrayDeepInSetsAssertEqualsForExpectedLessThanActual() { + Set actual = new HashSet<>(); + actual.add("42"); + actual.add("43"); + Set expected = new HashSet<>(); + expected.add("42"); + + assertEqualsDeep(actual, expected, "Sets not equal:"); + } + + @Test(expectedExceptions = AssertionError.class) + public void arrayDeepInSetsAssertEqualsForActualLessThanExpected() { + Set actual = new HashSet<>(); + actual.add("42"); + Set expected = new HashSet<>(); + expected.add("42"); + expected.add("43"); + + assertEqualsDeep(actual, expected, "Sets not equal:"); + } + + @Test(expectedExceptions = AssertionError.class) + public void arrayInsideSetAssertNotEqualsDeep() { + Set set = new HashSet<>(); + set.add(new int[] {42}); + Set setCopy = new HashSet<>(); + setCopy.add(new int[] {42}); + + assertNotEqualsDeep( + set, setCopy, "arrays inside sets are compared by value in assertNotEqualsDeep"); + } + + @Test(expectedExceptions = AssertionError.class) + public void arrayDeepInListsAssertEquals() { + List> list = Collections.singletonList(Arrays.asList(new int[] {42})); + List> listCopy = Collections.singletonList(Arrays.asList(new int[] {42})); + + assertEquals( + list, + listCopy, + "arrays inside lists which are inside lists themselves are compared by reference in assertEquals"); + } + + @Test(expectedExceptions = AssertionError.class) + public void arrayDeepInMapsAssertEquals() { + Map> map = new HashMap<>(); + Map innerMap = new HashMap<>(); + innerMap.put("array", new int[] {42}); + map.put("map", innerMap); + Map> mapCopy = new HashMap<>(); + Map innerMapCopy = new HashMap<>(); + innerMapCopy.put("array", new int[] {42}); + mapCopy.put("map", innerMapCopy); + + assertEquals( + map, + mapCopy, + "arrays inside maps which are inside maps themselves are compared by reference in assertEquals"); + } + + @Test(expectedExceptions = AssertionError.class) + public void arrayDeepInListAndMapAssertEquals() { + List> list = new ArrayList<>(); + Map innerMap = new HashMap<>(); + innerMap.put("array", new int[] {42}); + list.add(innerMap); + List> listCopy = new ArrayList<>(); + Map innerMapCopy = new HashMap<>(); + innerMapCopy.put("array", new int[] {42}); + list.add(innerMapCopy); + + assertEquals( + list, + listCopy, + "arrays inside maps which are inside lists themselves are compared by reference in assertEquals"); + } + + @Test(expectedExceptions = AssertionError.class) + public void arrayDeepInMapAndListAssertEquals() { + Map> map = new HashMap<>(); + map.put("list", Arrays.asList(new int[] {42})); + Map> mapCopy = new HashMap<>(); + mapCopy.put("list", Arrays.asList(new int[] {42})); + + assertEquals( + map, + mapCopy, + "arrays inside maps which are inside lists themselves are compared by reference in assertEquals"); + } + + @Test(expectedExceptions = AssertionError.class) + public void arrayInsideIterableAssertEquals() { + Iterable iterable = Arrays.asList(new int[] {42}); + Iterable iterableCopy = Arrays.asList(new int[] {42}); + assertEquals( + iterable, + iterableCopy, + "arrays inside Iterables are compared by reference in assertEquals"); + } + + @Test(expectedExceptions = AssertionError.class) + public void arrayDeepInIterablesAssertEquals() { + List> iterable = Collections.singletonList(Arrays.asList(new int[] {42})); + List> iterableCopy = Collections.singletonList(Arrays.asList(new int[] {42})); + + assertEquals( + iterable, + iterableCopy, + "arrays inside Iterables which are inside Iterables themselves are compared by reference in assertEquals"); + } + + @Test + public void arrayInsideArrayAssertEquals() { + int[][] array = new int[][] {new int[] {42}}; + int[][] arrayCopy = new int[][] {new int[] {42}}; + assertEquals(array, arrayCopy, "arrays inside arrays are compared by value in assertEquals"); + } + + @Test + public void arrayDeepInArraysAssertEquals() { + int[][][] array = new int[][][] {new int[][] {new int[] {42}}}; + int[][][] arrayCopy = new int[][][] {new int[][] {new int[] {42}}}; + + assertEquals( + array, + arrayCopy, + "arrays inside arrays which are inside arrays themselves are compared by value in assertEquals"); + } + + @SuppressWarnings("unchecked") + @Test(expectedExceptions = AssertionError.class) + public void arrayDeepInArrayAndListAssertEquals() { + List[] array = new List[] {Arrays.asList(new int[] {42})}; + List[] arrayCopy = new List[] {Arrays.asList(new int[] {42})}; + + assertEquals( + array, + arrayCopy, + "arrays inside arrays which are inside arrays themselves are compared by reference in assertEquals"); + } +} diff --git a/testng-asserts/src/test/java/test/asserttests/AssertTest.java b/testng-asserts/src/test/java/test/asserttests/AssertTest.java new file mode 100644 index 0000000..bc4fc3a --- /dev/null +++ b/testng-asserts/src/test/java/test/asserttests/AssertTest.java @@ -0,0 +1,483 @@ +package test.asserttests; + +import static org.testng.Assert.*; + +import java.io.IOException; +import java.util.*; +import org.testng.Assert; +import org.testng.Assert.ThrowingRunnable; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.testng.collections.Sets; + +public class AssertTest { + + @Test(description = "GITHUB-2652") + public void assertEqualsBoxedUnboxedDouble() { + Double a = 3.14; + double b = 3.14; + Double deltaA = 0.1; + double deltaB = 0.1; + assertEquals(a, b, deltaA); + assertEquals(a, b, deltaB); + assertEquals(a, b); + assertEquals(a, b, ""); + assertEquals(b, a); + assertEquals(b, a, ""); + } + + @Test(description = "GITHUB-2652") + public void assertEqualsBoxedUnboxedFloat() { + Float a = 3.1f; + float b = 3.1f; + Float deltaA = 0.1f; + float deltaB = 0.1f; + assertEquals(a, b, deltaA); + assertEquals(a, b, deltaB); + assertEquals(a, b); + assertEquals(a, b, ""); + assertEquals(b, a); + assertEquals(b, a, ""); + } + + @Test(description = "GITHUB-2652") + public void assertEqualsBoxedUnboxedLong() { + Long a = 3L; + long b = 3L; + Long deltaA = 1L; + long deltaB = 1L; + assertEquals(a, b, deltaA); + assertEquals(a, b, deltaB); + assertEquals(a, b); + assertEquals(a, b, ""); + assertEquals(b, a); + assertEquals(b, a, ""); + } + + @Test(description = "GITHUB-2652") + public void assertEqualsBoxedUnboxedBoolean() { + Boolean a = true; + boolean b = true; + assertEquals(a, b); + assertEquals(a, b, ""); + assertEquals(b, a); + assertEquals(b, a, ""); + } + + @Test(description = "GITHUB-2652") + public void assertEqualsBoxedUnboxedByte() { + Byte a = Byte.valueOf("3"); + byte b = (byte) 3; + assertEquals(a, b); + assertEquals(a, b, ""); + assertEquals(b, a); + assertEquals(b, a, ""); + } + + @Test(description = "GITHUB-2652") + public void assertEqualsBoxedUnboxedChar() { + Character a = 'a'; + char b = 'a'; + assertEquals(a, b); + assertEquals(a, b, ""); + assertEquals(b, a); + assertEquals(b, a, ""); + } + + @Test(description = "GITHUB-2652") + public void assertEqualsBoxedUnboxedShort() { + Short a = Short.valueOf("3"); + short b = (short) 3; + Short deltaA = Short.valueOf("3"); + short deltaB = (short) 1; + assertEquals(a, b, deltaA); + assertEquals(a, b, deltaB); + assertEquals(a, b); + assertEquals(a, b, ""); + assertEquals(b, a); + assertEquals(b, a, ""); + } + + @Test(description = "GITHUB-2652") + public void assertEqualsBoxedUnboxedInteger() { + Integer a = 3; + int b = 3; + Integer deltaA = 1; + int deltaB = 1; + assertEquals(a, b, deltaA); + assertEquals(a, b, deltaB); + assertEquals(a, b); + assertEquals(a, b, ""); + assertEquals(b, a); + assertEquals(b, a, ""); + } + + @Test + public void noOrderSuccess() { + String[] rto1 = { + "boolean", "BigInteger", "List", + }; + String[] rto2 = { + "List", "BigInteger", "boolean", + }; + Assert.assertEqualsNoOrder(rto1, rto2); + } + + @Test(expectedExceptions = AssertionError.class) + public void noOrderFailure() { + String[] rto1 = { + "a", "a", "b", + }; + String[] rto2 = { + "a", "b", "b", + }; + Assert.assertEqualsNoOrder(rto1, rto2); + } + + @Test + public void intArray_Issue4() { + int[] intArr00 = {1}; + int[] intArr01 = {1}; + assertEquals(intArr00, intArr01); + } + + @Test(expectedExceptions = AssertionError.class) + public void arraysFailures_1() { + int[] intArr = {1, 2}; + long[] longArr = {1, 2}; + assertEquals(intArr, longArr); + } + + @Test(expectedExceptions = AssertionError.class) + public void arraysFailures_2() { + int[] intArr = {1, 2}; + assertEquals(intArr, (long) 1); + } + + @Test(expectedExceptions = AssertionError.class) + public void arraysFailures_3() { + long[] longArr = {1}; + assertEquals((long) 1, longArr); + } + + @Test + public void setsSuccess() { + Set set1 = Sets.newHashSet(); + Set set2 = Sets.newHashSet(); + + set1.add(1); + set2.add(1); + + set1.add(3); + set2.add(3); + + set1.add(2); + set2.add(2); + + assertEquals(set1, set2); + assertEquals(set2, set1); + } + + @Test(expectedExceptions = AssertionError.class) + public void expectThrowsRequiresAnExceptionToBeThrown() { + expectThrows(Throwable.class, nonThrowingRunnable()); + } + + @Test + public void expectThrowsIncludesAnInformativeDefaultMessage() { + try { + expectThrows(Throwable.class, nonThrowingRunnable()); + } catch (AssertionError ex) { + assertEquals("Expected Throwable to be thrown, but nothing was thrown", ex.getMessage()); + return; + } + fail(); + } + + @Test + public void expectThrowsReturnsTheSameObjectThrown() { + NullPointerException npe = new NullPointerException(); + + Throwable throwable = expectThrows(Throwable.class, throwingRunnable(npe)); + + assertSame(npe, throwable); + } + + @Test(expectedExceptions = AssertionError.class) + public void expectThrowsDetectsTypeMismatchesViaExplicitTypeHint() { + NullPointerException npe = new NullPointerException(); + + expectThrows(IOException.class, throwingRunnable(npe)); + } + + @Test + public void expectThrowsWrapsAndPropagatesUnexpectedExceptions() { + NullPointerException npe = new NullPointerException("inner-message"); + + try { + expectThrows(IOException.class, throwingRunnable(npe)); + } catch (AssertionError ex) { + assertSame(npe, ex.getCause()); + assertEquals("inner-message", ex.getCause().getMessage()); + return; + } + fail(); + } + + @Test + public void expectThrowsSuppliesACoherentErrorMessageUponTypeMismatch() { + NullPointerException npe = new NullPointerException(); + + try { + expectThrows(IOException.class, throwingRunnable(npe)); + } catch (AssertionError error) { + assertEquals( + "Expected IOException to be thrown, but NullPointerException was thrown", + error.getMessage()); + assertSame(npe, error.getCause()); + return; + } + fail(); + } + + private static ThrowingRunnable nonThrowingRunnable() { + return () -> {}; + } + + private static ThrowingRunnable throwingRunnable(final Throwable t) { + return () -> { + throw t; + }; + } + + @Test + public void doubleNaNAssertion() { + assertEquals(Double.NaN, Double.NaN, 0.0); + } + + @DataProvider + public Object[][] identicalArraysWithNull() { + return new Object[][] { + {new String[] {"foo", "bar", null}, new String[] {"foo", "bar", null}}, + {new String[] {"foo", null, "bar"}, new String[] {"foo", null, "bar"}}, + {new String[] {null, "foo", "bar"}, new String[] {null, "foo", "bar"}} + }; + } + + @Test(dataProvider = "identicalArraysWithNull") + public void identicalArraysWithNullValues(String[] actual, String[] expected) { + assertEquals(actual, expected); + } + + @DataProvider + public Object[][] nonIdenticalArraysWithNull() { + return new Object[][] { + {new String[] {"foo", "bar", null}, new String[] {"foo", "bar", "not-null"}}, + {new String[] {"foo", "not-null", "bar"}, new String[] {"foo", null, "bar"}}, + {new String[] {null, "foo", "bar"}, new String[] {" not-null", "foo", "bar"}} + }; + } + + @Test(dataProvider = "nonIdenticalArraysWithNull") + public void nonIdenticalarrayWithNullValue(String[] actual, String[] expected) { + Assert.assertNotEquals(actual, expected); + } + + @Test(description = "GITHUB-2540", expectedExceptions = AssertionError.class) + public void checkCollectionEqualsFailsWhenDifferentOrder() { + + Collection collection1 = new LinkedHashSet<>(Arrays.asList("a", "b", "c")); + Collection collection2 = new LinkedHashSet<>(Arrays.asList("a", "c", "b")); + + assertEquals(collection1, collection2); + } + + @Test(description = "GITHUB-2540") + public void checkCollectionEqualsNoOrder() { + + Collection collection1 = new LinkedHashSet<>(Arrays.asList("a", "b", "c")); + Collection collection2 = new LinkedHashSet<>(Arrays.asList("a", "c", "b")); + + assertEqualsNoOrder(collection1, collection2); + } + + @Test(description = "GITHUB-2540") + public void checkCollectionEquals() { + + Collection collection1 = new LinkedHashSet<>(Arrays.asList("a", "b", "c")); + Collection collection2 = new LinkedHashSet<>(Arrays.asList("a", "b", "c")); + + assertEquals(collection1, collection2); + } + + @Test(description = "GITHUB-2540") + public void checkCollectionNotEquals() { + + Collection collection1 = new LinkedHashSet<>(Arrays.asList("a", "b", "c")); + Collection collection2 = new LinkedHashSet<>(Arrays.asList("a", "c", "b")); + + assertNotEquals(collection1, collection2); + } + + @Test(description = "GITHUB-2540", expectedExceptions = AssertionError.class) + public void checkCollectionNotEqualsFailsWhenDifferentOrder() { + + Collection collection1 = new LinkedHashSet<>(Arrays.asList("a", "b", "c")); + Collection collection2 = new LinkedHashSet<>(Arrays.asList("a", "b", "c")); + + assertNotEquals(collection1, collection2); + } + + @Test(description = "GITHUB-2540", expectedExceptions = AssertionError.class) + public void checkIteratorEqualsFailsWhenDifferentOrder() { + + Iterator iterator1 = (new LinkedHashSet<>(Arrays.asList("a", "b", "c"))).iterator(); + Iterator iterator2 = (new LinkedHashSet<>(Arrays.asList("a", "c", "b"))).iterator(); + + assertEquals(iterator1, iterator2); + } + + @Test(description = "GITHUB-2540") + public void checkIteratorEqualsNoOrder() { + + Iterator iterator1 = (new LinkedHashSet<>(Arrays.asList("a", "b", "c"))).iterator(); + Iterator iterator2 = (new LinkedHashSet<>(Arrays.asList("a", "c", "b"))).iterator(); + + assertEqualsNoOrder(iterator1, iterator2); + } + + @Test(description = "GITHUB-2540") + public void checkIteratorEquals() { + + Iterator iterator1 = (new LinkedHashSet<>(Arrays.asList("a", "b", "c"))).iterator(); + Iterator iterator2 = (new LinkedHashSet<>(Arrays.asList("a", "b", "c"))).iterator(); + + assertEquals(iterator1, iterator2); + } + + @Test(description = "GITHUB-2540") + public void checkIteratorNotEquals() { + + Iterator iterator1 = (new LinkedHashSet<>(Arrays.asList("a", "b", "c"))).iterator(); + Iterator iterator2 = (new LinkedHashSet<>(Arrays.asList("a", "c", "b"))).iterator(); + + assertNotEquals(iterator1, iterator2); + } + + @Test(description = "GITHUB-2540", expectedExceptions = AssertionError.class) + public void checkIteratorNotEqualsFailsWhenDifferentOrder() { + + Iterator iterator1 = (new LinkedHashSet<>(Arrays.asList("a", "b", "c"))).iterator(); + Iterator iterator2 = (new LinkedHashSet<>(Arrays.asList("a", "b", "c"))).iterator(); + + assertNotEquals(iterator1, iterator2); + } + + @Test(description = "GITHUB-2643") + public void checkSetEqualsWhenDifferentOrder() { + + Set set1 = new LinkedHashSet<>(Arrays.asList("a", "b", "c")); + Set set2 = new LinkedHashSet<>(Arrays.asList("a", "c", "b")); + + assertEquals(set1, set2); + } + + @Test(description = "GITHUB-2540") + public void checkSetEqualsNoOrder() { + + Set set1 = new LinkedHashSet<>(Arrays.asList("a", "b", "c")); + Set set2 = new LinkedHashSet<>(Arrays.asList("a", "c", "b")); + + assertEqualsNoOrder(set1, set2); + } + + @Test(description = "GITHUB-2540") + public void checkSetEquals() { + + Set set1 = new LinkedHashSet<>(Arrays.asList("a", "b", "c")); + Set set2 = new LinkedHashSet<>(Arrays.asList("a", "b", "c")); + + assertEquals(set1, set2); + } + + @Test(description = "GITHUB-2643", expectedExceptions = AssertionError.class) + public void checkSetNotEqualsFailsWhenDifferentOrder() { + + Set set1 = new LinkedHashSet<>(Arrays.asList("a", "b", "c")); + Set set2 = new LinkedHashSet<>(Arrays.asList("a", "c", "b")); + + assertNotEquals(set1, set2); + } + + @Test(description = "GITHUB-2643", expectedExceptions = AssertionError.class) + public void checkSetNotEqualsFailsWhenSameOrder() { + + Set set1 = new LinkedHashSet<>(Arrays.asList("a", "b", "c")); + Set set2 = new LinkedHashSet<>(Arrays.asList("a", "b", "c")); + + assertNotEquals(set1, set2); + } + + @Test(description = "GITHUB-2643") + public void checkSetNotEqualsWhenNotSameSets() { + + Set set1 = new LinkedHashSet<>(Arrays.asList("a", "b")); + Set set2 = new LinkedHashSet<>(Arrays.asList("a", "b", "c")); + + assertNotEquals(set1, set2); + } + + @Test(description = "GITHUB-2643", expectedExceptions = AssertionError.class) + public void checkSetEqualsFailsWhenNotSameSets() { + + Set set1 = new LinkedHashSet<>(Arrays.asList("a", "b", "c")); + Set set2 = new LinkedHashSet<>(Arrays.asList("a", "b")); + + assertEquals(set1, set2); + } + + @Test(description = "GITHUB-2540", expectedExceptions = AssertionError.class) + public void checkArrayEqualsFailsWhenDifferentOrder() { + + String[] array1 = {"a", "b", "c"}; + String[] array2 = {"a", "c", "b"}; + + assertEquals(array1, array2); + } + + @Test(description = "GITHUB-2540") + public void checkArrayEqualsNoOrder() { + + String[] array1 = {"a", "b", "c"}; + String[] array2 = {"a", "c", "b"}; + + assertEqualsNoOrder(array1, array2); + } + + @Test(description = "GITHUB-2540") + public void checkArrayEquals() { + + String[] array1 = {"a", "b", "c"}; + String[] array2 = {"a", "b", "c"}; + + assertEquals(array1, array2); + } + + @Test(description = "GITHUB-2540") + public void checkArrayNotEquals() { + + String[] array1 = {"a", "b", "c"}; + String[] array2 = {"a", "c", "b"}; + + assertNotEquals(array1, array2); + } + + @Test(description = "GITHUB-2540", expectedExceptions = AssertionError.class) + public void checkArrayNotEqualsFailsWhenDifferentOrder() { + + String[] array1 = {"a", "b", "c"}; + String[] array2 = {"a", "b", "c"}; + + assertNotEquals(array1, array2); + } +} diff --git a/testng-asserts/testng-asserts-build.gradle.kts b/testng-asserts/testng-asserts-build.gradle.kts new file mode 100644 index 0000000..2e95801 --- /dev/null +++ b/testng-asserts/testng-asserts-build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("testng.java-library") +} + +dependencies { + implementation(projects.testngCollections) { + because("Lists.newArrayList") + } + + testImplementation("org.testng:testng:7.3.0") { + because("core depends on assertions and we need testng to test assertions") + } + testImplementation(projects.testngTestKit) +} diff --git a/testng-bom/testng-bom-build.gradle.kts b/testng-bom/testng-bom-build.gradle.kts new file mode 100644 index 0000000..404bd17 --- /dev/null +++ b/testng-bom/testng-bom-build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + // Note: un-comment a dependency in testng.published-java-library when testng-bom becomes published + id("testng.java-platform") +} + +// Add a convenience pom.xml that sets all the versions +dependencies { + constraints { + api(projects.testngAnt) + api(projects.testngApi) + api(projects.testngAsserts) + api(projects.testngCollections) + api(projects.testngCoreApi) + api(projects.testngCore) + api(projects.testngReflectionUtils) + api(projects.testngRunnerApi) + api(projects.testngRunnerJunit4) + } +} diff --git a/testng-collections/src/main/java/org/testng/collections/CollectionUtils.java b/testng-collections/src/main/java/org/testng/collections/CollectionUtils.java new file mode 100644 index 0000000..bf47ee9 --- /dev/null +++ b/testng-collections/src/main/java/org/testng/collections/CollectionUtils.java @@ -0,0 +1,22 @@ +package org.testng.collections; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +public final class CollectionUtils { + + private CollectionUtils() {} + + public static boolean hasElements(Collection c) { + return c != null && !c.isEmpty(); + } + + public static boolean hasElements(Map c) { + return c != null && !c.isEmpty(); + } + + public static Iterable asIterable(Iterator iterator) { + return () -> iterator; + } +} diff --git a/testng-collections/src/main/java/org/testng/collections/ListMultiMap.java b/testng-collections/src/main/java/org/testng/collections/ListMultiMap.java new file mode 100644 index 0000000..b0261f0 --- /dev/null +++ b/testng-collections/src/main/java/org/testng/collections/ListMultiMap.java @@ -0,0 +1,16 @@ +package org.testng.collections; + +import java.util.List; + +/** A container to hold lists indexed by a key. */ +public class ListMultiMap extends MultiMap> { + + public ListMultiMap(boolean isSorted) { + super(isSorted); + } + + @Override + protected List createValue() { + return Lists.newArrayList(); + } +} diff --git a/testng-collections/src/main/java/org/testng/collections/Lists.java b/testng-collections/src/main/java/org/testng/collections/Lists.java new file mode 100644 index 0000000..f2daa94 --- /dev/null +++ b/testng-collections/src/main/java/org/testng/collections/Lists.java @@ -0,0 +1,85 @@ +package org.testng.collections; + +import java.util.*; +import java.util.function.BiPredicate; +import java.util.stream.Collectors; + +public final class Lists { + + private Lists() {} + + public static List newArrayList() { + return new ArrayList<>(); + } + + public static List newLinkedList() { + return new LinkedList<>(); + } + + public static List newLinkedList(Collection c) { + return new LinkedList<>(c); + } + + public static List newArrayList(Collection c) { + return new ArrayList<>(c); + } + + public static List newArrayList(Iterator c) { + List result = new ArrayList<>(); + while (c.hasNext()) { + result.add(c.next()); + } + return result; + } + + @SafeVarargs + public static List newArrayList(K... elements) { + List result = new ArrayList<>(); + Collections.addAll(result, elements); + return result; + } + + public static List newArrayList(int size) { + return new ArrayList<>(size); + } + + public static List intersection(List list1, List list2) { + return list1.stream().filter(list2::contains).collect(Collectors.toList()); + } + + public static List merge(Collection l1, Collection l2) { + List result = newArrayList(l1); + result.addAll(l2); + return result; + } + + /** + * Utility method that merges two lists by applying the provided condition. + * + * @param - The generic type + * @param l1 - The first list + * @param condition - The condition that is used to determine if an element is to be added or not. + * @param lists - The lists which are to be merged into the first list + * @return - The merged list. + */ + @SafeVarargs + public static List merge(List l1, BiPredicate condition, List... lists) { + List result = newArrayList(l1); + Arrays.stream(lists) + .flatMap(Collection::stream) + .forEach( + eachItem -> { + boolean exists = result.stream().anyMatch(e -> condition.test(e, eachItem)); + if (!exists) { + result.add(eachItem); + } + }); + return result; + } + + public static List newReversedArrayList(Collection c) { + List list = newArrayList(c); + Collections.reverse(list); + return list; + } +} diff --git a/testng-collections/src/main/java/org/testng/collections/Maps.java b/testng-collections/src/main/java/org/testng/collections/Maps.java new file mode 100644 index 0000000..294f2d2 --- /dev/null +++ b/testng-collections/src/main/java/org/testng/collections/Maps.java @@ -0,0 +1,47 @@ +package org.testng.collections; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class Maps { + + public static Map newHashMap() { + return new HashMap<>(); + } + + public static Map newHashtable() { + return new Hashtable<>(); + } + + public static Map newConcurrentMap() { + return new ConcurrentHashMap<>(); + } + + public static ListMultiMap newListMultiMap() { + return new ListMultiMap<>(false); + } + + public static ListMultiMap newSortedListMultiMap() { + return new ListMultiMap<>(true); + } + + public static SetMultiMap newSetMultiMap() { + return new SetMultiMap<>(false); + } + + public static Map newLinkedHashMap() { + return new LinkedHashMap<>(); + } + + public static Map synchronizedLinkedHashMap() { + return Collections.synchronizedMap(newLinkedHashMap()); + } + + public static Map newHashMap(Map parameters) { + return new HashMap<>(parameters); + } +} diff --git a/testng-collections/src/main/java/org/testng/collections/MultiMap.java b/testng-collections/src/main/java/org/testng/collections/MultiMap.java new file mode 100644 index 0000000..8d2df15 --- /dev/null +++ b/testng-collections/src/main/java/org/testng/collections/MultiMap.java @@ -0,0 +1,91 @@ +package org.testng.collections; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +public abstract class MultiMap> { + protected final Map m_objects; + + protected MultiMap(boolean isSorted) { + if (isSorted) { + m_objects = Maps.newLinkedHashMap(); + } else { + m_objects = Maps.newHashMap(); + } + } + + protected abstract C createValue(); + + public boolean put(K key, V method) { + AtomicBoolean exists = new AtomicBoolean(true); + return m_objects + .computeIfAbsent( + key, + k -> { + exists.set(false); + return createValue(); + }) + .add(method) + && exists.get(); + } + + public C get(K key) { + return m_objects.computeIfAbsent(key, k -> createValue()); + } + + public Set keySet() { + return new HashSet<>(m_objects.keySet()); + } + + public boolean containsKey(K k) { + return m_objects.containsKey(k); + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + Set indices = keySet(); + for (K i : indices) { + result.append("\n ").append(i).append(" <-- "); + for (Object o : m_objects.get(i)) { + result.append(o).append(" "); + } + } + return result.toString(); + } + + public boolean isEmpty() { + return m_objects.size() == 0; + } + + public int size() { + return m_objects.size(); + } + + public boolean remove(K key, V value) { + return get(key).remove(value); + } + + public C removeAll(K key) { + return m_objects.remove(key); + } + + public Set> entrySet() { + return m_objects.entrySet(); + } + + public Collection values() { + return m_objects.values(); + } + + public boolean putAll(K k, Collection values) { + boolean result = false; + for (V v : values) { + result = put(k, v) || result; + } + return result; + } +} diff --git a/testng-collections/src/main/java/org/testng/collections/Objects.java b/testng-collections/src/main/java/org/testng/collections/Objects.java new file mode 100644 index 0000000..2e0afbe --- /dev/null +++ b/testng-collections/src/main/java/org/testng/collections/Objects.java @@ -0,0 +1,89 @@ +package org.testng.collections; + +import java.util.List; +import org.testng.util.Strings; + +public final class Objects { + + private Objects() {} + + private static class ValueHolder { + private final String m_name; + private final String m_value; + + public ValueHolder(String name, String value) { + m_name = name; + m_value = value; + } + + boolean isNull() { + return m_value == null; + } + + @Override + public String toString() { + return m_name + "=" + m_value; + } + + public boolean isEmptyString() { + return Strings.isNullOrEmpty(m_value); + } + } + + public static class ToStringHelper { + private final String m_className; + private final List values = Lists.newArrayList(); + private boolean m_omitNulls = false; + private boolean m_omitEmptyStrings = false; + + public ToStringHelper(String className) { + m_className = className; + } + + public ToStringHelper omitNulls() { + m_omitNulls = true; + return this; + } + + public ToStringHelper omitEmptyStrings() { + m_omitEmptyStrings = true; + return this; + } + + public ToStringHelper add(String name, String value) { + values.add(new ValueHolder(name, s(value))); + return this; + } + + public ToStringHelper add(String name, Object value) { + values.add(new ValueHolder(name, s(value))); + return this; + } + + private String s(Object o) { + return o != null ? (o.toString().isEmpty() ? "\"\"" : o.toString()) : "{null}"; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder("[" + m_className + " "); + for (int i = 0; i < values.size(); i++) { + ValueHolder vh = values.get(i); + if (m_omitNulls && vh.isNull()) continue; + if (m_omitEmptyStrings && vh.isEmptyString()) continue; + + if (i > 0) { + result.append(" "); + } + result.append(vh.toString()); + } + result.append("]"); + + return result.toString(); + } + } + + public static ToStringHelper toStringHelper(Class class1) { + return new ToStringHelper(class1.getSimpleName()); + } +} diff --git a/testng-collections/src/main/java/org/testng/collections/SetMultiMap.java b/testng-collections/src/main/java/org/testng/collections/SetMultiMap.java new file mode 100644 index 0000000..7cc8364 --- /dev/null +++ b/testng-collections/src/main/java/org/testng/collections/SetMultiMap.java @@ -0,0 +1,16 @@ +package org.testng.collections; + +import java.util.Set; + +/** A container to hold sets indexed by a key. */ +public class SetMultiMap extends MultiMap> { + + public SetMultiMap(boolean isSorted) { + super(isSorted); + } + + @Override + protected Set createValue() { + return Sets.newHashSet(); + } +} diff --git a/testng-collections/src/main/java/org/testng/collections/Sets.java b/testng-collections/src/main/java/org/testng/collections/Sets.java new file mode 100644 index 0000000..af61652 --- /dev/null +++ b/testng-collections/src/main/java/org/testng/collections/Sets.java @@ -0,0 +1,33 @@ +package org.testng.collections; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; + +public final class Sets { + + private Sets() {} + + public static Set newHashSet() { + return new HashSet<>(); + } + + public static Set newHashSet(Collection c) { + return new HashSet<>(c); + } + + @SafeVarargs + public static Set newHashSet(V... a) { + return newHashSet(Arrays.asList(a)); + } + + public static Set newLinkedHashSet() { + return new LinkedHashSet<>(); + } + + public static Set newLinkedHashSet(Collection c) { + return new LinkedHashSet<>(c); + } +} diff --git a/testng-collections/src/main/java/org/testng/util/Strings.java b/testng-collections/src/main/java/org/testng/util/Strings.java new file mode 100644 index 0000000..57a72ba --- /dev/null +++ b/testng-collections/src/main/java/org/testng/util/Strings.java @@ -0,0 +1,76 @@ +package org.testng.util; + +import java.util.Map; +import org.testng.collections.Maps; + +public final class Strings { + private Strings() { + // Utility class. Defeat instantiation. + } + + // TODO: When TestNG moves to JDK11 as the default JDK this method needs to be deprecated and + // removed + // because now this method is present in JDK11 as part of the JDK itself. + // See + // https://hg.openjdk.java.net/jdk/jdk/file/fc16b5f193c7/src/java.base/share/classes/java/lang/String.java#l2984 + public static String repeat(String text, int count) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < count; i++) { + builder.append(text); + } + return builder.toString(); + } + + public static boolean isNullOrEmpty(String string) { + return string == null || string.trim().isEmpty(); + } + + public static boolean isNotNullAndNotEmpty(String string) { + return !(isNullOrEmpty(string)); + } + + /** + * @param string - The input String. + * @return - Returns an empty string if the input String is null (or) empty, else it + * returns back the input string. + */ + public static String getValueOrEmpty(String string) { + return isNotNullAndNotEmpty(string) ? string : ""; + } + + private static final Map ESCAPE_HTML_MAP = Maps.newLinkedHashMap(); + + static { + ESCAPE_HTML_MAP.put("&", "&"); + ESCAPE_HTML_MAP.put("<", "<"); + ESCAPE_HTML_MAP.put(">", ">"); + } + + public static String escapeHtml(String text) { + String result = text; + for (Map.Entry entry : ESCAPE_HTML_MAP.entrySet()) { + result = result.replace(entry.getKey(), entry.getValue()); + } + return result; + } + + public static String valueOf(Map m) { + StringBuilder result = new StringBuilder(); + for (Object o : m.values()) { + result.append(o).append(" "); + } + + return result.toString(); + } + + public static String join(String delimiter, String[] parts) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < parts.length - 1; i++) { + sb.append(parts[i]).append(delimiter); + } + if (parts.length > 0) { + sb.append(parts[parts.length - 1]); + } + return sb.toString(); + } +} diff --git a/testng-collections/testng-collections-build.gradle.kts b/testng-collections/testng-collections-build.gradle.kts new file mode 100644 index 0000000..0438ec3 --- /dev/null +++ b/testng-collections/testng-collections-build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + id("testng.java-library") +} diff --git a/testng-core-api/src/main/java/org/testng/IAlterSuiteListener.java b/testng-core-api/src/main/java/org/testng/IAlterSuiteListener.java new file mode 100644 index 0000000..b28c7fa --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IAlterSuiteListener.java @@ -0,0 +1,24 @@ +package org.testng; + +import java.util.List; +import org.testng.xml.XmlSuite; + +/** + * Implementations of this interface will gain access to the {@link XmlSuite} object and thus let + * users be able to alter a suite or a test based on their own needs. This listener can be added + * ONLY via the following two ways : + * + *
    + *
  1. <listeners> tag in a suite file. + *
  2. via Service loaders + *
+ * + *

Note: This listener will NOT be invoked if it is wired in via the @ + * Listeners annotation. + */ +public interface IAlterSuiteListener extends ITestNGListener { + /** @param suites - The list of {@link XmlSuite}s that are part of the current execution. */ + default void alter(List suites) { + // not implemented + } +} diff --git a/testng-core-api/src/main/java/org/testng/IAnnotationTransformer.java b/testng-core-api/src/main/java/org/testng/IAnnotationTransformer.java new file mode 100644 index 0000000..4546968 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IAnnotationTransformer.java @@ -0,0 +1,79 @@ +package org.testng; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import org.testng.annotations.IConfigurationAnnotation; +import org.testng.annotations.IDataProviderAnnotation; +import org.testng.annotations.IFactoryAnnotation; +import org.testng.annotations.IListenersAnnotation; +import org.testng.annotations.ITestAnnotation; + +public interface IAnnotationTransformer extends ITestNGListener { + + /** + * This method will be invoked by TestNG to give you a chance to modify a TestNG annotation read + * from your test classes. You can change the values you need by calling any of the setters on the + * ITest interface. + * + *

Note that only one of the three parameters testClass, testConstructor and testMethod will be + * non-null. + * + * @param annotation The annotation that was read from your test class. + * @param testClass If the annotation was found on a class, this parameter represents this class + * (null otherwise). + * @param testConstructor If the annotation was found on a constructor, this parameter represents + * this constructor (null otherwise). + * @param testMethod If the annotation was found on a method, this parameter represents this + * method (null otherwise). + */ + default void transform( + ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) { + // not implemented + } + + /** + * Transform an IConfiguration annotation. + * + *

Note that only one of the three parameters testClass, testConstructor and testMethod will be + * non-null. + * + * @param annotation The annotation that was read from your test class. + * @param testClass If the annotation was found on a class, this parameter represents this class + * (null otherwise). + * @param testConstructor If the annotation was found on a constructor, this parameter represents + * this constructor (null otherwise). + * @param testMethod If the annotation was found on a method, this parameter represents this + * method (null otherwise). + */ + default void transform( + IConfigurationAnnotation annotation, + Class testClass, + Constructor testConstructor, + Method testMethod) { + // not implemented + } + + /** + * Transform an IDataProvider annotation. + * + * @param annotation The @DataProvider annotation + * @param method The method annotated with the IDataProvider annotation. + */ + default void transform(IDataProviderAnnotation annotation, Method method) { + // not implemented + } + + /** + * Transform an IFactory annotation. + * + * @param annotation The annotation factory + * @param method The method annotated with the IFactory annotation. + */ + default void transform(IFactoryAnnotation annotation, Method method) { + // not implemented + } + + default void transform(IListenersAnnotation annotation, Class testClass) { + // not implemented + } +} diff --git a/testng-core-api/src/main/java/org/testng/IAttributes.java b/testng-core-api/src/main/java/org/testng/IAttributes.java new file mode 100644 index 0000000..98e925c --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IAttributes.java @@ -0,0 +1,31 @@ +package org.testng; + +import java.util.Set; + +/** A trait that is used by all interfaces that lets the user add or remove their own attributes. */ +public interface IAttributes { + /** + * @param name The name of the attribute to return + * @return The attribute + */ + Object getAttribute(String name); + + /** + * Set a custom attribute. + * + * @param name The attribute name + * @param value The attribute value + */ + void setAttribute(String name, Object value); + + /** @return all the attributes names. */ + Set getAttributeNames(); + + /** + * Remove the attribute + * + * @param name The attribute name + * @return the attribute value if found, null otherwise + */ + Object removeAttribute(String name); +} diff --git a/testng-core-api/src/main/java/org/testng/IClass.java b/testng-core-api/src/main/java/org/testng/IClass.java new file mode 100644 index 0000000..adf1df9 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IClass.java @@ -0,0 +1,40 @@ +package org.testng; + +import org.testng.xml.XmlClass; +import org.testng.xml.XmlTest; + +/** IClass represents a test class and a collection of its instances. */ +public interface IClass { + + /** @return this test class name. This is the name of the corresponding Java class. */ + String getName(); + + /** @return the <test> tag this class was found in. */ + XmlTest getXmlTest(); + + /** @return the *lt;class> tag this class was found in. */ + XmlClass getXmlClass(); + + /** @return its test name if this class implements org.testng.ITest, null otherwise. */ + String getTestName(); + + /** @return the Java class corresponding to this IClass. */ + Class getRealClass(); + + /** + * Returns all the instances the methods will be invoked upon. This will typically be an array of + * one object in the absence of a @Factory annotation. + * + * @param create flag if a new set of instances must be returned (if set to false) + * @return All the instances the methods will be invoked upon. + */ + Object[] getInstances(boolean create); + + default Object[] getInstances(boolean create, String errorMsgPrefix) { + return getInstances(create); + } + + long[] getInstanceHashCodes(); + + void addInstance(Object instance); +} diff --git a/testng-core-api/src/main/java/org/testng/IClassListener.java b/testng-core-api/src/main/java/org/testng/IClassListener.java new file mode 100644 index 0000000..d5138f9 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IClassListener.java @@ -0,0 +1,12 @@ +package org.testng; + +public interface IClassListener extends ITestNGListener { + + default void onBeforeClass(ITestClass testClass) { + // not implemented + } + + default void onAfterClass(ITestClass testClass) { + // not implemented + } +} diff --git a/testng-core-api/src/main/java/org/testng/IConfigurable.java b/testng-core-api/src/main/java/org/testng/IConfigurable.java new file mode 100644 index 0000000..4666a79 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IConfigurable.java @@ -0,0 +1,13 @@ +package org.testng; + +/** + * If a test class implements this interface, its run() method will be invoked instead of each + * configuration method found. The invocation of the configuration method will then be performed + * upon invocation of the callBack() method of the IConfigureCallBack parameter. + * + * @author cbeust Sep 07, 2010 + */ +public interface IConfigurable extends ITestNGListener { + + void run(IConfigureCallBack callBack, ITestResult testResult); +} diff --git a/testng-core-api/src/main/java/org/testng/IConfigurationListener.java b/testng-core-api/src/main/java/org/testng/IConfigurationListener.java new file mode 100644 index 0000000..9d755de --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IConfigurationListener.java @@ -0,0 +1,81 @@ +package org.testng; + +/** Listener interface for events related to configuration methods. */ +public interface IConfigurationListener extends ITestNGListener { + + /** + * Invoked whenever a configuration method succeeded. + * + * @param tr The test result + */ + default void onConfigurationSuccess(ITestResult tr) { + // not implemented + } + + /** + * Invoked whenever a configuration method succeeded. + * + * @param tr The test result + * @param tm The test method + */ + default void onConfigurationSuccess(ITestResult tr, ITestNGMethod tm) { + // not implemented + } + + /** + * Invoked whenever a configuration method failed. + * + * @param tr The test result + */ + default void onConfigurationFailure(ITestResult tr) { + // not implemented + } + + /** + * Invoked whenever a configuration method failed. + * + * @param tr The test result + * @param tm The test method + */ + default void onConfigurationFailure(ITestResult tr, ITestNGMethod tm) { + // not implemented + } + + /** + * Invoked whenever a configuration method was skipped. + * + * @param tr The test result + */ + default void onConfigurationSkip(ITestResult tr) { + // not implemented + } + + /** + * Invoked whenever a configuration method was skipped. + * + * @param tr The test result + * @param tm The test method + */ + default void onConfigurationSkip(ITestResult tr, ITestNGMethod tm) { + // not implemented + } + + /** + * Invoked before a configuration method is invoked. + * + * @param tr The test result + */ + default void beforeConfiguration(ITestResult tr) { + // not implemented + } + + /** + * Invoked before a configuration method is invoked. + * + * @param tr The test result + * @param tm The test method + */ + default void beforeConfiguration(ITestResult tr, ITestNGMethod tm) { + // not implemented + } +} diff --git a/testng-core-api/src/main/java/org/testng/IConfigurationListener2.java b/testng-core-api/src/main/java/org/testng/IConfigurationListener2.java new file mode 100644 index 0000000..04c889d --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IConfigurationListener2.java @@ -0,0 +1,7 @@ +package org.testng; + +// WARNING: Donot delete this interface. This is internally being referred to by Gradle here +// https://github.com/gradle/gradle/blob/f0b9d60906c7b8c42cd6c61a39ae7b74767bb012/subprojects/testing-jvm/src/main/java/org/gradle/api/internal/tasks/testing/testng/TestNGListenerAdapterFactory.java#L37-L49 +/** @deprecated As of release 7.0.0, replaced by {@link org.testng.IConfigurationListener} */ +@Deprecated +public interface IConfigurationListener2 extends IConfigurationListener {} diff --git a/testng-core-api/src/main/java/org/testng/IConfigureCallBack.java b/testng-core-api/src/main/java/org/testng/IConfigureCallBack.java new file mode 100644 index 0000000..360855a --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IConfigureCallBack.java @@ -0,0 +1,24 @@ +package org.testng; + +/** + * A parameter of this type will be passed to the run() method of a IConfigurable. Invoking + * runConfigurationMethod() on that parameter will cause the test method currently being diverted to + * be invoked. + * + *

This interface is not meant to be implemented by clients, only by TestNG. + * + * @see org.testng.IConfigurable + * @author cbeust Sep 07, 2010 + */ +public interface IConfigureCallBack { + + /** + * Invoke the test method currently being hijacked. + * + * @param testResult The test result + */ + void runConfigurationMethod(ITestResult testResult); + + /** @return the parameters that will be used to invoke the configuration method. */ + Object[] getParameters(); +} diff --git a/testng-core-api/src/main/java/org/testng/IDataProviderInterceptor.java b/testng-core-api/src/main/java/org/testng/IDataProviderInterceptor.java new file mode 100644 index 0000000..8eb816b --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IDataProviderInterceptor.java @@ -0,0 +1,29 @@ +package org.testng; + +import java.util.Iterator; + +/** + * This interface helps define an interceptor for data providers. Implementations of this TestNG + * listener can be wired in via the @Listeners annotation or via the listeners + * tag in the suite file or via a Service Provider Interface mechanism. + * + *

The implementation would be able to alter the actual set of data using which a test method + * would be iterated upon. + */ +public interface IDataProviderInterceptor extends ITestNGListener { + + /** + * @param original - The original data set as produced by a particular data provider. + * @param dataProviderMethod - The {@link IDataProviderMethod} method object which represents the + * data provider that was invoked. + * @param method - The {@link ITestNGMethod} method object which represents the test method that + * will receive the parameters. + * @param iTestContext - The {@link ITestContext} object that represents the current test context. + * @return - The altered data set that would be used by TestNG to run the test method. + */ + Iterator intercept( + Iterator original, + IDataProviderMethod dataProviderMethod, + ITestNGMethod method, + ITestContext iTestContext); +} diff --git a/testng-core-api/src/main/java/org/testng/IDataProviderListener.java b/testng-core-api/src/main/java/org/testng/IDataProviderListener.java new file mode 100644 index 0000000..b4a59bb --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IDataProviderListener.java @@ -0,0 +1,45 @@ +package org.testng; + +/** A listener that gets invoked before and after a data provider is invoked by TestNG. */ +public interface IDataProviderListener extends ITestNGListener { + + /** + * This method gets invoked just before a data provider is invoked. + * + * @param dataProviderMethod - A {@link IDataProviderMethod} object that contains details about + * the data provider that is about to be executed. + * @param method - The {@link ITestNGMethod} method that is going to consume the data + * @param iTestContext - The current test context + */ + default void beforeDataProviderExecution( + IDataProviderMethod dataProviderMethod, ITestNGMethod method, ITestContext iTestContext) { + // not implemented + } + + /** + * This method gets invoked just after a data provider is invoked. + * + * @param dataProviderMethod - A {@link IDataProviderMethod} object that contains details about + * the data provider that got executed. + * @param method - The {@link ITestNGMethod} method that received the data + * @param iTestContext - The current test context + */ + default void afterDataProviderExecution( + IDataProviderMethod dataProviderMethod, ITestNGMethod method, ITestContext iTestContext) { + // not implemented + } + + /** + * This method gets invoked when the data provider encounters an exception + * + * @param method - The {@link ITestNGMethod} method that received the data. A reference to the + * corresponding data provider can be obtained via {@link + * ITestNGMethod#getDataProviderMethod()} + * @param ctx - The current test context + * @param t - The {@link RuntimeException} that embeds the actual exception. Use {@link + * RuntimeException#getCause()} to get to the actual exception. + */ + default void onDataProviderFailure(ITestNGMethod method, ITestContext ctx, RuntimeException t) { + // not implemented + } +} diff --git a/testng-core-api/src/main/java/org/testng/IDataProviderMethod.java b/testng-core-api/src/main/java/org/testng/IDataProviderMethod.java new file mode 100644 index 0000000..7a495ba --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IDataProviderMethod.java @@ -0,0 +1,28 @@ +package org.testng; + +import java.lang.reflect.Method; +import java.util.List; + +/** Represents the attributes of a {@link org.testng.annotations.DataProvider} annotated method. */ +public interface IDataProviderMethod { + /** + * @return - The instance to which the data provider belongs to. null if the data + * provider is a static one. + */ + Object getInstance(); + + /** + * @return - A {@link Method} object that represents the actual {@literal @}{@link + * org.testng.annotations.DataProvider} method. + */ + Method getMethod(); + + /** @return The name of this DataProvider. */ + String getName(); + + /** @return Whether this data provider should be run in parallel. */ + boolean isParallel(); + + /** @return Which indices to run from this data provider, default: all. */ + List getIndices(); +} diff --git a/testng-core-api/src/main/java/org/testng/IDynamicGraph.java b/testng-core-api/src/main/java/org/testng/IDynamicGraph.java new file mode 100644 index 0000000..b0734a2 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IDynamicGraph.java @@ -0,0 +1,43 @@ +package org.testng; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * Represents the graphical representative capabilities of an entity. The entities could be either a + * {@link ISuite} or an {@link ITestNGMethod} object which are usually the logical units of work + * that TestNG deals with. + */ +public interface IDynamicGraph { + + boolean addNode(T node); + + void addEdge(int weight, T from, T to); + + void setVisualisers(Set listener); + + void addEdges(int weight, T from, Iterable tos); + + List getFreeNodes(); + + List getDependenciesFor(T node); + + void setStatus(Collection nodes, Status status); + + void setStatus(T node, Status status); + + int getNodeCount(); + + int getNodeCountWithStatus(Status status); + + Set getNodesWithStatus(Status status); + + String toDot(); + + enum Status { + READY, + RUNNING, + FINISHED + } +} diff --git a/testng-core-api/src/main/java/org/testng/IExecutionListener.java b/testng-core-api/src/main/java/org/testng/IExecutionListener.java new file mode 100644 index 0000000..607aff6 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IExecutionListener.java @@ -0,0 +1,25 @@ +package org.testng; + +/** + * A listener used to monitor when a TestNG run starts and ends. When implementation of this + * listener is wired into TestNG, TestNG will ensure that + * + *

    + *
  • {@link IExecutionListener#onExecutionStart()} gets invoked before TestNG proceeds with + * invoking any other listener. + *
  • {@link IExecutionListener#onExecutionFinish()} gets invoked at the very last (after report + * generation phase), before TestNG exits the JVM. + *
+ */ +public interface IExecutionListener extends ITestNGListener { + + /** Invoked before the TestNG run starts. */ + default void onExecutionStart() { + // not implemented + } + + /** Invoked once all the suites have been run. */ + default void onExecutionFinish() { + // not implemented + } +} diff --git a/testng-core-api/src/main/java/org/testng/IExecutionVisualiser.java b/testng-core-api/src/main/java/org/testng/IExecutionVisualiser.java new file mode 100644 index 0000000..a361901 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IExecutionVisualiser.java @@ -0,0 +1,14 @@ +package org.testng; + +/** + * A TestNG listener that can be used to build graph representations of TestNG methods as and when + * they are being executed on a real-time basis. + */ +public interface IExecutionVisualiser extends ITestNGListener { + /** + * @param dotDefinition - A DOT + * representation of the Directed Acyclic Graph that TestNG builds internally to represent its + * tests. + */ + void consumeDotDefinition(String dotDefinition); +} diff --git a/testng-core-api/src/main/java/org/testng/IExpectedExceptionsHolder.java b/testng-core-api/src/main/java/org/testng/IExpectedExceptionsHolder.java new file mode 100644 index 0000000..18a174b --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IExpectedExceptionsHolder.java @@ -0,0 +1,20 @@ +package org.testng; + +public interface IExpectedExceptionsHolder { + + /** + * Get the message in case the Throwable thrown by the test is not matching. + * + * @param ite The Throwable thrown by the test + * @return The message which will be displayed as test result + */ + String getWrongExceptionMessage(Throwable ite); + + /** + * Check if the Throwable thrown by the test is matching with the holder logic + * + * @param ite The Throwable thrown by the test + * @return true if the Throwable is matching with the holder logic, false otherwise + */ + boolean isThrowableMatching(Throwable ite); +} diff --git a/testng-core-api/src/main/java/org/testng/IHookCallBack.java b/testng-core-api/src/main/java/org/testng/IHookCallBack.java new file mode 100644 index 0000000..0ab5fc3 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IHookCallBack.java @@ -0,0 +1,24 @@ +package org.testng; + +/** + * A parameter of this type will be passed to the run() method of a IHookable. Invoking + * runTestMethod() on that parameter will cause the test method currently being diverted to be + * invoked. + * + *

This interface is not meant to be implemented by clients, only by TestNG. + * + * @see org.testng.IHookable + * @author cbeust Jan 28, 2006 + */ +public interface IHookCallBack { + + /** + * Invoke the test method currently being hijacked. + * + * @param testResult The test result + */ + void runTestMethod(ITestResult testResult); + + /** @return the parameters that will be used to invoke the test method. */ + Object[] getParameters(); +} diff --git a/testng-core-api/src/main/java/org/testng/IHookable.java b/testng-core-api/src/main/java/org/testng/IHookable.java new file mode 100644 index 0000000..f80912d --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IHookable.java @@ -0,0 +1,29 @@ +package org.testng; + +/** + * If a test class implements this interface, its run() method will be invoked instead of each @Test + * method found. The invocation of the test method will then be performed upon invocation of the + * callBack() method of the IHookCallBack parameter. + * + *

This is useful to test classes that require JAAS authentication, which can be implemented as + * follows: + * + *

+ * public void run(final IHookCallBack icb, ITestResult testResult) {
+ *   // Preferably initialized in a @Configuration method
+ *   mySubject = authenticateWithJAAs();
+ *
+ *   Subject.doAs(mySubject, new PrivilegedExceptionAction() {
+ *     public Object run() {
+ *       icb.callback(testResult);
+ *     }
+ *   };
+ * }
+ * 
+ * + * @author cbeust Jan 28, 2006 + */ +public interface IHookable extends ITestNGListener { + + void run(IHookCallBack callBack, ITestResult testResult); +} diff --git a/testng-core-api/src/main/java/org/testng/IInjectorFactory.java b/testng-core-api/src/main/java/org/testng/IInjectorFactory.java new file mode 100644 index 0000000..a9b9a34 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IInjectorFactory.java @@ -0,0 +1,36 @@ +package org.testng; + +import com.google.inject.Injector; +import com.google.inject.Module; +import com.google.inject.Stage; +import javax.annotation.Nullable; + +/** Allows customization of the {@link Injector} creation when working with dependency injection. */ +public interface IInjectorFactory { + /** + * Note that {@link #getInjector(Injector, Stage, Module...)} should be used instead. + * + * @param stage - A {@link Stage} object that defines the appropriate stage + * @param modules - An array of {@link Module} + * @return - An {@link com.google.inject.Injector} instance that can be used to perform dependency + * injection. + * @deprecated - As of TestNG 7.5.0 + */ + @Deprecated + Injector getInjector(Stage stage, Module... modules); + + /** + * Adding this method breaks existing implementations therefore for the time being (until + * deprecated method is removed) it calls the existing method. + * + * @param parent - Parent {@link com.google.inject.Injector} instance that was built with parent + * injector + * @param stage - A {@link Stage} object that defines the appropriate stage + * @param modules - An array of {@link Module} + * @return - An {@link com.google.inject.Injector} instance that can be used to perform dependency + * injection. + */ + default Injector getInjector(@Nullable Injector parent, Stage stage, Module... modules) { + return getInjector(stage, modules); + } +} diff --git a/testng-core-api/src/main/java/org/testng/IInstanceInfo.java b/testng-core-api/src/main/java/org/testng/IInstanceInfo.java new file mode 100644 index 0000000..e965580 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IInstanceInfo.java @@ -0,0 +1,18 @@ +package org.testng; + +/** + * This class defines a pair of instance/class. A method with @Factory can return an array of these + * objects instead of Object[] so that instances can be dynamic proxies or mock objects and still + * provide enough information to TestNG to figure out what classes the annotations should be looked + * up in. + * + * @author Cedric Beust + */ +public interface IInstanceInfo { + + /** @return The instance on which the tests will be invoked. */ + T getInstance(); + + /** @return The class on which the TestNG annotations should be looked for. */ + Class getInstanceClass(); +} diff --git a/testng-core-api/src/main/java/org/testng/IInvokedMethod.java b/testng-core-api/src/main/java/org/testng/IInvokedMethod.java new file mode 100644 index 0000000..72ef84c --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IInvokedMethod.java @@ -0,0 +1,23 @@ +package org.testng; + +/** + * An interface representing a method that has been invoked by TestNG. + * + *

This interface is internal. + */ +public interface IInvokedMethod { + + /** @return true if this method is a test method */ + boolean isTestMethod(); + + /** @return true if this method is a configuration method (@BeforeXXX or @AfterXXX) */ + boolean isConfigurationMethod(); + + /** @return the test method */ + ITestNGMethod getTestMethod(); + + ITestResult getTestResult(); + + /** @return the date when this method was run */ + long getDate(); +} diff --git a/testng-core-api/src/main/java/org/testng/IInvokedMethodListener.java b/testng-core-api/src/main/java/org/testng/IInvokedMethodListener.java new file mode 100644 index 0000000..8ae3197 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IInvokedMethodListener.java @@ -0,0 +1,48 @@ +package org.testng; + +/** + * A listener that gets invoked before and after a method is invoked by TestNG. This listener will + * be invoked for configuration and test methods irrespective of whether they passe/fail or get + * skipped. This listener invocation can be disabled for SKIPPED tests through one of the below + * mechanisms: + * + *

    + *
  • Command line parameter alwaysRunListeners + *
  • Build tool + *
  • Via {@code TestNG.alwaysRunListeners(false)} + *
+ */ +public interface IInvokedMethodListener extends ITestNGListener { + + default void beforeInvocation(IInvokedMethod method, ITestResult testResult) { + // not implemented + } + + default void afterInvocation(IInvokedMethod method, ITestResult testResult) { + // not implemented + } + + /** + * To be implemented if the method needs a handle to contextual information. + * + * @param method The invoked method + * @param testResult The test result + * @param context The test context + */ + default void beforeInvocation( + IInvokedMethod method, ITestResult testResult, ITestContext context) { + // not implemented + } + + /** + * To be implemented if the method needs a handle to contextual information. + * + * @param method The invoked method + * @param testResult The test result + * @param context The test context + */ + default void afterInvocation( + IInvokedMethod method, ITestResult testResult, ITestContext context) { + // not implemented + } +} diff --git a/testng-core-api/src/main/java/org/testng/IMethodInstance.java b/testng-core-api/src/main/java/org/testng/IMethodInstance.java new file mode 100644 index 0000000..5a181db --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IMethodInstance.java @@ -0,0 +1,9 @@ +package org.testng; + +/** This interface captures a test method along with all the instances it should be run on. */ +public interface IMethodInstance { + + ITestNGMethod getMethod(); + + Object getInstance(); +} diff --git a/testng-core-api/src/main/java/org/testng/IMethodInterceptor.java b/testng-core-api/src/main/java/org/testng/IMethodInterceptor.java new file mode 100644 index 0000000..b3d2168 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IMethodInterceptor.java @@ -0,0 +1,27 @@ +package org.testng; + +import java.util.List; + +/** + * This class is used to alter the list of test methods that TestNG is about to run. + * + *

An instance of this class will be invoked right before TestNG starts invoking test methods. + * Only methods that have no dependents and that don't depend on any other test methods will be + * passed in parameter. Implementers of this interface need to return a list of {@link + * IMethodInstance} that represents the list of test methods they want run. TestNG will run these + * methods in the same order found in the returned value. + * + *

Typically, the returned list will be just the methods passed in parameter but sorted + * differently, but it can actually have any size (it can be empty, it can be of the same size as + * the original list or it can contain more methods). + * + *

The {@link ITestContext} is passed in the intercept method so that implementers + * can set user values (using {@link ITestContext#setAttribute(String, Object)}), which they can + * then look up later while generating the reports. + * + * @author cbeust + */ +public interface IMethodInterceptor extends ITestNGListener { + + List intercept(List methods, ITestContext context); +} diff --git a/testng-core-api/src/main/java/org/testng/IMethodSelector.java b/testng-core-api/src/main/java/org/testng/IMethodSelector.java new file mode 100644 index 0000000..16af063 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IMethodSelector.java @@ -0,0 +1,29 @@ +package org.testng; + +import java.util.List; + +/** + * This interface is used to augment or replace TestNG's algorithm to decide whether a test method + * should be included in a test run. + */ +public interface IMethodSelector { + + /** + * @param context The selector context. The implementation of this method can invoke + * setHalted(true) to indicate that no other Method Selector should be invoked by TestNG after + * this one. Additionally, this implementation can manipulate the Map object returned by + * getUserData(). + * @param method The test method + * @param isTestMethod true if this is a @Test method, false if it's a configuration method + * @return true if this method should be included in the test run, false otherwise + */ + boolean includeMethod(IMethodSelectorContext context, ITestNGMethod method, boolean isTestMethod); + + /** + * Invoked when all the test methods are known so that the method selector can perform additional + * work, such as adding the transitive closure of all the groups being included and depended upon. + * + * @param testMethods The test methods + */ + void setTestMethods(List testMethods); +} diff --git a/testng-core-api/src/main/java/org/testng/IMethodSelectorContext.java b/testng-core-api/src/main/java/org/testng/IMethodSelectorContext.java new file mode 100644 index 0000000..81095f9 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IMethodSelectorContext.java @@ -0,0 +1,31 @@ +package org.testng; + +import java.util.Map; + +/** + * An implementation of this interface is passed to all the Method Selectors when their + * includeMethod() is invoked. Method selectors can invoke any method of this context at that time. + * + *

Created on Jan 3, 2007 + * + * @author Cedric Beust + */ +public interface IMethodSelectorContext { + + /** @return true if no more Method Selectors should be invoked after the current one. */ + boolean isStopped(); + + /** + * Indicate that no other Method Selectors should be invoked after the current one if stopped is + * false. + * + * @param stopped The value + */ + void setStopped(boolean stopped); + + /** + * @return a Map that can be freely manipulated by the Method Selector. This can be used to share + * information among several Method Selectors. + */ + Map getUserData(); +} diff --git a/testng-core-api/src/main/java/org/testng/IModule.java b/testng-core-api/src/main/java/org/testng/IModule.java new file mode 100644 index 0000000..cc6ce56 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IModule.java @@ -0,0 +1,11 @@ +package org.testng; + +import com.google.inject.Module; + +/** + * This interface provides {@link Module} to implicitly add to the Guice context. Classes that + * implement this interface are instantiated with {@link java.util.ServiceLoader ServiceLoader}. + */ +public interface IModule { + Module getModule(); +} diff --git a/testng-core-api/src/main/java/org/testng/IModuleFactory.java b/testng-core-api/src/main/java/org/testng/IModuleFactory.java new file mode 100644 index 0000000..8055be8 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IModuleFactory.java @@ -0,0 +1,17 @@ +package org.testng; + +import com.google.inject.Module; + +/** + * This interface is used by the moduleFactory attribute of the @Guice annotation. It allows users + * to use different Guice modules based on the test class waiting to be injected. + */ +public interface IModuleFactory { + + /** + * @param context The current test context + * @param testClass The test class + * @return The Guice module that should be used to get an instance of this test class. + */ + Module createModule(ITestContext context, Class testClass); +} diff --git a/testng-core-api/src/main/java/org/testng/IObjectFactory.java b/testng-core-api/src/main/java/org/testng/IObjectFactory.java new file mode 100644 index 0000000..a671850 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IObjectFactory.java @@ -0,0 +1,12 @@ +package org.testng; + +/** + * Factory used to create all test instances. This factory is passed the constructor along with the + * parameters that TestNG calculated based on the environment (@Parameters, etc...). + * + * @see IObjectFactory2 + * @since 5.6 + * @deprecated - This interface stands deprecated as of TestNG 7.5.0 + */ +@Deprecated +public interface IObjectFactory extends ITestObjectFactory {} diff --git a/testng-core-api/src/main/java/org/testng/IObjectFactory2.java b/testng-core-api/src/main/java/org/testng/IObjectFactory2.java new file mode 100644 index 0000000..a690d71 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IObjectFactory2.java @@ -0,0 +1,23 @@ +package org.testng; + +/** + * Factory used to create all test instances. This object factory only receives the class in + * parameter. + * + * @see org.testng.ITestObjectFactory + * @since 5.14.6 + * @deprecated - This interface stands deprecated as of TestNG 7.5.0 + */ +@Deprecated +public interface IObjectFactory2 extends ITestObjectFactory { + + /** + * @deprecated - This interface stands deprecated as of TestNG 7.5.0 + * @param cls - The class for which a new instance is to be created + * @return - The newly created object. + */ + @Deprecated + default Object newInstance(Class cls) { + return newInstance(cls, new Object[0]); + } +} diff --git a/testng-core-api/src/main/java/org/testng/IReporter.java b/testng-core-api/src/main/java/org/testng/IReporter.java new file mode 100644 index 0000000..029f705 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IReporter.java @@ -0,0 +1,38 @@ +package org.testng; + +import java.util.List; +import org.testng.reporters.IReporterConfig; +import org.testng.reporters.PojoReporterConfig; +import org.testng.xml.XmlSuite; + +/** + * This interface can be implemented by clients to generate a report. Its method generateReport() + * will be invoked after all the suite have run and the parameters give all the test results that + * happened during that run. + */ +public interface IReporter extends ITestNGListener { + /** + * Generate a report for the given suites into the specified output directory. + * + * @param xmlSuites The list of XmlSuite + * @param suites The list of ISuite + * @param outputDirectory The output directory + */ + default void generateReport( + List xmlSuites, List suites, String outputDirectory) { + // not implemented + } + + /** + * Get the reporter configuration object. + * + *

NOTE: Reporter configuration objects must adhere to the JavaBean object conventions, + * providing getter and setter methods that conform to standard naming rules. This enables {@link + * org.testng.internal.ReporterConfig} to serialize, deserialize, and instantiate the reporter. + * + * @return reporter configuration object + */ + default IReporterConfig getConfig() { + return new PojoReporterConfig(this); + } +} diff --git a/testng-core-api/src/main/java/org/testng/IResultMap.java b/testng-core-api/src/main/java/org/testng/IResultMap.java new file mode 100644 index 0000000..a111aa0 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IResultMap.java @@ -0,0 +1,21 @@ +package org.testng; + +import java.util.Collection; +import java.util.Set; + +public interface IResultMap { + + void addResult(ITestResult result); + + Set getResults(ITestNGMethod method); + + Set getAllResults(); + + void removeResult(ITestNGMethod m); + + void removeResult(ITestResult r); + + Collection getAllMethods(); + + int size(); +} diff --git a/testng-core-api/src/main/java/org/testng/IRetryAnalyzer.java b/testng-core-api/src/main/java/org/testng/IRetryAnalyzer.java new file mode 100644 index 0000000..6570a6e --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/IRetryAnalyzer.java @@ -0,0 +1,17 @@ +package org.testng; + +/** + * Interface to implement to be able to have a chance to retry a failed test. + * + * @author tocman@gmail.com (Jeremie Lenfant-Engelmann) + */ +public interface IRetryAnalyzer { + + /** + * Returns true if the test method has to be retried, false otherwise. + * + * @param result The result of the test method that just ran. + * @return true if the test method has to be retried, false otherwise. + */ + boolean retry(ITestResult result); +} diff --git a/testng-core-api/src/main/java/org/testng/ISuite.java b/testng-core-api/src/main/java/org/testng/ISuite.java new file mode 100644 index 0000000..773f95b --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/ISuite.java @@ -0,0 +1,94 @@ +package org.testng; + +import com.google.inject.Injector; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import org.testng.internal.annotations.IAnnotationFinder; +import org.testng.xml.XmlSuite; + +/** + * Interface defining a Test Suite. + * + * @author Cedric Beust, Aug 6, 2004 + */ +public interface ISuite extends IAttributes { + + /** @return the name of this suite. */ + String getName(); + + /** @return The results for this suite. */ + Map getResults(); + + /** @return The object factory used to create all test instances. */ + ITestObjectFactory getObjectFactory(); + + @Deprecated + /** @deprecated - This interface stands deprecated as of TestNG 7.5.0 */ + default IObjectFactory2 getObjectFactory2() { + return null; + } + + /** @return The output directory used for the reports. */ + String getOutputDirectory(); + + /** @return true if the tests must be run in parallel. */ + String getParallel(); + + String getParentModule(); + + String getGuiceStage(); + + /** + * @param parameterName The name of the parameter + * @return The value of this parameter, or null if none was specified. + */ + String getParameter(String parameterName); + + /** + * Retrieves the map of groups and their associated test methods. + * + * @return A map where the key is the group and the value is a list of methods used by this group. + */ + Map> getMethodsByGroups(); + + /** @return a list of all the methods that were invoked in this suite. */ + List getAllInvokedMethods(); + + /** @return All the methods that were not included in this test run. */ + Collection getExcludedMethods(); + + /** Triggers the start of running tests included in the suite. */ + void run(); + + /** + * @return The host where this suite was run, or null if it was run locally. The returned string + * has the form: host:port + */ + String getHost(); + + /** + * Retrieves the shared state for a suite. + * + * @return the share state of the current suite. + */ + SuiteRunState getSuiteState(); + + /** @return the annotation finder used for the specified type (JDK5 or javadoc) */ + IAnnotationFinder getAnnotationFinder(); + + /** @return The representation of the current XML suite file. */ + XmlSuite getXmlSuite(); + + void addListener(ITestNGListener listener); + + Injector getParentInjector(); + + void setParentInjector(Injector injector); + + /** + * @return the total number of methods found in this suite. The presence of factories or data + * providers might cause the actual number of test methods run be bigger than this list. + */ + List getAllMethods(); +} diff --git a/testng-core-api/src/main/java/org/testng/ISuiteListener.java b/testng-core-api/src/main/java/org/testng/ISuiteListener.java new file mode 100644 index 0000000..ada0401 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/ISuiteListener.java @@ -0,0 +1,26 @@ +package org.testng; + +/** + * Listener for test suites. + * + * @author Cedric Beust, Aug 6, 2004 + */ +public interface ISuiteListener extends ITestNGListener { + /** + * This method is invoked before the SuiteRunner starts. + * + * @param suite The suite + */ + default void onStart(ISuite suite) { + // not implemented + } + + /** + * This method is invoked after the SuiteRunner has run all the tests in the suite. + * + * @param suite The suite + */ + default void onFinish(ISuite suite) { + // not implemented + } +} diff --git a/testng-core-api/src/main/java/org/testng/ISuiteResult.java b/testng-core-api/src/main/java/org/testng/ISuiteResult.java new file mode 100644 index 0000000..9848233 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/ISuiteResult.java @@ -0,0 +1,8 @@ +package org.testng; + +/** This class represents the result of a suite run. */ +public interface ISuiteResult { + + /** @return The testing context for these tests. */ + ITestContext getTestContext(); +} diff --git a/testng-core-api/src/main/java/org/testng/ITest.java b/testng-core-api/src/main/java/org/testng/ITest.java new file mode 100644 index 0000000..d95d990 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/ITest.java @@ -0,0 +1,17 @@ +package org.testng; + +/** + * If a test class implements this interface, it will receive a special treatment, such as having + * the test name displayed in the HTML reports. + * + * @author cbeust Jun 6, 2006 + */ +public interface ITest { + + /** + * The name of test instance(s). + * + * @return name associated with a particular instance of a test. + */ + String getTestName(); +} diff --git a/testng-core-api/src/main/java/org/testng/ITestClass.java b/testng-core-api/src/main/java/org/testng/ITestClass.java new file mode 100644 index 0000000..69eedd5 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/ITestClass.java @@ -0,0 +1,100 @@ +package org.testng; + +/** + * This class represents a test class: + * + *

    + *
  • The test methods + *
  • The configuration methods (test and method) + *
  • The class file + *
+ * + * Note that the methods returned by instances of this class are expected to be correct at runtime. + * In other words, they might differ from what the ITestMethodFinder returned since ITestClass will + * take into account the groups being included and excluded. + */ +public interface ITestClass extends IClass { + + /** + * Returns all the applicable test methods. + * + * @return All the applicable test methods. + */ + ITestNGMethod[] getTestMethods(); + + /** + * Returns all the methods that should be invoked before a test method is invoked. + * + * @return All the methods that should be invoked before a test method is invoked. + */ + ITestNGMethod[] getBeforeTestMethods(); + + /** + * Returns all the methods that should be invoked after a test method completes. + * + * @return All the methods that should be invoked after a test method completes. + */ + ITestNGMethod[] getAfterTestMethods(); + + /** + * Return all the methods that should be invoked after the test class has been created and before + * any of its test methods is invoked. + * + * @return All the methods that should be invoked after the test class has been created and before + * any of its test methods is invoked. + */ + ITestNGMethod[] getBeforeClassMethods(); + + /** + * Returns all the methods that should be invoked after all the tests have been run on this class. + * + * @return All the methods that should be invoked after all the tests have been run on this class. + */ + ITestNGMethod[] getAfterClassMethods(); + + /** + * Returns All the methods that should be invoked before the suite is run. + * + * @return All the methods that should be invoked before the suite is run. + */ + ITestNGMethod[] getBeforeSuiteMethods(); + + /** + * Returns all the methods that should be invoked after the suite has run. + * + * @return All the methods that should be invoked after the suite has run. + */ + ITestNGMethod[] getAfterSuiteMethods(); + + /** + * Returns all @Configuration methods that should be invoked before any others in the current + * test. + * + * @return all @Configuration methods that should be invoked before any others in the current + * test. + */ + ITestNGMethod[] getBeforeTestConfigurationMethods(); + + /** + * Returns all @Configuration methods that should be invoked last before any others in the + * current test. + * + * @return all @Configuration methods that should be invoked last before any others in the current + * test. + */ + ITestNGMethod[] getAfterTestConfigurationMethods(); + + /** + * Returns all @Configuration methods that should be invoked before certain groups. + * + * @return all @Configuration methods that should be invoked before certain groups. + */ + ITestNGMethod[] getBeforeGroupsMethods(); + + /** + * Returns all @Configuration methods that should be invoked after certain groups. + * + * @return all @Configuration methods that should be invoked after certain groups. + */ + ITestNGMethod[] getAfterGroupsMethods(); +} diff --git a/testng-core-api/src/main/java/org/testng/ITestClassFinder.java b/testng-core-api/src/main/java/org/testng/ITestClassFinder.java new file mode 100644 index 0000000..1d82c0e --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/ITestClassFinder.java @@ -0,0 +1,24 @@ +package org.testng; + +/** + * This class is used by TestNG to locate the test classes. + * + * @author Cedric Beust + */ +public interface ITestClassFinder { + /** + * @return An array of all the classes that contain test methods. This method usually returns an + * array of one class, which is the class on which TestNG is running, except in the following + * cases. - TestNG: the class contains an @Factory method - JUnit: the class contains a + * suite() method + */ + IClass[] findTestClasses(); + + /** + * Return the IClass for a given class + * + * @param cls The class + * @return The related IClass + */ + IClass getIClass(Class cls); +} diff --git a/testng-core-api/src/main/java/org/testng/ITestContext.java b/testng-core-api/src/main/java/org/testng/ITestContext.java new file mode 100644 index 0000000..5de1055 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/ITestContext.java @@ -0,0 +1,82 @@ +package org.testng; + +import java.util.Collection; +import java.util.Date; +import org.testng.xml.XmlTest; + +/** + * This class defines a test context which contains all the information for a given test run. An + * instance of this context is passed to the test listeners so they can query information about + * their environment. + * + * @author Cedric Beust, Aug 6, 2004 + */ +public interface ITestContext extends IAttributes { + + /** @return The name of this test. */ + String getName(); + + /** @return When this test started running. */ + Date getStartDate(); + + /** @return When this test stopped running. */ + Date getEndDate(); + + /** @return A list of all the tests that run successfully. */ + IResultMap getPassedTests(); + + /** @return A list of all the tests that were skipped */ + IResultMap getSkippedTests(); + + /** + * @return A list of all the tests that failed but are being ignored because annotated with a + * successPercentage. + */ + IResultMap getFailedButWithinSuccessPercentageTests(); + + /** + * @return A map of all the tests that failed, indexed by their ITestNGMethod. + * @see org.testng.ITestNGMethod + */ + IResultMap getFailedTests(); + + /** @return All the groups that are included for this test run. */ + String[] getIncludedGroups(); + + /** @return All the groups that are excluded for this test run. */ + String[] getExcludedGroups(); + + /** @return Where the reports will be generated. */ + String getOutputDirectory(); + + /** @return The Suite object that was passed to the runner at start-up. */ + ISuite getSuite(); + + /** @return All the test methods that were run. */ + ITestNGMethod[] getAllTestMethods(); + + /** + * @return The host where this test was run, or null if it was run locally. The returned string + * has the form: host:port + */ + String getHost(); + + /** @return All the methods that were not included in this test run. */ + Collection getExcludedMethods(); + + /** @return The information about the successful configuration method invocations. */ + IResultMap getPassedConfigurations(); + + /** @return The information about the skipped configuration method invocations. */ + IResultMap getSkippedConfigurations(); + + /** @return The information about the failed configuration method invocations. */ + IResultMap getFailedConfigurations(); + + /** @return the current XmlTest. */ + XmlTest getCurrentXmlTest(); + + default IInjectorFactory getInjectorFactory() { + return null; + } +} diff --git a/testng-core-api/src/main/java/org/testng/ITestListener.java b/testng-core-api/src/main/java/org/testng/ITestListener.java new file mode 100644 index 0000000..2e66555 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/ITestListener.java @@ -0,0 +1,91 @@ +package org.testng; + +/** + * A listener for test running. + * + * @author Cedric Beust + * @author Alexandru Popescu + * @author Hani Suleiman + */ +public interface ITestListener extends ITestNGListener { + /** + * Invoked each time before a test will be invoked. The ITestResult is only partially + * filled with the references to class, method, start millis and status. + * + * @param result the partially filled ITestResult + * @see ITestResult#STARTED + */ + default void onTestStart(ITestResult result) { + // not implemented + } + + /** + * Invoked each time a test succeeds. + * + * @param result ITestResult containing information about the run test + * @see ITestResult#SUCCESS + */ + default void onTestSuccess(ITestResult result) { + // not implemented + } + + /** + * Invoked each time a test fails. + * + * @param result ITestResult containing information about the run test + * @see ITestResult#FAILURE + */ + default void onTestFailure(ITestResult result) { + // not implemented + } + + /** + * Invoked each time a test is skipped. + * + * @param result ITestResult containing information about the run test + * @see ITestResult#SKIP + */ + default void onTestSkipped(ITestResult result) { + // not implemented + } + + /** + * Invoked each time a method fails but has been annotated with successPercentage and this failure + * still keeps it within the success percentage requested. + * + * @param result ITestResult containing information about the run test + * @see ITestResult#SUCCESS_PERCENTAGE_FAILURE + */ + default void onTestFailedButWithinSuccessPercentage(ITestResult result) { + // not implemented + } + + /** + * Invoked each time a test fails due to a timeout. + * + * @param result ITestResult containing information about the run test + */ + default void onTestFailedWithTimeout(ITestResult result) { + onTestFailure(result); + } + + /** + * Invoked before running all the test methods belonging to the classes inside the <test> + * tag and calling all their Configuration methods. + * + * @param context The test context + */ + default void onStart(ITestContext context) { + // not implemented + } + + /** + * Invoked after all the test methods belonging to the classes inside the <test> tag have + * run and all their Configuration methods have been called. + * + * @param context The test context + */ + default void onFinish(ITestContext context) { + // not implemented + } +} diff --git a/testng-core-api/src/main/java/org/testng/ITestMethodFinder.java b/testng-core-api/src/main/java/org/testng/ITestMethodFinder.java new file mode 100644 index 0000000..c463c3b --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/ITestMethodFinder.java @@ -0,0 +1,67 @@ +package org.testng; + +import org.testng.xml.XmlTest; + +/** + * This interface allows to modify the strategy used by TestRunner to find its test methods. At the + * time of this writing, TestNG supports two different strategies: TestNG (using annotations to + * locate these methods) and JUnit (setUp()/tearDown() and all methods that start with "test" or + * have a suite() method). + * + * @author Cedric Beust, May 3, 2004 + */ +public interface ITestMethodFinder { + + /** + * @param cls The test class + * @param xmlTest The test node of xml + * @return All the applicable test methods. + */ + ITestNGMethod[] getTestMethods(Class cls, XmlTest xmlTest); + + /** + * @param cls The test class + * @return All the methods that should be invoked before a test method is invoked. + */ + ITestNGMethod[] getBeforeTestMethods(Class cls); + + /** + * @param cls The test class + * @return All the methods that should be invoked after a test method completes. + */ + ITestNGMethod[] getAfterTestMethods(Class cls); + + /** + * @param cls The test class + * @return All the methods that should be invoked after the test class has been created and before + * any of its test methods is invoked. + */ + ITestNGMethod[] getBeforeClassMethods(Class cls); + + /** + * @param cls The test class + * @return All the methods that should be invoked after the test class has been created and after + * all its test methods have completed. + */ + ITestNGMethod[] getAfterClassMethods(Class cls); + + /** + * @param cls The test class + * @return All the methods that should be invoked before the suite starts running. + */ + ITestNGMethod[] getBeforeSuiteMethods(Class cls); + + /** + * @param cls The test class + * @return All the methods that should be invoked after the suite has run all its tests. + */ + ITestNGMethod[] getAfterSuiteMethods(Class cls); + + ITestNGMethod[] getBeforeTestConfigurationMethods(Class testClass); + + ITestNGMethod[] getAfterTestConfigurationMethods(Class testClass); + + ITestNGMethod[] getBeforeGroupsConfigurationMethods(Class testClass); + + ITestNGMethod[] getAfterGroupsConfigurationMethods(Class testClass); +} diff --git a/testng-core-api/src/main/java/org/testng/ITestNGListener.java b/testng-core-api/src/main/java/org/testng/ITestNGListener.java new file mode 100644 index 0000000..b167ac5 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/ITestNGListener.java @@ -0,0 +1,8 @@ +package org.testng; + +/** + * This is a marker interface for all objects that can be passed as a -listener argument. + * + * @author cbeust + */ +public interface ITestNGListener {} diff --git a/testng-core-api/src/main/java/org/testng/ITestNGListenerFactory.java b/testng-core-api/src/main/java/org/testng/ITestNGListenerFactory.java new file mode 100644 index 0000000..a4b48db --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/ITestNGListenerFactory.java @@ -0,0 +1,17 @@ +package org.testng; + +/** + * A factory used to create instances of ITestNGListener. Users can implement this interface in any + * of their test classes but there can be only one such instance. + */ +public interface ITestNGListenerFactory { + + /** + * Create and return an instance of the listener class passed in parameter. Return null if you + * want to use the default factory. + * + * @param listenerClass The class of listener to create + * @return The created listener + */ + ITestNGListener createListener(Class listenerClass); +} diff --git a/testng-core-api/src/main/java/org/testng/ITestNGMethod.java b/testng-core-api/src/main/java/org/testng/ITestNGMethod.java new file mode 100644 index 0000000..9866332 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/ITestNGMethod.java @@ -0,0 +1,272 @@ +package org.testng; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import org.testng.annotations.CustomAttribute; +import org.testng.internal.ConstructorOrMethod; +import org.testng.internal.IParameterInfo; +import org.testng.xml.XmlTest; + +/** + * Describes a TestNG annotated method and the instance on which it will be invoked. + * + *

This interface is not meant to be implemented by users. + */ +public interface ITestNGMethod extends Cloneable { + + /** + * @return The real class on which this method was declared (can be different from + * getMethod().getDeclaringClass() if the test method was defined in a superclass). + */ + Class getRealClass(); + + ITestClass getTestClass(); + + /** + * Sets the test class having this method. This is not necessarily the declaring class. + * + * @param cls The test class having this method. + */ + void setTestClass(ITestClass cls); + + /** + * Returns the method name. This is needed for serialization because methods are not Serializable. + * + * @return the method name. + */ + String getMethodName(); + + Object getInstance(); + + /** + * Needed for serialization. + * + * @return The hashcode of instances + */ + long[] getInstanceHashCodes(); + + /** + * @return The groups this method belongs to, possibly added to the groups declared on the class. + */ + String[] getGroups(); + + /** + * @return The groups this method depends on, possibly added to the groups declared on the class. + */ + String[] getGroupsDependedUpon(); + + /** @return If a group was not found. */ + String getMissingGroup(); + + void setMissingGroup(String group); + + String[] getBeforeGroups(); + + String[] getAfterGroups(); + + /** + * @return The methods this method depends on, possibly added to the methods declared on the + * class. + */ + String[] getMethodsDependedUpon(); + + void addMethodDependedUpon(String methodName); + + /** @return true if this method was annotated with @Test */ + boolean isTest(); + + /** @return true if this method was annotated with @Configuration and beforeTestMethod = true */ + boolean isBeforeMethodConfiguration(); + + /** @return true if this method was annotated with @Configuration and beforeTestMethod = false */ + boolean isAfterMethodConfiguration(); + + /** @return true if this method was annotated with @Configuration and beforeClassMethod = true */ + boolean isBeforeClassConfiguration(); + + /** @return true if this method was annotated with @Configuration and beforeClassMethod = false */ + boolean isAfterClassConfiguration(); + + /** @return true if this method was annotated with @Configuration and beforeSuite = true */ + boolean isBeforeSuiteConfiguration(); + + /** @return true if this method was annotated with @Configuration and afterSuite = true */ + boolean isAfterSuiteConfiguration(); + + /** @return true if this method is a @BeforeTest (@Configuration beforeTest=true) */ + boolean isBeforeTestConfiguration(); + + /** @return true if this method is an @AfterTest (@Configuration afterTest=true) */ + boolean isAfterTestConfiguration(); + + boolean isBeforeGroupsConfiguration(); + + boolean isAfterGroupsConfiguration(); + + default boolean hasBeforeGroupsConfiguration() { + return false; + } + + default boolean hasAfterGroupsConfiguration() { + return false; + } + + /** @return The timeout in milliseconds. */ + long getTimeOut(); + + void setTimeOut(long timeOut); + + /** @return the number of times this method needs to be invoked. */ + int getInvocationCount(); + + void setInvocationCount(int count); + + /** @return the success percentage for this method (between 0 and 100). */ + int getSuccessPercentage(); + + /** @return The id of the thread this method was run in. */ + String getId(); + + void setId(String id); + + long getDate(); + + void setDate(long date); + + /** + * @param testClass The test class + * @return true if this ITestNGMethod can be invoked from within IClass. + */ + boolean canRunFromClass(IClass testClass); + + /** @return true if this method is alwaysRun=true */ + boolean isAlwaysRun(); + + /** @return the number of threads to be used when invoking the method on parallel */ + int getThreadPoolSize(); + + void setThreadPoolSize(int threadPoolSize); + + boolean getEnabled(); + + String getDescription(); + + void setDescription(String description); + + void incrementCurrentInvocationCount(); + + int getCurrentInvocationCount(); + + void setParameterInvocationCount(int n); + + int getParameterInvocationCount(); + + void setMoreInvocationChecker(Callable moreInvocationChecker); + + boolean hasMoreInvocation(); + + ITestNGMethod clone(); + + IRetryAnalyzer getRetryAnalyzer(ITestResult result); + + void setRetryAnalyzerClass(Class clazz); + + Class getRetryAnalyzerClass(); + + boolean skipFailedInvocations(); + + void setSkipFailedInvocations(boolean skip); + + /** @return The time under which all invocationCount methods need to complete by. */ + long getInvocationTimeOut(); + + boolean ignoreMissingDependencies(); + + void setIgnoreMissingDependencies(boolean ignore); + + /** + * Which invocation numbers of this method should be used (only applicable if it uses a data + * provider). If this value is an empty list, use all the values returned from the data provider. + * These values are read from the XML file in the <include invocationNumbers="..."> + * tag. + * + * @return The list of invocation numbers + */ + List getInvocationNumbers(); + + void setInvocationNumbers(List numbers); + + /** + * The list of invocation numbers that failed, which is only applicable for methods that have a + * data provider. + * + * @param number The invocation number that failed + */ + void addFailedInvocationNumber(int number); + + List getFailedInvocationNumbers(); + + /** + * The scheduling priority. Lower priorities get scheduled first. + * + * @return The priority value + */ + int getPriority(); + + void setPriority(int priority); + + int getInterceptedPriority(); + + void setInterceptedPriority(int priority); + + /** @return the XmlTest this method belongs to. */ + XmlTest getXmlTest(); + + ConstructorOrMethod getConstructorOrMethod(); + + /** + * @param test - The {@link XmlTest} object. + * @return the parameters found in the include tag, if any + */ + Map findMethodParameters(XmlTest test); + + /** + * getRealClass().getName() + "." + getMethodName() + * + * @return qualified name for this method + */ + String getQualifiedName(); + + default boolean isDataDriven() { + return false; + } + + /** + * @return - A {@link IParameterInfo} object that represents details about the parameters + * associated with the factory method. + */ + default IParameterInfo getFactoryMethodParamsInfo() { + return null; + } + + /** + * @return - An array of {@link CustomAttribute} that represents the custom attributes associated + * with a test. + */ + default CustomAttribute[] getAttributes() { + return new CustomAttribute[] {}; + } + + /** + * @return - An {@link IDataProviderMethod} for a data provider powered test method and null + * otherwise. + */ + default IDataProviderMethod getDataProviderMethod() { + return null; + } + + default Class[] getParameterTypes() { + return new Class[] {}; + } +} diff --git a/testng-core-api/src/main/java/org/testng/ITestObjectFactory.java b/testng-core-api/src/main/java/org/testng/ITestObjectFactory.java new file mode 100644 index 0000000..b2c9505 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/ITestObjectFactory.java @@ -0,0 +1,20 @@ +package org.testng; + +import java.lang.reflect.Constructor; +import org.testng.internal.objects.InstanceCreator; + +/** Parent interface of all the object factories. */ +public interface ITestObjectFactory { + + default T newInstance(Class cls, Object... parameters) { + return InstanceCreator.newInstance(cls, parameters); + } + + default T newInstance(String clsName, Object... parameters) { + return InstanceCreator.newInstance(clsName, parameters); + } + + default T newInstance(Constructor constructor, Object... parameters) { + return InstanceCreator.newInstance(constructor, parameters); + } +} diff --git a/testng-core-api/src/main/java/org/testng/ITestResult.java b/testng-core-api/src/main/java/org/testng/ITestResult.java new file mode 100644 index 0000000..08db76c --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/ITestResult.java @@ -0,0 +1,131 @@ +package org.testng; + +import java.util.Collections; +import java.util.List; +import org.testng.internal.thread.ThreadTimeoutException; + +/** + * This class describes the result of a test. + * + * @author Cedric Beust, May 2, 2004 + * @version $Revision: 721 $, $Date: 2009-05-23 09:55:46 -0700 (Sat, 23 May 2009) $ + * @since May 2, 2004 + */ +public interface ITestResult extends IAttributes, Comparable { + + // Test status + int CREATED = -1; + int SUCCESS = 1; + int FAILURE = 2; + int SKIP = 3; + int SUCCESS_PERCENTAGE_FAILURE = 4; + int STARTED = 16; + + /** @return The status of this result, using one of the constants above. */ + int getStatus(); + + void setStatus(int status); + + /** @return The test method this result represents. */ + ITestNGMethod getMethod(); + + /** @return The parameters this method was invoked with. */ + Object[] getParameters(); + + void setParameters(Object[] parameters); + + /** @return The test class used this object is a result for. */ + IClass getTestClass(); + + /** + * @return The throwable that was thrown while running the method, or null if no exception was + * thrown. + */ + Throwable getThrowable(); + + void setThrowable(Throwable throwable); + + /** @return the start date for this test, in milliseconds. */ + long getStartMillis(); + + /** @return the end date for this test, in milliseconds. */ + long getEndMillis(); + + void setEndMillis(long millis); + + /** @return The name of this TestResult, typically identical to the name of the method. */ + String getName(); + + /** @return true if if this test run is a SUCCESS */ + boolean isSuccess(); + + /** + * @return The host where this suite was run, or null if it was run locally. The returned string + * has the form: host:port + */ + String getHost(); + + /** @return The instance on which this method was run. */ + Object getInstance(); + + /** + * @return - A parameter array that was passed to a factory method (or) an empty object array + * otherwise. + */ + Object[] getFactoryParameters(); + + /** + * @return The test name if this result's related instance implements ITest or + * use @Test(testName=...), null otherwise. + */ + String getTestName(); + + String getInstanceName(); + + /** @return the {@link ITestContext} for this test result. */ + ITestContext getTestContext(); + + /** @param name - The new name to be used as a test name */ + void setTestName(String name); + + /** + * @return - true if the test was retried again by an implementation of {@link + * IRetryAnalyzer} + */ + boolean wasRetried(); + + /** + * @param wasRetried - true if the test was retried and false otherwise. + */ + void setWasRetried(boolean wasRetried); + + /** + * @return - The list of either upstream method(s) or configuration method(s) whose failure led to + * the current method being skipped. An empty list is returned when the current method is not + * a skipped method. + */ + default List getSkipCausedBy() { + return Collections.emptyList(); + } + + /** + * @return - A unique id for the current JVM that represents a unique way of identifying a + * specific test method's result. + */ + String id(); + + /** + * @param result - The test result of a method + * @return - true if the test failure was due to a timeout. + */ + static boolean wasFailureDueToTimeout(ITestResult result) { + Throwable cause = result.getThrowable(); + while (cause != null && !cause.getClass().equals(Throwable.class)) { + if (cause instanceof ThreadTimeoutException) { + return true; + } + cause = cause.getCause(); + } + return false; + } +} diff --git a/testng-core-api/src/main/java/org/testng/Reporter.java b/testng-core-api/src/main/java/org/testng/Reporter.java new file mode 100644 index 0000000..704fc8e --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/Reporter.java @@ -0,0 +1,177 @@ +package org.testng; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Vector; +import org.testng.collections.Lists; +import org.testng.collections.Maps; +import org.testng.internal.Utils; +import org.testng.util.Strings; + +/** + * This class is used for test methods to log messages that will be included in the HTML reports + * generated by TestNG.
+ *
+ * Implementation details
+ *
+ * The reporter keeps a combined output of strings (in m_output) and also a record of which method + * output which line. In order to do this, callers specify what the current method is with + * setCurrentTestResult() and the Reporter maintains a mapping of each test result with a list of + * integers. These integers are indices in the combined output (avoids duplicating the output). + * + *

Created on Nov 2, 2005 + * + * @author cbeust + */ +public class Reporter { + // When tests are run in parallel, each thread may be working with different + // 'current test result'. Also, this value should be inherited if the test code + // spawns its own thread. + private static final ThreadLocal m_currentTestResult = + new InheritableThreadLocal<>(); + + /** All output logged in a sequential order. */ + private static final List m_output = new Vector<>(); + + private static final Map> m_methodOutputMap = Maps.newHashMap(); + + private static boolean m_escapeHtml = false; + // This variable is responsible for persisting all output that is yet to be associated with any + // valid TestResult objects. + private static final ThreadLocal> m_orphanedOutput = new InheritableThreadLocal<>(); + + public static void setCurrentTestResult(ITestResult m) { + m_currentTestResult.set(m); + } + + public static List getOutput() { + return m_output; + } + + /** Erase the content of all the output generated so far. */ + public static void clear() { + m_methodOutputMap.clear(); + m_output.clear(); + } + + /** @return If true, use HTML entities for special HTML characters (<, >, &, ...). */ + public static boolean getEscapeHtml() { + return m_escapeHtml; + } + + /** + * @param escapeHtml If true, use HTML entities for special HTML characters (<, >, &, + * ...). + */ + public static void setEscapeHtml(boolean escapeHtml) { + m_escapeHtml = escapeHtml; + } + + private static synchronized void log(String s, ITestResult m) { + // Escape for the HTML reports. + if (m_escapeHtml) { + s = Strings.escapeHtml(s); + } + + if (m == null) { + // Persist the output temporarily into a Threadlocal String list. + if (m_orphanedOutput.get() == null) { + m_orphanedOutput.set(new ArrayList<>()); + } + m_orphanedOutput.get().add(s); + return; + } + + // Synchronization needed to ensure the line number and m_output are updated atomically. + int n = getOutput().size(); + + List lines = m_methodOutputMap.computeIfAbsent(m.id(), k -> Lists.newArrayList()); + + // Check if there was already some orphaned output for the current thread. + if (m_orphanedOutput.get() != null) { + n = n + m_orphanedOutput.get().size(); + getOutput().addAll(m_orphanedOutput.get()); + // Since we have already added all of the orphaned output to the current + // TestResult, let's clear it off. + m_orphanedOutput.remove(); + } + lines.add(n); + getOutput().add(s); + } + + /** + * Log the passed string to the HTML reports. + * + * @param s The message to log + */ + public static void log(String s) { + log(s, getCurrentTestResult()); + } + + /** + * Log the passed string to the HTML reports if the current verbosity is equal to or greater than + * the one passed as a parameter. If logToStandardOut is true, the string will also be printed on + * standard out. + * + * @param s The message to log + * @param level The verbosity of this message + * @param logToStandardOut Whether to print this string on standard out too + */ + public static void log(String s, int level, boolean logToStandardOut) { + if (Utils.getVerbose() >= level) { + log(s, getCurrentTestResult()); + if (logToStandardOut) { + System.out.println(s); + } + } + } + + /** + * Log the passed string to the HTML reports. If logToStandardOut is true, the string will also be + * printed on standard out. + * + * @param s The message to log + * @param logToStandardOut Whether to print this string on standard out too + */ + public static void log(String s, boolean logToStandardOut) { + log(s, getCurrentTestResult()); + if (logToStandardOut) { + System.out.println(s); + } + } + /** + * Log the passed string to the HTML reports if the current verbosity is equal to or greater than + * the one passed as a parameter. + * + * @param s The message to log + * @param level The verbosity of this message + */ + public static void log(String s, int level) { + if (Utils.getVerbose() >= level) { + log(s, getCurrentTestResult()); + } + } + + /** @return the current test result. */ + public static ITestResult getCurrentTestResult() { + return m_currentTestResult.get(); + } + + public static synchronized List getOutput(ITestResult tr) { + List result = Lists.newArrayList(); + if (tr == null) { + // Guard against a possible NPE in scenarios wherein the test result object itself could be a + // null value. + return result; + } + List lines = m_methodOutputMap.get(tr.id()); + if (lines != null) { + for (Integer n : lines) { + result.add(getOutput().get(n)); + } + } + + return result; + } +} diff --git a/testng-core-api/src/main/java/org/testng/SuiteRunState.java b/testng-core-api/src/main/java/org/testng/SuiteRunState.java new file mode 100644 index 0000000..d4258a5 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/SuiteRunState.java @@ -0,0 +1,20 @@ +package org.testng; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * A state object that records the status of the suite run. Mainly used to figure out if there are + * any @BeforeSuite failures. + */ +public class SuiteRunState { + + private final AtomicBoolean m_hasFailures = new AtomicBoolean(); + + public void failed() { + m_hasFailures.set(true); + } + + public boolean isFailed() { + return m_hasFailures.get(); + } +} diff --git a/testng-core-api/src/main/java/org/testng/TestNGException.java b/testng-core-api/src/main/java/org/testng/TestNGException.java new file mode 100644 index 0000000..fd1e31c --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/TestNGException.java @@ -0,0 +1,19 @@ +package org.testng; + +/** The base class for all exceptions thrown by TestNG. */ +public class TestNGException extends RuntimeException { + + private static final long serialVersionUID = -422675971506425913L; + + public TestNGException(Throwable t) { + super(t); + } + + public TestNGException(String string) { + super("\n" + string); + } + + public TestNGException(String string, Throwable t) { + super("\n" + string, t); + } +} diff --git a/testng-core-api/src/main/java/org/testng/annotations/AfterClass.java b/testng-core-api/src/main/java/org/testng/annotations/AfterClass.java new file mode 100644 index 0000000..19654ea --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/annotations/AfterClass.java @@ -0,0 +1,80 @@ +package org.testng.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@Target(java.lang.annotation.ElementType.METHOD) +@Documented +public @interface AfterClass { + /** + * Whether methods on this class/method are enabled. + * + * @return true if enabled (true by default) + */ + boolean enabled() default true; + + /** + * The list of groups this class/method belongs to. + * + * @return the value + */ + String[] groups() default {}; + + /** + * The list of groups this method depends on. Every method member of one of these groups is + * guaranteed to have been invoked before this method. Furthermore, if any of these methods was + * not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + * @return the value + */ + String[] dependsOnGroups() default {}; + + /** + * The list of methods this method depends on. There is no guarantee of the order in which the + * methods depended upon will be run, but you are guaranteed that all these methods will be run + * before the test method that contains this annotation is run. Furthermore, if any of these + * methods was not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + *

If some of these methods have been overloaded, all the overloaded versions will be run. + * + * @return the value + */ + String[] dependsOnMethods() default {}; + + /** + * For before methods (beforeSuite, beforeTest, beforeTestClass and beforeTestMethod, but not + * beforeGroups): if set to true, this configuration method will be run regardless of what groups + * it belongs to.
+ * For after methods (afterSuite, afterClass, ...): if set to true, this configuration method will + * be run even if one or more methods invoked previously failed or was skipped. + * + * @return the value (default false) + */ + boolean alwaysRun() default false; + + /** + * If true, this @Configuration method will belong to groups specified in the @Test + * annotation on the class (if any). + * + * @return the value (default true) + */ + boolean inheritGroups() default true; + + /** + * The description for this method. The string used will appear in the HTML report and also on + * standard output if verbose >= 2. + * + * @return the value (default empty) + */ + String description() default ""; + + /** + * The maximum number of milliseconds this method should take. If it hasn't returned after this + * time, this method will fail and it will cause test methods depending on it to be skipped. + * + * @return the value (default 0) + */ + long timeOut() default 0; +} diff --git a/testng-core-api/src/main/java/org/testng/annotations/AfterGroups.java b/testng-core-api/src/main/java/org/testng/annotations/AfterGroups.java new file mode 100644 index 0000000..f2bc0d0 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/annotations/AfterGroups.java @@ -0,0 +1,90 @@ +package org.testng.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@Target(java.lang.annotation.ElementType.METHOD) +@Documented +public @interface AfterGroups { + /** + * The list of groups that this configuration method will run after. If specified, it overrides + * the list of groups provided through {@link #groups()} attribute. This method is guaranteed to + * run shortly after the last test method that belongs to any of these groups is invoked. + * + * @return the value + */ + String[] value() default {}; + + /** + * Whether methods on this class/method are enabled. + * + * @return the value (default true) + */ + boolean enabled() default true; + + /** + * The list of groups this class/method belongs to. The list also describes the groups that this + * configuration method will be run after (if no {@link #value()} attribute is defined). + * + * @return the value + */ + String[] groups() default {}; + + /** + * The list of groups this method depends on. Every method member of one of these groups is + * guaranteed to have been invoked before this method. Furthermore, if any of these methods was + * not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + * @return the value + */ + String[] dependsOnGroups() default {}; + + /** + * The list of methods this method depends on. There is no guarantee of the order on which the + * methods depended upon will be run, but you are guaranteed that all these methods will be run + * before the test method that contains this annotation is run. Furthermore, if any of these + * methods was not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + *

If some of these methods have been overloaded, all the overloaded versions will be run. + * + * @return the value + */ + String[] dependsOnMethods() default {}; + + /** + * For before methods (beforeSuite, beforeTest, beforeTestClass and beforeTestMethod, but not + * beforeGroups): if set to true, this configuration method will be run regardless of what groups + * it belongs to.
+ * For after methods (afterSuite, afterClass, ...): if set to true, this configuration method will + * be run even if one or more methods invoked previously failed or was skipped. + * + * @return the value (default false) + */ + boolean alwaysRun() default false; + + /** + * If true, this @Configuration method will belong to groups specified in the @Test + * annotation on the class (if any). + * + * @return the value (default false) + */ + boolean inheritGroups() default false; + + /** + * The description for this method. The string used will appear in the HTML report and also on + * standard output if verbose > 2. + * + * @return the value (default empty) + */ + String description() default ""; + + /** + * The maximum number of milliseconds this method should take. If it hasn't returned after this + * time, this method will fail and it will cause test methods depending on it to be skipped. + * + * @return the value (default 0) + */ + long timeOut() default 0; +} diff --git a/testng-core-api/src/main/java/org/testng/annotations/AfterMethod.java b/testng-core-api/src/main/java/org/testng/annotations/AfterMethod.java new file mode 100644 index 0000000..fbe135f --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/annotations/AfterMethod.java @@ -0,0 +1,102 @@ +package org.testng.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@Target(java.lang.annotation.ElementType.METHOD) +@Documented +public @interface AfterMethod { + /** + * Whether methods on this class/method are enabled. + * + * @return the value (default true) + */ + boolean enabled() default true; + + /** + * The list of groups this class/method belongs to. Note that even if the test method that was + * invoked belongs to a different group, all @AfterMethod methods will be invoked after it as long + * as they belong to groups that were selected to run at all. See {@link #onlyForGroups()} to + * select test method groups which this method will be invoked after. + * + * @return the value + */ + String[] groups() default {}; + + /** + * The list of groups this method depends on. Every method member of one of these groups is + * guaranteed to have been invoked before this method. Furthermore, if any of these methods was + * not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + * @return the value + */ + String[] dependsOnGroups() default {}; + + /** + * The list of methods this method depends on. There is no guarantee of the order in which the + * methods depended upon will be run, but you are guaranteed that all these methods will be run + * before the test method that contains this annotation is run. Furthermore, if any of these + * methods was not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + *

If some of these methods have been overloaded, all the overloaded versions will be run. + * + * @return the value + */ + String[] dependsOnMethods() default {}; + + /** + * Causes this method to be invoked only if the test method belongs to a listed group. It can be + * used if different cleanups are needed for different groups. Omitting this or setting it to an + * empty list will cause this method to run after every test method, regardless of which group it + * belongs to. Otherwise, this method is only invoked if the test method that was invoked belongs + * to one of the groups listed here. + * + * @return the value + */ + String[] onlyForGroups() default {}; + + /** + * For before methods (beforeSuite, beforeTest, beforeTestClass and beforeTestMethod, but not + * beforeGroups): if set to true, this configuration method will be run regardless of what groups + * it belongs to.
+ * For after methods (afterSuite, afterClass, ...): if set to true, this configuration method will + * be run even if one or more test methods invoked previously failed or was skipped. + * + * @return the value (default false) + */ + boolean alwaysRun() default false; + + /** + * If true, this @Configuration method will belong to groups specified in the @Test + * annotation on the class (if any). + * + * @return the value (default true) + */ + boolean inheritGroups() default true; + + /** + * The description for this method. The string used will appear in the HTML report and also on + * standard output if verbose > 2. + * + * @return the value (default empty) + */ + String description() default ""; + + /** + * If true and the @Test method that was just run has an invocationCount > 1, this AfterMethod + * will only be invoked once (after the last test invocation). + * + * @return the value (default false) + */ + boolean lastTimeOnly() default false; + + /** + * The maximum number of milliseconds this method should take. If it hasn't returned after this + * time, this method will fail and it will cause test methods depending on it to be skipped. + * + * @return the value (default 0) + */ + long timeOut() default 0; +} diff --git a/testng-core-api/src/main/java/org/testng/annotations/AfterSuite.java b/testng-core-api/src/main/java/org/testng/annotations/AfterSuite.java new file mode 100644 index 0000000..5cf961a --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/annotations/AfterSuite.java @@ -0,0 +1,80 @@ +package org.testng.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@Target(java.lang.annotation.ElementType.METHOD) +@Documented +public @interface AfterSuite { + /** + * Whether methods on this class/method are enabled. + * + * @return the value (default true) + */ + boolean enabled() default true; + + /** + * The list of groups this class/method belongs to. + * + * @return the value + */ + String[] groups() default {}; + + /** + * The list of groups this method depends on. Every method member of one of these groups is + * guaranteed to have been invoked before this method. Furthermore, if any of these methods was + * not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + * @return the value + */ + String[] dependsOnGroups() default {}; + + /** + * The list of methods this method depends on. There is no guarantee of the order in which the + * methods depended upon will be run, but you are guaranteed that all these methods will be run + * before the test method that contains this annotation is run. Furthermore, if any of these + * methods was not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + *

If some of these methods have been overloaded, all the overloaded versions will be run. + * + * @return the value + */ + String[] dependsOnMethods() default {}; + + /** + * For before methods (beforeSuite, beforeTest, beforeTestClass and beforeTestMethod, but not + * beforeGroups): if set to true, this configuration method will be run regardless of what groups + * it belongs to.
+ * For after methods (afterSuite, afterClass, ...): if set to true, this configuration method will + * be run even if one or more methods invoked previously failed or was skipped. + * + * @return the value (default false) + */ + boolean alwaysRun() default false; + + /** + * If true, this @Configuration method will belong to groups specified in the @Test + * annotation on the class (if any). + * + * @return the value (default true) + */ + boolean inheritGroups() default true; + + /** + * The description for this method. The string used will appear in the HTML report and also on + * standard output if verbose > 2. + * + * @return the value (default empty) + */ + String description() default ""; + + /** + * The maximum number of milliseconds this method should take. If it hasn't returned after this + * time, this method will fail and it will cause test methods depending on it to be skipped. + * + * @return the valude (default 0) + */ + long timeOut() default 0; +} diff --git a/testng-core-api/src/main/java/org/testng/annotations/AfterTest.java b/testng-core-api/src/main/java/org/testng/annotations/AfterTest.java new file mode 100644 index 0000000..53344b2 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/annotations/AfterTest.java @@ -0,0 +1,80 @@ +package org.testng.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@Target(java.lang.annotation.ElementType.METHOD) +@Documented +public @interface AfterTest { + /** + * Whether methods on this class/method are enabled. + * + * @return the value (default true) + */ + boolean enabled() default true; + + /** + * The list of groups this class/method belongs to. + * + * @return the value + */ + String[] groups() default {}; + + /** + * The list of groups this method depends on. Every method member of one of these groups is + * guaranteed to have been invoked before this method. Furthermore, if any of these methods was + * not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + * @return the value + */ + String[] dependsOnGroups() default {}; + + /** + * The list of methods this method depends on. There is no guarantee of the order in which the + * methods depended upon will be run, but you are guaranteed that all these methods will be run + * before the test method that contains this annotation is run. Furthermore, if any of these + * methods was not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + *

If some of these methods have been overloaded, all the overloaded versions will be run. + * + * @return the value + */ + String[] dependsOnMethods() default {}; + + /** + * For before methods (beforeSuite, beforeTest, beforeTestClass and beforeTestMethod, but not + * beforeGroups): if set to true, this configuration method will be run regardless of what groups + * it belongs to.
+ * For after methods (afterSuite, afterClass, ...): if set to true, this configuration method will + * be run even if one or more methods invoked previously failed or was skipped. + * + * @return the value (default false) + */ + boolean alwaysRun() default false; + + /** + * If true, this @Configuration method will belong to groups specified in the @Test + * annotation on the class (if any). + * + * @return the value (default true) + */ + boolean inheritGroups() default true; + + /** + * The description for this method. The string used will appear in the HTML report and also on + * standard output if verbose > 2. + * + * @return the value (default empty) + */ + String description() default ""; + + /** + * The maximum number of milliseconds this method should take. If it hasn't returned after this + * time, this method will fail and it will cause test methods depending on it to be skipped. + * + * @return the value (default 0) + */ + long timeOut() default 0; +} diff --git a/testng-core-api/src/main/java/org/testng/annotations/BeforeClass.java b/testng-core-api/src/main/java/org/testng/annotations/BeforeClass.java new file mode 100644 index 0000000..a9d2afe --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/annotations/BeforeClass.java @@ -0,0 +1,80 @@ +package org.testng.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@Target(java.lang.annotation.ElementType.METHOD) +@Documented +public @interface BeforeClass { + /** + * Whether methods on this class/method are enabled. + * + * @return the value (default true) + */ + boolean enabled() default true; + + /** + * The list of groups this class/method belongs to. + * + * @return the value + */ + String[] groups() default {}; + + /** + * The list of groups this method depends on. Every method member of one of these groups is + * guaranteed to have been invoked before this method. Furthermore, if any of these methods was + * not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + * @return the value + */ + String[] dependsOnGroups() default {}; + + /** + * The list of methods this method depends on. There is no guarantee on the order on which the + * methods depended upon will be run, but you are guaranteed that all these methods will be run + * before the test method that contains this annotation is run. Furthermore, if any of these + * methods was not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + *

If some of these methods have been overloaded, all the overloaded versions will be run. + * + * @return the value + */ + String[] dependsOnMethods() default {}; + + /** + * For before methods (beforeSuite, beforeTest, beforeTestClass and beforeTestMethod, but not + * beforeGroups): If set to true, this configuration method will be run regardless of what groups + * it belongs to.
+ * For after methods (afterSuite, afterClass, ...): If set to true, this configuration method will + * be run even if one or more methods invoked previously failed or was skipped. + * + * @return the value (default false) + */ + boolean alwaysRun() default false; + + /** + * If true, this @Configuration method will belong to groups specified in the @Test + * annotation on the class (if any). + * + * @return the value (default true) + */ + boolean inheritGroups() default true; + + /** + * The description for this method. The string used will appear in the HTML report and also on + * standard output if verbose > 2. + * + * @return the value (default empty) + */ + String description() default ""; + + /** + * The maximum number of milliseconds this method should take. If it hasn't returned after this + * time, this method will fail and it will cause test methods depending on it to be skipped. + * + * @return the value (default 0) + */ + long timeOut() default 0; +} diff --git a/testng-core-api/src/main/java/org/testng/annotations/BeforeGroups.java b/testng-core-api/src/main/java/org/testng/annotations/BeforeGroups.java new file mode 100644 index 0000000..b25a184 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/annotations/BeforeGroups.java @@ -0,0 +1,90 @@ +package org.testng.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@Target(java.lang.annotation.ElementType.METHOD) +@Documented +public @interface BeforeGroups { + /** + * The list of groups that this configuration method will run before. If specified it overrides + * the list of groups provided through {@link #groups()} attribute. This method is guaranteed to + * run shortly before the first test method that belongs to any of these groups is invoked. + * + * @return the value + */ + String[] value() default {}; + + /** + * Whether methods on this class/method are enabled. + * + * @return the value (default true) + */ + boolean enabled() default true; + + /** + * The list of groups this class/method belongs to. This list also describes the groups that this + * configuration method will run before (if no {@link #value()} attribute is defined). + * + * @return the value + */ + String[] groups() default {}; + + /** + * The list of groups this method depends on. Every method member of one of these groups is + * guaranteed to have been invoked before this method. Furthermore, if any of these methods was + * not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + * @return the value + */ + String[] dependsOnGroups() default {}; + + /** + * The list of methods this method depends on. There is no guarantee on the order on which the + * methods depended upon will be run, but you are guaranteed that all these methods will be run + * before the test method that contains this annotation is run. Furthermore, if any of these + * methods was not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + *

If some of these methods have been overloaded, all the overloaded versions will be run. + * + * @return the value + */ + String[] dependsOnMethods() default {}; + + /** + * For before methods (beforeSuite, beforeTest, beforeTestClass and beforeTestMethod, but not + * beforeGroups): If set to true, this configuration method will be run regardless of what groups + * it belongs to.
+ * For after methods (afterSuite, afterClass, ...): If set to true, this configuration method will + * be run even if one or more methods invoked previously failed or was skipped. + * + * @return the value (default false) + */ + boolean alwaysRun() default false; + + /** + * If true, this @Configuration method will belong to groups specified in the @Test + * annotation on the class (if any). + * + * @return the value (default false) + */ + boolean inheritGroups() default false; + + /** + * The description for this method. The string used will appear in the HTML report and also on + * standard output if verbose > 2. + * + * @return the value (default empty) + */ + String description() default ""; + + /** + * The maximum number of milliseconds this method should take. If it hasn't returned after this + * time, this method will fail and it will cause test methods depending on it to be skipped. + * + * @return the value (default 0) + */ + long timeOut() default 0; +} diff --git a/testng-core-api/src/main/java/org/testng/annotations/BeforeMethod.java b/testng-core-api/src/main/java/org/testng/annotations/BeforeMethod.java new file mode 100644 index 0000000..7eec503 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/annotations/BeforeMethod.java @@ -0,0 +1,102 @@ +package org.testng.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@Target(java.lang.annotation.ElementType.METHOD) +@Documented +public @interface BeforeMethod { + /** + * Whether methods on this class/method are enabled. + * + * @return the value (default true) + */ + boolean enabled() default true; + + /** + * The list of groups this class/method belongs to. Note that even if the test method being + * invoked belongs to a different group, all @BeforeMethod methods will be invoked before it as + * long as they belong to groups that were selected to run at all. See {@link #onlyForGroups()} to + * select test method groups which this method will be invoked before. + * + * @return the value + */ + String[] groups() default {}; + + /** + * The list of groups this method depends on. Every method member of one of these groups is + * guaranteed to have been invoked before this method. Furthermore, if any of these methods was + * not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + * @return the value + */ + String[] dependsOnGroups() default {}; + + /** + * The list of methods this method depends on. There is no guarantee on the order on which the + * methods depended upon will be run, but you are guaranteed that all these methods will be run + * before the test method that contains this annotation is run. Furthermore, if any of these + * methods was not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + *

If some of these methods have been overloaded, all the overloaded versions will be run. + * + * @return the value + */ + String[] dependsOnMethods() default {}; + + /** + * Causes this method to be invoked only if the test method belongs to a listed group. It can be + * used if different setups are needed for different groups. Omitting this or setting it to an + * empty list will cause this method to run before every test method, regardless of which group it + * belongs to. Otherwise, this method is only invoked if the test method being invoked belongs to + * one of the groups listed here. + * + * @return the value + */ + String[] onlyForGroups() default {}; + + /** + * For before methods (beforeSuite, beforeTest, beforeTestClass and beforeTestMethod, but not + * beforeGroups): If set to true, this configuration method will be run regardless of what groups + * it belongs to.
+ * For after methods (afterSuite, afterClass, ...): If set to true, this configuration method will + * be run even if one or more methods invoked previously failed or was skipped. + * + * @return the value (default false) + */ + boolean alwaysRun() default false; + + /** + * If true, this @Configuration method will belong to groups specified in the @Test + * annotation on the class (if any). + * + * @return the value (default true) + */ + boolean inheritGroups() default true; + + /** + * The description for this method. The string used will appear in the HTML report and also on + * standard output if verbose >= 2. + * + * @return the value (default empty) + */ + String description() default ""; + + /** + * If true and the @Test method about to be run has an invocationCount > 1, this BeforeMethod + * will only be invoked once (before the first test invocation). + * + * @return the value (default false) + */ + boolean firstTimeOnly() default false; + + /** + * The maximum number of milliseconds this method should take. If it hasn't returned after this + * time, this method will fail and it will cause test methods depending on it to be skipped. + * + * @return the value (default 0) + */ + long timeOut() default 0; +} diff --git a/testng-core-api/src/main/java/org/testng/annotations/BeforeSuite.java b/testng-core-api/src/main/java/org/testng/annotations/BeforeSuite.java new file mode 100644 index 0000000..c1f3d86 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/annotations/BeforeSuite.java @@ -0,0 +1,80 @@ +package org.testng.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@Target(java.lang.annotation.ElementType.METHOD) +@Documented +public @interface BeforeSuite { + /** + * Whether methods on this class/method are enabled. + * + * @return the value (default true) + */ + boolean enabled() default true; + + /** + * The list of groups this class/method belongs to. + * + * @return the value + */ + String[] groups() default {}; + + /** + * The list of groups this method depends on. Every method member of one of these groups is + * guaranteed to have been invoked before this method. Furthermore, if any of these methods was + * not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + * @return the value + */ + String[] dependsOnGroups() default {}; + + /** + * The list of methods this method depends on. There is no guarantee on the order on which the + * methods depended upon will be run, but you are guaranteed that all these methods will be run + * before the test method that contains this annotation is run. Furthermore, if any of these + * methods was not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + *

If some of these methods have been overloaded, all the overloaded versions will be run. + * + * @return the value + */ + String[] dependsOnMethods() default {}; + + /** + * For before methods (beforeSuite, beforeTest, beforeTestClass and beforeTestMethod, but not + * beforeGroups): If set to true, this configuration method will be run regardless of what groups + * it belongs to.
+ * For after methods (afterSuite, afterClass, ...): If set to true, this configuration method will + * be run even if one or more methods invoked previously failed or was skipped. + * + * @return the value (default false) + */ + boolean alwaysRun() default false; + + /** + * If true, this @Configuration method will belong to groups specified in the @Test + * annotation on the class (if any). + * + * @return the value (default true) + */ + boolean inheritGroups() default true; + + /** + * The description for this method. The string used will appear in the HTML report and also on + * standard output if verbose >= 2. + * + * @return the value (default empty) + */ + String description() default ""; + + /** + * The maximum number of milliseconds this method should take. If it hasn't returned after this + * time, this method will fail and it will cause test methods depending on it to be skipped. + * + * @return the value (default 0) + */ + long timeOut() default 0; +} diff --git a/testng-core-api/src/main/java/org/testng/annotations/BeforeTest.java b/testng-core-api/src/main/java/org/testng/annotations/BeforeTest.java new file mode 100644 index 0000000..fee1b66 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/annotations/BeforeTest.java @@ -0,0 +1,80 @@ +package org.testng.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@Target(java.lang.annotation.ElementType.METHOD) +@Documented +public @interface BeforeTest { + /** + * Whether methods on this class/method are enabled. + * + * @return the value (default true) + */ + boolean enabled() default true; + + /** + * The list of groups this class/method belongs to. + * + * @return the value + */ + String[] groups() default {}; + + /** + * The list of groups this method depends on. Every method member of one of these groups is + * guaranteed to have been invoked before this method. Furthermore, if any of these methods was + * not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + * @return the value + */ + String[] dependsOnGroups() default {}; + + /** + * The list of methods this method depends on. There is no guarantee on the order on which the + * methods depended upon will be run, but you are guaranteed that all these methods will be run + * before the test method that contains this annotation is run. Furthermore, if any of these + * methods was not a SUCCESS, this test method will not be run and will be flagged as a SKIP. + * + *

If some of these methods have been overloaded, all the overloaded versions will be run. + * + * @return the value + */ + String[] dependsOnMethods() default {}; + + /** + * For before methods (beforeSuite, beforeTest, beforeTestClass and beforeTestMethod, but not + * beforeGroups): If set to true, this configuration method will be run regardless of what groups + * it belongs to.
+ * For after methods (afterSuite, afterClass, ...): If set to true, this configuration method will + * be run even if one or more methods invoked previously failed or was skipped. + * + * @return the value (default false) + */ + boolean alwaysRun() default false; + + /** + * If true, this @Configuration method will belong to groups specified in the @Test + * annotation on the class (if any). + * + * @return the value (default true) + */ + boolean inheritGroups() default true; + + /** + * The description for this method. The string used will appear in the HTML report and also on + * standard output if verbose > 2. + * + * @return the value (default empty) + */ + String description() default ""; + + /** + * The maximum number of milliseconds this method should take. If it hasn't returned after this + * time, this method will fail and it will cause test methods depending on it to be skipped. + * + * @return the value (default 0) + */ + long timeOut() default 0; +} diff --git a/testng-core-api/src/main/java/org/testng/annotations/CustomAttribute.java b/testng-core-api/src/main/java/org/testng/annotations/CustomAttribute.java new file mode 100644 index 0000000..4cf1037 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/annotations/CustomAttribute.java @@ -0,0 +1,19 @@ +package org.testng.annotations; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** Represents a means to add in custom attributes to @{@link Test} annotated tests. */ +@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@Target({METHOD, TYPE}) +public @interface CustomAttribute { + + /** @return - The name for the custom attribute */ + String name(); + + /** @return - The custom attribute values as an array. */ + String[] values() default {}; +} diff --git a/testng-core-api/src/main/java/org/testng/annotations/DataProvider.java b/testng-core-api/src/main/java/org/testng/annotations/DataProvider.java new file mode 100644 index 0000000..2d4dfa2 --- /dev/null +++ b/testng-core-api/src/main/java/org/testng/annotations/DataProvider.java @@ -0,0 +1,51 @@ +package org.testng.annotations; + +import static java.lang.annotation.ElementType.METHOD; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Mark a method as supplying data for a test method. + * + *

The {@link #name() name} defaults to the name of the annotated method. + * + *

The annotated method must return any of the following: + * + *