diff --git a/java/ql/integration-tests/java/query-suite/java-code-quality-extended.qls.expected b/java/ql/integration-tests/java/query-suite/java-code-quality-extended.qls.expected index 7a1a986b2aa1..5de435a0e741 100644 --- a/java/ql/integration-tests/java/query-suite/java-code-quality-extended.qls.expected +++ b/java/ql/integration-tests/java/query-suite/java-code-quality-extended.qls.expected @@ -37,6 +37,7 @@ ql/java/ql/src/Likely Bugs/Concurrency/ScheduledThreadPoolExecutorZeroThread.ql ql/java/ql/src/Likely Bugs/Concurrency/SynchOnBoxedType.ql ql/java/ql/src/Likely Bugs/Concurrency/SynchSetUnsynchGet.ql ql/java/ql/src/Likely Bugs/Frameworks/JUnit/JUnit5MissingNestedAnnotation.ql +ql/java/ql/src/Likely Bugs/Frameworks/JUnit/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig.ql ql/java/ql/src/Likely Bugs/Inheritance/NoNonFinalInConstructor.ql ql/java/ql/src/Likely Bugs/Likely Typos/ContainerSizeCmpZero.ql ql/java/ql/src/Likely Bugs/Likely Typos/ContradictoryTypeChecks.ql diff --git a/java/ql/integration-tests/java/query-suite/java-code-quality.qls.expected b/java/ql/integration-tests/java/query-suite/java-code-quality.qls.expected index 17253dbe0f89..791f64cd6728 100644 --- a/java/ql/integration-tests/java/query-suite/java-code-quality.qls.expected +++ b/java/ql/integration-tests/java/query-suite/java-code-quality.qls.expected @@ -35,6 +35,7 @@ ql/java/ql/src/Likely Bugs/Concurrency/ScheduledThreadPoolExecutorZeroThread.ql ql/java/ql/src/Likely Bugs/Concurrency/SynchOnBoxedType.ql ql/java/ql/src/Likely Bugs/Concurrency/SynchSetUnsynchGet.ql ql/java/ql/src/Likely Bugs/Frameworks/JUnit/JUnit5MissingNestedAnnotation.ql +ql/java/ql/src/Likely Bugs/Frameworks/JUnit/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig.ql ql/java/ql/src/Likely Bugs/Inheritance/NoNonFinalInConstructor.ql ql/java/ql/src/Likely Bugs/Likely Typos/ContainerSizeCmpZero.ql ql/java/ql/src/Likely Bugs/Likely Typos/ContradictoryTypeChecks.ql diff --git a/java/ql/src/Likely Bugs/Frameworks/JUnit/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig.md b/java/ql/src/Likely Bugs/Frameworks/JUnit/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig.md new file mode 100644 index 000000000000..b0f2f8d1aa7c --- /dev/null +++ b/java/ql/src/Likely Bugs/Frameworks/JUnit/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig.md @@ -0,0 +1,52 @@ +## Overview + +Mocking methods of a class is necessary for unit tests to run without overhead caused by expensive I/O operations. However, when a unit test ends up mocking all non-private methods of a class, it may indicate that the test is too complicated, possibly because it is trying to test multiple things at once. Such extensive mocking is likely a signal that the scope of the unit test is reaching beyond a single unit of functionality. + +## Recommendation + +It is best to contain the scope of a single unit test to a single unit of functionality. For example, a unit test may aim to test a series of data-transforming functions that depend on an ORM class. Even though the functions may be semantically related with one another, it is better to create a unit test for each function. + +## Example + +The following example mocks all methods of an ORM class named `EmployeeRecord`, and tests four functions against them. Since the scope of the unit test harbors all four of them, all of the methods provided by the class are mocked. + +```java +public class EmployeeRecord { + public int add(Employee employee) { ... } + + public Employee get(String name) { ... } + + public int update(Employee employee, String newName) { ... } + + public int delete(Employee employee) { ... } +} + +public class TestORM { + @Test + public void nonCompliant() { + Employee sampleEmployee = new Employee("John Doe"); + EmployeeRecord employeeRecordMock = mock(EmployeeRecord.class); // NON_COMPLIANT: Mocked class has all of its public methods used in the test + when(employeeRecordMock.add(sampleEmployee)).thenReturn(0); // Mocked EmployeeRecord.add + when(employeeRecordMock.get("John Doe")).thenReturn(sampleEmployee); // Mocked EmployeeRecord.get + when(employeeRecordMock.update(sampleEmployee, "Jane Doe")).thenReturn(0); // Mocked EmployeeRecord.update + when(employeeRecordMock.delete(sampleEmployee)).thenReturn(0); // Mocked EmployeeRecord.delete + } + + @Test + public void compliant() { + Employee sampleEmployee = new Employee("John Doe"); + EmployeeRecord employeeRecordMock = mock(EmployeeRecord.class); // COMPLIANT: Only some of the public methods belonging to the mocked object are used + when(employeeRecordMock.add(sampleEmployee)).thenReturn(0); // Mocked EmployeeRecord.add + when(employeeRecordMock.update(sampleEmployee, "Jane Doe")).thenReturn(0); // Mocked EmployeeRecord.update + } + +} +``` + +## Implementation Notes + +JUnit provides two different ways of mocking a method call: `when(mockedObject.methodToMock(...)).thenReturn(...)` and `doReturn(...).when(mockedObject).methodToMock(...)`. Both forms are taken into account by the query. + +## References + +- Baeldung: [Best Practices for Unit Testing in Java](https://www.baeldung.com/java-unit-testing-best-practices). diff --git a/java/ql/src/Likely Bugs/Frameworks/JUnit/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig.ql b/java/ql/src/Likely Bugs/Frameworks/JUnit/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig.ql new file mode 100644 index 000000000000..f7a75aa94fe5 --- /dev/null +++ b/java/ql/src/Likely Bugs/Frameworks/JUnit/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig.ql @@ -0,0 +1,79 @@ +/** + * @id java/mocking-all-non-private-methods-means-unit-test-is-too-big + * @name Mocking all non-private methods of a class may indicate the unit test is testing too much + * @description Mocking all non-private methods provided by a class might indicate the unit test + * aims to test too many things. + * @kind problem + * @precision high + * @problem.severity recommendation + * @tags quality + * maintainability + * readability + */ + +import java + +/** + * A call to Mockito's `mock` method. + */ +class MockitoMockCall extends MethodCall { + MockitoMockCall() { this.getMethod().hasQualifiedName("org.mockito", "Mockito", "mock") } + + /** + * Gets the type that this call intends to mock. For example: + * ```java + * EmployeeRecord employeeRecordMock = mock(EmployeeRecord.class); + * ``` + * This predicate gets the class `EmployeeRecord` in the above example. + */ + Type getMockedType() { result = this.getAnArgument().(TypeLiteral).getReferencedType() } +} + +/** + * A method call that mocks a target method in a JUnit test. For example: + * ```java + * EmployeeRecord employeeRecordMock = mock(EmployeeRecord.class); + * when(employeeRecordMock.add(sampleEmployee)).thenReturn(0); // Mocked EmployeeRecord.add + * doReturn(0).when(employeeRecordMock).add(sampleEmployee); // Mocked EmployeeRecord.add + * ``` + * This class captures the call to `add` which mocks the equivalent method of the class `EmployeeRecord`. + */ +class MockitoMockingMethodCall extends MethodCall { + MockitoMockCall mockCall; + + MockitoMockingMethodCall() { + /* 1. The qualifier originates from the mock call. */ + this.getQualifier().getControlFlowNode().getAPredecessor+() = mockCall.getControlFlowNode() and + /* 2. The mocked method can be found in the class being mocked with the mock call. */ + mockCall.getMockedType().(ClassOrInterface).getAMethod() = this.getMethod() + } + + /** + * Gets the call to Mockito's `mock` from which the qualifier, the mocked object, originates. + */ + MockitoMockCall getMockitoMockCall() { result = mockCall } +} + +/* + * The following from-which-select embodies this pseudocode: + * - Find a JUnit4TestMethod which: + * - for a class that it mocks with a call to `mock`, + * - for all methods that the class has, there is a method that this test method mocks. + */ + +from JUnit4TestMethod testMethod, ClassOrInterface mockedClassOrInterface +where + exists(MockitoMockCall mockCall | + mockCall.getParent+() = testMethod.getBody().getAStmt() and + mockedClassOrInterface = mockCall.getMockedType() and + // Only flag classes with multiple public methods (2 or more) + count(Method m | m = mockedClassOrInterface.getAMethod() and m.isPublic()) > 1 and + forex(Method method | method = mockedClassOrInterface.getAMethod() and method.isPublic() | + exists(MockitoMockingMethodCall mockedMethod | + mockedMethod.getMockitoMockCall() = mockCall and + mockedMethod.getMethod() = method + ) + ) + ) +select testMethod, "This test method mocks all public methods of a $@.", mockedClassOrInterface, + "class or an interface" diff --git a/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/Employee.java b/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/Employee.java new file mode 100644 index 000000000000..70a0091ec370 --- /dev/null +++ b/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/Employee.java @@ -0,0 +1,10 @@ +/** + * Underlying data type of the ORM class and functions. + */ +public class Employee { + Employee(String name) { + this.name = name; + } + + String name; +} diff --git a/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/EmployeeRecord.java b/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/EmployeeRecord.java new file mode 100644 index 000000000000..4aa40d97ec37 --- /dev/null +++ b/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/EmployeeRecord.java @@ -0,0 +1,26 @@ +/** + * Sample ORM class for the type `Employee`. + */ +public class EmployeeRecord { + public int add(Employee employee) { + return 1; + } + + public Employee get(String name) { + return new Employee("Sample"); + } + + public int update(Employee employee, String newName) { + return 1; + } + + public int delete(Employee employee) { + return 1; + } + + private void f() { } + + private void g() { } + + private void h() { } +} diff --git a/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/EmployeeStatus.java b/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/EmployeeStatus.java new file mode 100644 index 000000000000..3b581bb39ef8 --- /dev/null +++ b/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/EmployeeStatus.java @@ -0,0 +1,9 @@ +/** + * Simple class with a single public method to test the edge case. + * When this single method is mocked, it means ALL public methods are mocked. + */ +public class EmployeeStatus { + public String getStatus() { + return "active"; + } +} diff --git a/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig.expected b/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig.expected new file mode 100644 index 000000000000..d3e95329380f --- /dev/null +++ b/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig.expected @@ -0,0 +1,2 @@ +| TestORM.java:34:15:34:27 | nonCompliant1 | This test method mocks all public methods of a $@. | EmployeeRecord.java:4:14:4:27 | EmployeeRecord | class or an interface | +| TestORM.java:47:15:47:27 | nonCompliant2 | This test method mocks all public methods of a $@. | EmployeeRecord.java:4:14:4:27 | EmployeeRecord | class or an interface | diff --git a/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig.qlref b/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig.qlref new file mode 100644 index 000000000000..6d22c90940b1 --- /dev/null +++ b/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig.qlref @@ -0,0 +1,2 @@ +query: Likely Bugs/Frameworks/JUnit/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/TestORM.java b/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/TestORM.java new file mode 100644 index 000000000000..eadf01515067 --- /dev/null +++ b/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/TestORM.java @@ -0,0 +1,65 @@ +import org.junit.Test; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.doReturn; + +public class TestORM { + /** + * Test of form `when(mockedObject.methodToBeMocked()).thenReturn(someVal)`. + */ + @Test + public void compliant1() { + Employee sampleEmployee = new Employee("John Doe"); + EmployeeRecord employeeRecordMock = mock(EmployeeRecord.class); // COMPLIANT: Only some of the public methods belonging to the mocked object are used + when(employeeRecordMock.add(sampleEmployee)).thenReturn(0); // Mocked EmployeeRecord.add + when(employeeRecordMock.update(sampleEmployee, "Jane Doe")).thenReturn(0); // Mocked EmployeeRecord.update + } + + /** + * Test of form `doReturn(someVal).when(mockedObject).methodToBeMocked()`. + */ + @Test + public void compliant2() { + Employee sampleEmployee = new Employee("John Doe"); + EmployeeRecord employeeRecordMock = mock(EmployeeRecord.class); // COMPLIANT: Only some of the public methods belonging to the mocked object are used + doReturn(0).when(employeeRecordMock).add(sampleEmployee); // Mocked EmployeeRecord.add + doReturn(0).when(employeeRecordMock).get("John Doe"); // Mocked EmployeeRecord.get + doReturn(0).when(employeeRecordMock).delete(sampleEmployee); // Mocked EmployeeRecord.delete + } + + /** + * Test of form `when(mockedObject.methodToBeMocked()).thenReturn(someVal)`. + */ + @Test + public void nonCompliant1() { // $ Alert + Employee sampleEmployee = new Employee("John Doe"); + EmployeeRecord employeeRecordMock = mock(EmployeeRecord.class); // NON_COMPLIANT: All public methods of the mocked object are used + when(employeeRecordMock.add(sampleEmployee)).thenReturn(0); // Mocked EmployeeRecord.add + when(employeeRecordMock.get("John Doe")).thenReturn(sampleEmployee); // Mocked EmployeeRecord.get + when(employeeRecordMock.update(sampleEmployee, "Jane Doe")).thenReturn(0); // Mocked EmployeeRecord.update + when(employeeRecordMock.delete(sampleEmployee)).thenReturn(0); // Mocked EmployeeRecord.delete + } + + /** + * Test of form `doReturn(someVal).when(mockedObject).methodToBeMocked()`. + */ + @Test + public void nonCompliant2() { // $ Alert + Employee sampleEmployee = new Employee("John Doe"); + EmployeeRecord employeeRecordMock = mock(EmployeeRecord.class); // NON_COMPLIANT: All public methods of the mocked object are used + doReturn(0).when(employeeRecordMock).add(sampleEmployee); // Mocked EmployeeRecord.add + doReturn(0).when(employeeRecordMock).get("John Doe"); // Mocked EmployeeRecord.get + doReturn(0).when(employeeRecordMock).update(sampleEmployee, "Jane Doe"); // Mocked EmployeeRecord.update + doReturn(0).when(employeeRecordMock).delete(sampleEmployee); // Mocked EmployeeRecord.delete + } + + /** + * Edge case: Class with single public method - should NOT be flagged. + * When there's only one public method, mocking it doesn't indicate a "too big" test. + */ + @Test + public void compliantSingleMethod() { + EmployeeStatus statusMock = mock(EmployeeStatus.class); // COMPLIANT: Single public method, no choice but to mock it if needed + when(statusMock.getStatus()).thenReturn("inactive"); // Mocked EmployeeStatus.getStatus (the only public method, but that's OK) + } +} diff --git a/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/options b/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/options new file mode 100644 index 000000000000..8dbd14d41c15 --- /dev/null +++ b/java/ql/test/query-tests/MockingAllNonPrivateMethodsMeansUnitTestIsTooBig/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -cp ${testdir}/../../stubs/junit-4.13:${testdir}/../../stubs/mockito-5.14 diff --git a/java/ql/test/stubs/junit-4.13/LICENSE-junit.txt b/java/ql/test/stubs/junit-4.13/LICENSE-junit.txt new file mode 100644 index 000000000000..fb686291a055 --- /dev/null +++ b/java/ql/test/stubs/junit-4.13/LICENSE-junit.txt @@ -0,0 +1,214 @@ +JUnit + +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC +LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM +CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + + a) in the case of the initial Contributor, the initial code and + documentation distributed under this Agreement, and + b) in the case of each subsequent Contributor: + + i) changes to the Program, and + + ii) additions to the Program; + + where such changes and/or additions to the Program originate from and are +distributed by that particular Contributor. A Contribution 'originates' from a +Contributor if it was added to the Program by such Contributor itself or anyone +acting on such Contributor's behalf. Contributions do not include additions to +the Program which: (i) are separate modules of software distributed in +conjunction with the Program under their own license agreement, and (ii) are +not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents " mean patent claims licensable by a Contributor which are +necessarily infringed by the use or sale of its Contribution alone or when +combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, +including all Contributors. + +2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor hereby grants +Recipient a non-exclusive, worldwide, royalty-free copyright license to +reproduce, prepare derivative works of, publicly display, publicly perform, +distribute and sublicense the Contribution of such Contributor, if any, and +such derivative works, in source code and object code form. + + b) Subject to the terms of this Agreement, each Contributor hereby grants +Recipient a non-exclusive, worldwide, royalty-free patent license under +Licensed Patents to make, use, sell, offer to sell, import and otherwise +transfer the Contribution of such Contributor, if any, in source code and +object code form. This patent license shall apply to the combination of the +Contribution and the Program if, at the time the Contribution is added by the +Contributor, such addition of the Contribution causes such combination to be +covered by the Licensed Patents. The patent license shall not apply to any +other combinations which include the Contribution. No hardware per se is +licensed hereunder. + + c) Recipient understands that although each Contributor grants the +licenses to its Contributions set forth herein, no assurances are provided by +any Contributor that the Program does not infringe the patent or other +intellectual property rights of any other entity. Each Contributor disclaims +any liability to Recipient for claims brought by any other entity based on +infringement of intellectual property rights or otherwise. As a condition to +exercising the rights and licenses granted hereunder, each Recipient hereby +assumes sole responsibility to secure any other intellectual property rights +needed, if any. For example, if a third party patent license is required to +allow Recipient to distribute the Program, it is Recipient's responsibility to +acquire that license before distributing the Program. + + d) Each Contributor represents that to its knowledge it has sufficient +copyright rights in its Contribution, if any, to grant the copyright license +set forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under +its own license agreement, provided that: + + a) it complies with the terms and conditions of this Agreement; and + + b) its license agreement: + + i) effectively disclaims on behalf of all Contributors all warranties and +conditions, express and implied, including warranties or conditions of title +and non-infringement, and implied warranties or conditions of merchantability +and fitness for a particular purpose; + + ii) effectively excludes on behalf of all Contributors all liability for +damages, including direct, indirect, special, incidental and consequential +damages, such as lost profits; + + iii) states that any provisions which differ from this Agreement are +offered by that Contributor alone and not by any other party; and + + iv) states that source code for the Program is available from such +Contributor, and informs licensees how to obtain it in a reasonable manner on +or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + + a) it must be made available under this Agreement; and + + b) a copy of this Agreement must be included with each copy of the +Program. + +Contributors may not remove or alter any copyright notices contained within the +Program. + +Each Contributor must identify itself as the originator of its Contribution, if +any, in a manner that reasonably allows subsequent Recipients to identify the +originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with +respect to end users, business partners and the like. While this license is +intended to facilitate the commercial use of the Program, the Contributor who +includes the Program in a commercial product offering should do so in a manner +which does not create potential liability for other Contributors. Therefore, if +a Contributor includes the Program in a commercial product offering, such +Contributor ("Commercial Contributor") hereby agrees to defend and indemnify +every other Contributor ("Indemnified Contributor") against any losses, damages +and costs (collectively "Losses") arising from claims, lawsuits and other legal +actions brought by a third party against the Indemnified Contributor to the +extent caused by the acts or omissions of such Commercial Contributor in +connection with its distribution of the Program in a commercial product +offering. The obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In order +to qualify, an Indemnified Contributor must: a) promptly notify the Commercial +Contributor in writing of such claim, and b) allow the Commercial Contributor +to control, and cooperate with the Commercial Contributor in, the defense and +any related settlement negotiations. The Indemnified Contributor may +participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product +offering, Product X. That Contributor is then a Commercial Contributor. If that +Commercial Contributor then makes performance claims, or offers warranties +related to Product X, those performance claims and warranties are such +Commercial Contributor's responsibility alone. Under this section, the +Commercial Contributor would have to defend claims against the other +Contributors related to those performance claims and warranties, and if a court +requires any other Contributor to pay any damages as a result, the Commercial +Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED 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. Each +Recipient is solely responsible for determining the appropriateness of using +and distributing the Program and assumes all risks associated with its exercise +of rights under this Agreement, including but not limited to the risks and +costs of program errors, compliance with applicable laws, damage to or loss of +data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY +CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST +PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY +WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS +GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable +law, it shall not affect the validity or enforceability of the remainder of the +terms of this Agreement, and without further action by the parties hereto, such +provision shall be reformed to the minimum extent necessary to make such +provision valid and enforceable. + +If Recipient institutes patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging that the +Program itself (excluding combinations of the Program with other software or +hardware) infringes such Recipient's patent(s), then such Recipient's rights +granted under Section 2(b) shall terminate as of the date such litigation is +filed. + +All Recipient's rights under this Agreement shall terminate if it fails to +comply with any of the material terms or conditions of this Agreement and does +not cure such failure in a reasonable period of time after becoming aware of +such noncompliance. If all Recipient's rights under this Agreement terminate, +Recipient agrees to cease use and distribution of the Program as soon as +reasonably practicable. However, Recipient's obligations under this Agreement +and any licenses granted by Recipient relating to the Program shall continue +and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in +order to avoid inconsistency the Agreement is copyrighted and may only be +modified in the following manner. The Agreement Steward reserves the right to +publish new versions (including revisions) of this Agreement from time to time. +No one other than the Agreement Steward has the right to modify this Agreement. +The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to +serve as the Agreement Steward to a suitable separate entity. Each new version +of the Agreement will be given a distinguishing version number. The Program +(including Contributions) may always be distributed subject to the version of +the Agreement under which it was received. In addition, after a new version of +the Agreement is published, Contributor may elect to distribute the Program +(including its Contributions) under the new version. Except as expressly stated +in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to +the intellectual property of any Contributor under this Agreement, whether +expressly, by implication, estoppel or otherwise. All rights in the Program not +expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the +intellectual property laws of the United States of America. No party to this +Agreement will bring a legal action under this Agreement more than one year +after the cause of action arose. Each party waives its rights to a jury trial +in any resulting litigation. + diff --git a/java/ql/test/stubs/junit-4.13/org/junit/Assert.java b/java/ql/test/stubs/junit-4.13/org/junit/Assert.java new file mode 100644 index 000000000000..cbd86acdb81c --- /dev/null +++ b/java/ql/test/stubs/junit-4.13/org/junit/Assert.java @@ -0,0 +1,472 @@ +package org.junit; + +import org.junit.function.ThrowingRunnable; + +//BSD License +// +//Copyright (c) 2000-2006, www.hamcrest.org +//All rights reserved. +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions are met: +// +//Redistributions of source code must retain the above copyright notice, this list of +//conditions and the following disclaimer. Redistributions in binary form must reproduce +//the above copyright notice, this list of conditions and the following disclaimer in +//the documentation and/or other materials provided with the distribution. +// +//Neither the name of Hamcrest nor the names of its contributors may be used to endorse +//or promote products derived from this software without specific prior written +//permission. +// +//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +//EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +//OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +//SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +//TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +//BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +//CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY +//WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +//DAMAGE. + +/* + * MODIFIED version of JUnit 4.13 as available at + * https://search.maven.org/remotecontent?filepath=junit/junit/4.13/junit-4.13-sources.jar + * Only parts of this file have been retained for test purposes. + */ + +public class Assert { + /** + * Asserts that a condition is true. If it isn't it throws an + * {@link AssertionError} with the given message. + * + * @param message the identifying message for the {@link AssertionError} + * (null + * okay) + * @param condition condition to be checked + */ + static public void assertTrue(String message, boolean condition) { + return; + } + + /** + * Asserts that a condition is true. If it isn't it throws an + * {@link AssertionError} without a message. + * + * @param condition condition to be checked + */ + static public void assertTrue(boolean condition) { + return; + } + + /** + * Asserts that a condition is false. If it isn't it throws an + * {@link AssertionError} with the given message. + * + * @param message the identifying message for the {@link AssertionError} + * (null + * okay) + * @param condition condition to be checked + */ + static public void assertFalse(String message, boolean condition) { + return; + } + + /** + * Asserts that a condition is false. If it isn't it throws an + * {@link AssertionError} without a message. + * + * @param condition condition to be checked + */ + static public void assertFalse(boolean condition) { + return; + } + + /** + * Fails a test with the given message. + * + * @param message the identifying message for the {@link AssertionError} + * (null + * okay) + * @see AssertionError + */ + static public void fail(String message) { + if (message == null) { + throw new AssertionError(); + } + throw new AssertionError(message); + } + + /** + * Asserts that an object isn't null. If it is an {@link AssertionError} is + * thrown with the given message. + * + * @param message the identifying message for the {@link AssertionError} + * (null + * okay) + * @param object Object to check or null + */ + static public void assertNotNull(String message, Object object) { + return; + } + + /** + * Asserts that an object isn't null. If it is an {@link AssertionError} is + * thrown. + * + * @param object Object to check or null + */ + static public void assertNotNull(Object object) { + return; + } + + /** + * Asserts that an object is null. If it is not, an {@link AssertionError} + * is thrown with the given message. + * + * @param message the identifying message for the {@link AssertionError} + * (null + * okay) + * @param object Object to check or null + */ + static public void assertNull(String message, Object object) { + return; + } + + /** + * Asserts that an object is null. If it isn't an {@link AssertionError} is + * thrown. + * + * @param object Object to check or null + */ + static public void assertNull(Object object) { + return; + } + + private static boolean equalsRegardingNull(Object expected, Object actual) { + if (expected == null) { + return actual == null; + } + + return isEquals(expected, actual); + } + + private static boolean isEquals(Object expected, Object actual) { + return expected.equals(actual); + } + + /** + * Asserts that two doubles are equal to within a positive delta. + * If they are not, an {@link AssertionError} is thrown with the given + * message. If the expected value is infinity then the delta value is + * ignored. NaNs are considered equal: + * assertEquals(Double.NaN, Double.NaN, *) passes + * + * @param message the identifying message for the {@link AssertionError} + * (null + * okay) + * @param expected expected value + * @param actual the value to check against expected + * @param delta the maximum delta between expected and + * actual for which both numbers are still + * considered equal. + */ + public static void assertEquals(String message, double expected, + double actual, double delta) { + return; + } + + private static void failNotEquals(String message, Object expected, + Object actual) { + fail(format(message, expected, actual)); + } + + static String format(String message, Object expected, Object actual) { + String formatted = ""; + if (message != null && !"".equals(message)) { + formatted = message + " "; + } + String expectedString = String.valueOf(expected); + String actualString = String.valueOf(actual); + if (equalsRegardingNull(expectedString, actualString)) { + return formatted + "expected: " + + formatClassAndValue(expected, expectedString) + + " but was: " + formatClassAndValue(actual, actualString); + } else { + return formatted + "expected:<" + expectedString + "> but was:<" + + actualString + ">"; + } + } + + private static String formatClass(Class value) { + String className = value.getCanonicalName(); + return className == null ? value.getName() : className; + } + + private static String formatClassAndValue(Object value, String valueString) { + String className = value == null ? "null" : value.getClass().getName(); + return className + "<" + valueString + ">"; + } + + /** + * Asserts that two floats are equal to within a positive delta. + * If they are not, an {@link AssertionError} is thrown with the given + * message. If the expected value is infinity then the delta value is + * ignored. NaNs are considered equal: + * assertEquals(Float.NaN, Float.NaN, *) passes + * + * @param message the identifying message for the {@link AssertionError} + * (null + * okay) + * @param expected expected value + * @param actual the value to check against expected + * @param delta the maximum delta between expected and + * actual for which both numbers are still + * considered equal. + */ + public static void assertEquals(String message, float expected, float actual, + float delta) { + if (floatIsDifferent(expected, actual, delta)) { + failNotEquals(message, Float.valueOf(expected), Float.valueOf(actual)); + } + } + + private static boolean doubleIsDifferent(double d1, double d2, double delta) { + if (Double.compare(d1, d2) == 0) { + return false; + } + if ((Math.abs(d1 - d2) <= delta)) { + return false; + } + + return true; + } + + private static boolean floatIsDifferent(float f1, float f2, float delta) { + if (Float.compare(f1, f2) == 0) { + return false; + } + if ((Math.abs(f1 - f2) <= delta)) { + return false; + } + + return true; + } + + /** + * Asserts that two longs are equal. If they are not, an + * {@link AssertionError} is thrown. + * + * @param expected expected long value. + * @param actual actual long value + */ + public static void assertEquals(long expected, long actual) { + assertEquals(null, expected, actual); + } + + /** + * Asserts that two longs are equal. If they are not, an + * {@link AssertionError} is thrown with the given message. + * + * @param message the identifying message for the {@link AssertionError} + * (null + * okay) + * @param expected long expected value. + * @param actual long actual value + */ + public static void assertEquals(String message, long expected, long actual) { + if (expected != actual) { + failNotEquals(message, Long.valueOf(expected), Long.valueOf(actual)); + } + } + + /** + * @deprecated Use + * assertEquals(double expected, double actual, double + * delta) instead + */ + @Deprecated + public static void assertEquals(double expected, double actual) { + assertEquals(null, expected, actual); + } + + /** + * @deprecated Use + * assertEquals(String message, double expected, double + * actual, double delta) instead + */ + @Deprecated + public static void assertEquals(String message, double expected, + double actual) { + fail("Use assertEquals(expected, actual, delta) to compare " + + "floating-point numbers"); + } + + /** + * Asserts that two doubles are equal to within a positive delta. + * If they are not, an {@link AssertionError} is thrown. If the expected + * value is infinity then the delta value is ignored.NaNs are considered + * equal: assertEquals(Double.NaN, Double.NaN, *) passes + * + * @param expected expected value + * @param actual the value to check against expected + * @param delta the maximum delta between expected and + * actual for which both numbers are still + * considered equal. + */ + public static void assertEquals(double expected, double actual, + double delta) { + assertEquals(null, expected, actual, delta); + } + + /** + * Asserts that two floats are equal to within a positive delta. + * If they are not, an {@link AssertionError} is thrown. If the expected + * value is infinity then the delta value is ignored. NaNs are considered + * equal: assertEquals(Float.NaN, Float.NaN, *) passes + * + * @param expected expected value + * @param actual the value to check against expected + * @param delta the maximum delta between expected and + * actual for which both numbers are still + * considered equal. + */ + public static void assertEquals(float expected, float actual, float delta) { + assertEquals(null, expected, actual, delta); + } + + /** + * Asserts that two objects are equal. If they are not, an + * {@link AssertionError} without a message is thrown. If + * expected and actual are null, + * they are considered equal. + * + * @param expected expected value + * @param actual the value to check against expected + */ + public static void assertEquals(Object expected, Object actual) { + assertEquals(null, expected, actual); + } + + public static void assertEquals(String message, Object expected, + Object actual) { + } + + public static void assertNotEquals(String message, Object unexpected, Object actual) { + return; + } + + public static void assertNotEquals(Object unexpected, Object actual) { + assertNotEquals(null, unexpected, actual); + } + + public static void assertNotEquals(String message, long unexpected, long actual) { + return; + } + + public static void assertNotEquals(long unexpected, long actual) { + assertNotEquals(null, unexpected, actual); + } + + public static void assertNotEquals(String message, double unexpected, double actual, double delta) { + return; + } + + public static void assertNotEquals(double unexpected, double actual, double delta) { + assertNotEquals(null, unexpected, actual, delta); + } + + public static void assertNotEquals(String message, float unexpected, float actual, float delta) { + return; + } + + public static void assertNotEquals(float unexpected, float actual, float delta) { + assertNotEquals(null, unexpected, actual, delta); + } + + public static void assertNotSame(String message, Object unexpected, Object actual) { + return; + } + + public static void assertNotSame(Object unexpected, Object actual) { + assertNotSame(null, unexpected, actual); + } + + public static void assertSame(String message, Object expected, Object actual) { + return; + } + + public static void assertSame(Object expected, Object actual) { + assertSame(null, expected, actual); + } + + /** + * Asserts that {@code runnable} throws an exception of type {@code expectedThrowable} when + * executed. If it does, the exception object is returned. 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 expectedThrowable the expected type of the exception + * @param runnable a function that is expected to throw an exception when executed + * @return the exception thrown by {@code runnable} + * @since 4.13 + */ + public static T assertThrows(Class expectedThrowable, + ThrowingRunnable runnable) { + return assertThrows(null, expectedThrowable, runnable); + } + + /** + * Asserts that {@code runnable} throws an exception of type {@code expectedThrowable} when + * executed. If it does, the exception object is returned. 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 message the identifying message for the {@link AssertionError} (null + * okay) + * @param expectedThrowable the expected type of the exception + * @param runnable a function that is expected to throw an exception when executed + * @return the exception thrown by {@code runnable} + * @since 4.13 + */ + public static T assertThrows(String message, Class expectedThrowable, + ThrowingRunnable runnable) { + try { + runnable.run(); + } catch (Throwable actualThrown) { + if (expectedThrowable.isInstance(actualThrown)) { + @SuppressWarnings("unchecked") T retVal = (T) actualThrown; + return retVal; + } else { + String expected = formatClass(expectedThrowable); + Class actualThrowable = actualThrown.getClass(); + String actual = formatClass(actualThrowable); + if (expected.equals(actual)) { + // There must be multiple class loaders. Add the identity hash code so the message + // doesn't say "expected: java.lang.String ..." + expected += "@" + Integer.toHexString(System.identityHashCode(expectedThrowable)); + actual += "@" + Integer.toHexString(System.identityHashCode(actualThrowable)); + } + String mismatchMessage = buildPrefix(message) + + format("unexpected exception type thrown;", expected, actual); + + // The AssertionError(String, Throwable) ctor is only available on JDK7. + AssertionError assertionError = new AssertionError(mismatchMessage); + assertionError.initCause(actualThrown); + throw assertionError; + } + } + String notThrownMessage = buildPrefix(message) + String + .format("expected %s to be thrown, but nothing was thrown", + formatClass(expectedThrowable)); + throw new AssertionError(notThrownMessage); + } + + private static String buildPrefix(String message) { + return message != null && message.length() != 0 ? message + ": " : ""; + } + +} diff --git a/java/ql/test/stubs/junit-4.13/org/junit/Test.java b/java/ql/test/stubs/junit-4.13/org/junit/Test.java new file mode 100644 index 000000000000..8356b546d792 --- /dev/null +++ b/java/ql/test/stubs/junit-4.13/org/junit/Test.java @@ -0,0 +1,28 @@ +/* + * Copyright 2015-2018 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * http://www.eclipse.org/legal/epl-v20.html + */ + +/* + * MODIFIED version of junit-jupiter-api 5.2.0 as available at + * https://search.maven.org/classic/remotecontent?filepath=org/junit/jupiter/junit-jupiter-api/5.2.0/junit-jupiter-api-5.2.0-sources.jar + * Only parts of this file have been retained for test purposes. + */ + +package org.junit; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Test {} diff --git a/java/ql/test/stubs/junit-4.13/org/junit/function/ThrowingRunnable.java b/java/ql/test/stubs/junit-4.13/org/junit/function/ThrowingRunnable.java new file mode 100644 index 000000000000..d0eb782ccd30 --- /dev/null +++ b/java/ql/test/stubs/junit-4.13/org/junit/function/ThrowingRunnable.java @@ -0,0 +1,14 @@ +package org.junit.function; + +/** + * This interface facilitates the use of + * {@link org.junit.Assert#assertThrows(Class, ThrowingRunnable)} from Java 8. It allows method + * references to void methods (that declare checked exceptions) to be passed directly into + * {@code assertThrows} + * without wrapping. It is not meant to be implemented directly. + * + * @since 4.13 + */ +public interface ThrowingRunnable { + void run() throws Throwable; +} diff --git a/java/ql/test/stubs/mockito-5.14/org/mockito/ArgumentMatchers.java b/java/ql/test/stubs/mockito-5.14/org/mockito/ArgumentMatchers.java new file mode 100644 index 000000000000..84004de2ac3d --- /dev/null +++ b/java/ql/test/stubs/mockito-5.14/org/mockito/ArgumentMatchers.java @@ -0,0 +1,4 @@ +package org.mockito; + +public class ArgumentMatchers { +} diff --git a/java/ql/test/stubs/mockito-5.14/org/mockito/MockSettings.java b/java/ql/test/stubs/mockito-5.14/org/mockito/MockSettings.java new file mode 100644 index 000000000000..f6e46d610e71 --- /dev/null +++ b/java/ql/test/stubs/mockito-5.14/org/mockito/MockSettings.java @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito; + +import java.io.Serializable; + +public interface MockSettings extends Serializable { +} \ No newline at end of file diff --git a/java/ql/test/stubs/mockito-5.14/org/mockito/Mockito.java b/java/ql/test/stubs/mockito-5.14/org/mockito/Mockito.java new file mode 100644 index 000000000000..e4d5a06a247c --- /dev/null +++ b/java/ql/test/stubs/mockito-5.14/org/mockito/Mockito.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito; + +import org.mockito.ArgumentMatchers; +import org.mockito.MockSettings; +import org.mockito.internal.creation.MockSettingsImpl; +import org.mockito.stubbing.Answer; +import org.mockito.stubbing.OngoingStubbing; +import org.mockito.internal.MockitoCore; +import org.mockito.MockSettings; +import org.mockito.stubbing.Stubber; + +public class Mockito extends ArgumentMatchers { + static final MockitoCore MOCKITO_CORE = new MockitoCore(); + + public static MockSettings withSettings() { + return new MockSettings() { + }; + } + + /** + * Creates a mock object of the requested class or interface. + *

+ * See examples in javadoc for the {@link Mockito} class. + * + * @param reified don't pass any values to it. It's a trick to detect the + * class/interface you + * want to mock. + * @return the mock object. + * @since 4.10.0 + */ + @SafeVarargs + public static T mock(T... reified) { + return mock(withSettings(), reified); + } + + /** + * Creates a mock object of the requested class or interface with the given + * default answer. + *

+ * See examples in javadoc for the {@link Mockito} class. + * + * @param defaultAnswer the default answer to use. + * @param reified don't pass any values to it. It's a trick to detect the + * class/interface you + * want to mock. + * @return the mock object. + * @since 5.1.0 + */ + @SafeVarargs + public static T mock(@SuppressWarnings("rawtypes") Answer defaultAnswer, T... reified) { + return mock(new Answer() { + }, reified); + } + + /** + * Creates a mock object of the requested class or interface with the given + * name. + *

+ * See examples in javadoc for the {@link Mockito} class. + * + * @param name the mock name to use. + * @param reified don't pass any values to it. It's a trick to detect the + * class/interface you + * want to mock. + * @return the mock object. + * @since 5.1.0 + */ + @SafeVarargs + public static T mock(String name, T... reified) { + return mock(withSettings(), reified); + } + + /** + * Creates a mock object of the requested class or interface with the given + * settings. + *

+ * See examples in javadoc for the {@link Mockito} class. + * + * @param settings the mock settings to use. + * @param reified don't pass any values to it. It's a trick to detect the + * class/interface you + * want to mock. + * @return the mock object. + * @since 5.1.0 + */ + @SafeVarargs + public static T mock(MockSettings settings, T... reified) { + if (reified == null || reified.length > 0) { + throw new IllegalArgumentException( + "Please don't pass any values here. Java will detect class automagically."); + } + + return mock(getClassOf(reified), settings); + } + + /** + * Creates mock object of given class or interface. + *

+ * See examples in javadoc for {@link Mockito} class + * + * @param classToMock class or interface to mock + * @return mock object + */ + public static T mock(Class classToMock) { + return mock(classToMock, withSettings()); + } + + /** + * Specifies mock name. Naming mocks can be helpful for debugging - the name is + * used in all verification errors. + *

+ * Beware that naming mocks is not a solution for complex code which uses too + * many mocks or collaborators. + * If you have too many mocks then refactor the code so that it's easy to + * test/debug without necessity of naming mocks. + *

+ * If you use @Mock annotation then you've got naming mocks + * for free! @Mock uses field name as mock name. + * {@link Mock Read more.} + *

+ * + * See examples in javadoc for {@link Mockito} class + * + * @param classToMock class or interface to mock + * @param name of the mock + * @return mock object + */ + public static T mock(Class classToMock, String name) { + return mock(classToMock, new Answer() { + }); + } + + /** + * Creates mock with a specified strategy for its answers to interactions. + * It's quite an advanced feature and typically you don't need it to write + * decent tests. + * However it can be helpful when working with legacy systems. + *

+ * It is the default answer so it will be used only when you don't stub + * the method call. + * + *

+   * 
+   *   Foo mock = mock(Foo.class, RETURNS_SMART_NULLS);
+   *   Foo mockTwo = mock(Foo.class, new YourOwnAnswer());
+   * 
+   * 
+ * + *

+ * See examples in javadoc for {@link Mockito} class + *

+ * + * @param classToMock class or interface to mock + * @param defaultAnswer default answer for un-stubbed methods + * + * @return mock object + */ + public static T mock(Class classToMock, Answer defaultAnswer) { + return mock(classToMock, new Answer() { + }); + } + + /** + * Creates a mock with some non-standard settings. + *

+ * The number of configuration points for a mock will grow, + * so we need a fluent way to introduce new configuration without adding more + * and more overloaded Mockito.mock() methods. + * Hence {@link MockSettings}. + * + *

+   * 
+   *   Listener mock = mock(Listener.class, withSettings()
+   *     .name("firstListner").defaultBehavior(RETURNS_SMART_NULLS));
+   *   );
+   * 
+   * 
+ * + * Use it carefully and occasionally. What might be reason your test + * needs non-standard mocks? + * Is the code under test so complicated that it requires non-standard mocks? + * Wouldn't you prefer to refactor the code under test, so that it is testable + * in a simple way? + *

+ * See also {@link Mockito#withSettings()} + *

+ * See examples in javadoc for {@link Mockito} class + * + * @param classToMock class or interface to mock + * @param mockSettings additional mock settings + * @return mock object + */ + public static T mock(Class classToMock, MockSettings mockSettings) { + return MOCKITO_CORE.mock(classToMock, mockSettings); + } + + private static Class getClassOf(T[] array) { + return (Class) array.getClass().getComponentType(); + } + + public static OngoingStubbing when(T methodCall) { + return MOCKITO_CORE.when(methodCall); + } + + public static Stubber doReturn(Object toBeReturned) { + return null; + } + + public static Stubber doReturn(Object toBeReturned, Object... toBeReturnedNext) { + return null; + } +} diff --git a/java/ql/test/stubs/mockito-5.14/org/mockito/internal/MockitoCore.java b/java/ql/test/stubs/mockito-5.14/org/mockito/internal/MockitoCore.java new file mode 100644 index 000000000000..0ae8790a6d37 --- /dev/null +++ b/java/ql/test/stubs/mockito-5.14/org/mockito/internal/MockitoCore.java @@ -0,0 +1,28 @@ +package org.mockito.internal; + +import org.mockito.MockSettings; +import org.mockito.internal.creation.MockSettingsImpl; +import org.mockito.internal.progress.MockingProgress; +import org.mockito.stubbing.OngoingStubbing; +import org.mockito.mock.MockCreationSettings; +import static org.mockito.internal.util.MockUtil.createMock; + +public class MockitoCore { + public T mock(Class typeToMock, MockSettings settings) { + MockSettingsImpl impl = (MockSettingsImpl) settings; + MockCreationSettings creationSettings = impl.build(typeToMock); + T mock = createMock(creationSettings); + return mock; + } + + public OngoingStubbing when(T methodCall) { + MockingProgress mockingProgress = new MockingProgress() { + @Override + public OngoingStubbing pullOngoingStubbing() { + return null; + } + }; + OngoingStubbing stubbing = (OngoingStubbing) mockingProgress.pullOngoingStubbing(); + return stubbing; + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/mockito-5.14/org/mockito/internal/creation/MockSettingsImpl.java b/java/ql/test/stubs/mockito-5.14/org/mockito/internal/creation/MockSettingsImpl.java new file mode 100644 index 000000000000..c1e9083f4de8 --- /dev/null +++ b/java/ql/test/stubs/mockito-5.14/org/mockito/internal/creation/MockSettingsImpl.java @@ -0,0 +1,14 @@ +package org.mockito.internal.creation; + +import org.mockito.MockSettings; +import org.mockito.mock.MockCreationSettings; + +public class MockSettingsImpl implements MockSettings { + public MockCreationSettings build(Class typeToMock) { + return new MockCreationSettings() { + public String getMockMaker() { + return null; + } + }; + } +} diff --git a/java/ql/test/stubs/mockito-5.14/org/mockito/internal/handler/MockHandlerFactory.java b/java/ql/test/stubs/mockito-5.14/org/mockito/internal/handler/MockHandlerFactory.java new file mode 100644 index 000000000000..17dc3dcd618e --- /dev/null +++ b/java/ql/test/stubs/mockito-5.14/org/mockito/internal/handler/MockHandlerFactory.java @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.handler; + +import org.mockito.mock.MockCreationSettings; +import org.mockito.invocation.MockHandler; + +public final class MockHandlerFactory { + public static MockHandler createMockHandler(MockCreationSettings settings) { + return new MockHandlerImpl(); + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/mockito-5.14/org/mockito/internal/handler/MockHandlerImpl.java b/java/ql/test/stubs/mockito-5.14/org/mockito/internal/handler/MockHandlerImpl.java new file mode 100644 index 000000000000..dd1dfc68dc37 --- /dev/null +++ b/java/ql/test/stubs/mockito-5.14/org/mockito/internal/handler/MockHandlerImpl.java @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.handler; + +import org.mockito.invocation.MockHandler; + +public class MockHandlerImpl implements MockHandler { +} \ No newline at end of file diff --git a/java/ql/test/stubs/mockito-5.14/org/mockito/internal/progress/MockingProgress.java b/java/ql/test/stubs/mockito-5.14/org/mockito/internal/progress/MockingProgress.java new file mode 100644 index 000000000000..d52a9631b68b --- /dev/null +++ b/java/ql/test/stubs/mockito-5.14/org/mockito/internal/progress/MockingProgress.java @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.progress; + +import org.mockito.stubbing.OngoingStubbing; + +public interface MockingProgress { + OngoingStubbing pullOngoingStubbing(); +} \ No newline at end of file diff --git a/java/ql/test/stubs/mockito-5.14/org/mockito/internal/util/MockUtil.java b/java/ql/test/stubs/mockito-5.14/org/mockito/internal/util/MockUtil.java new file mode 100644 index 000000000000..8a975ffe1194 --- /dev/null +++ b/java/ql/test/stubs/mockito-5.14/org/mockito/internal/util/MockUtil.java @@ -0,0 +1,18 @@ +package org.mockito.internal.util; + +import org.mockito.mock.MockCreationSettings; +import org.mockito.plugins.MockMaker; +import org.mockito.invocation.MockHandler; +import static org.mockito.internal.handler.MockHandlerFactory.createMockHandler; + +public class MockUtil { + public static T createMock(MockCreationSettings settings) { + MockMaker mockMaker = getMockMaker(settings.getMockMaker()); + MockHandler mockHandler = createMockHandler(settings); + return mockMaker.createMock(settings, mockHandler); + } + + public static MockMaker getMockMaker(String mockMaker) { + return null; + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/mockito-5.14/org/mockito/invocation/MockHandler.java b/java/ql/test/stubs/mockito-5.14/org/mockito/invocation/MockHandler.java new file mode 100644 index 000000000000..d667c3759294 --- /dev/null +++ b/java/ql/test/stubs/mockito-5.14/org/mockito/invocation/MockHandler.java @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.invocation; + +import java.io.Serializable; + +public interface MockHandler extends Serializable { +} \ No newline at end of file diff --git a/java/ql/test/stubs/mockito-5.14/org/mockito/mock/MockCreationSettings.java b/java/ql/test/stubs/mockito-5.14/org/mockito/mock/MockCreationSettings.java new file mode 100644 index 000000000000..86788eda5f12 --- /dev/null +++ b/java/ql/test/stubs/mockito-5.14/org/mockito/mock/MockCreationSettings.java @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.mock; + +public interface MockCreationSettings { + String getMockMaker(); +} diff --git a/java/ql/test/stubs/mockito-5.14/org/mockito/plugins/MockMaker.java b/java/ql/test/stubs/mockito-5.14/org/mockito/plugins/MockMaker.java new file mode 100644 index 000000000000..d7b06ab96f5e --- /dev/null +++ b/java/ql/test/stubs/mockito-5.14/org/mockito/plugins/MockMaker.java @@ -0,0 +1,8 @@ +package org.mockito.plugins; + +import org.mockito.mock.MockCreationSettings; +import org.mockito.invocation.MockHandler; + +public interface MockMaker { + T createMock(MockCreationSettings settings, MockHandler handler); +} \ No newline at end of file diff --git a/java/ql/test/stubs/mockito-5.14/org/mockito/stubbing/Answer.java b/java/ql/test/stubs/mockito-5.14/org/mockito/stubbing/Answer.java new file mode 100644 index 000000000000..60828a66c522 --- /dev/null +++ b/java/ql/test/stubs/mockito-5.14/org/mockito/stubbing/Answer.java @@ -0,0 +1,7 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.stubbing; + +public interface Answer { } \ No newline at end of file diff --git a/java/ql/test/stubs/mockito-5.14/org/mockito/stubbing/OngoingStubbing.java b/java/ql/test/stubs/mockito-5.14/org/mockito/stubbing/OngoingStubbing.java new file mode 100644 index 000000000000..3fb5e659f045 --- /dev/null +++ b/java/ql/test/stubs/mockito-5.14/org/mockito/stubbing/OngoingStubbing.java @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.stubbing; + +public interface OngoingStubbing { + OngoingStubbing thenReturn(T value); +} \ No newline at end of file diff --git a/java/ql/test/stubs/mockito-5.14/org/mockito/stubbing/Stubber.java b/java/ql/test/stubs/mockito-5.14/org/mockito/stubbing/Stubber.java new file mode 100644 index 000000000000..eccca0c2c38c --- /dev/null +++ b/java/ql/test/stubs/mockito-5.14/org/mockito/stubbing/Stubber.java @@ -0,0 +1,5 @@ +package org.mockito.stubbing; + +public interface Stubber { + T when(T mock); +} \ No newline at end of file