@@ -6,24 +6,32 @@ import org.utbot.framework.codegen.ParametrizedTestSource
66import org.utbot.framework.codegen.RuntimeExceptionTestsBehaviour
77import org.utbot.framework.codegen.StaticsMocking
88import org.utbot.framework.codegen.TestFramework
9+ import org.utbot.framework.codegen.model.constructor.builtin.TestClassUtilMethodProvider
10+ import org.utbot.framework.codegen.model.constructor.builtin.UtilClassFileMethodProvider
911import org.utbot.framework.codegen.model.constructor.CgMethodTestSet
1012import org.utbot.framework.codegen.model.constructor.context.CgContext
13+ import org.utbot.framework.codegen.model.constructor.context.CgContextOwner
1114import org.utbot.framework.codegen.model.constructor.tree.CgTestClassConstructor
15+ import org.utbot.framework.codegen.model.constructor.tree.CgUtilClassConstructor
1216import org.utbot.framework.codegen.model.constructor.tree.TestsGenerationReport
13- import org.utbot.framework.codegen.model.tree.CgTestClassFile
17+ import org.utbot.framework.codegen.model.tree.AbstractCgClassFile
18+ import org.utbot.framework.codegen.model.tree.CgRegularClassFile
1419import org.utbot.framework.codegen.model.visitor.CgAbstractRenderer
1520import org.utbot.framework.plugin.api.ClassId
1621import org.utbot.framework.plugin.api.CodegenLanguage
1722import org.utbot.framework.plugin.api.ExecutableId
1823import org.utbot.framework.plugin.api.MockFramework
1924import org.utbot.framework.plugin.api.UtMethodTestSet
2025import org.utbot.framework.codegen.model.constructor.TestClassModel
26+ import org.utbot.framework.codegen.model.tree.CgComment
27+ import org.utbot.framework.codegen.model.tree.CgSingleLineComment
2128
2229class CodeGenerator (
2330 private val classUnderTest : ClassId ,
2431 paramNames : MutableMap <ExecutableId , List <String >> = mutableMapOf(),
32+ generateUtilClassFile : Boolean = false ,
2533 testFramework : TestFramework = TestFramework .defaultItem,
26- mockFramework : MockFramework ? = MockFramework .defaultItem,
34+ mockFramework : MockFramework = MockFramework .defaultItem,
2735 staticsMocking : StaticsMocking = StaticsMocking .defaultItem,
2836 forceStaticMocking : ForceStaticMocking = ForceStaticMocking .defaultItem,
2937 generateWarningsForStaticMocking : Boolean = true ,
@@ -36,9 +44,10 @@ class CodeGenerator(
3644) {
3745 private var context: CgContext = CgContext (
3846 classUnderTest = classUnderTest,
47+ generateUtilClassFile = generateUtilClassFile,
3948 paramNames = paramNames,
4049 testFramework = testFramework,
41- mockFramework = mockFramework ? : MockFramework . MOCKITO ,
50+ mockFramework = mockFramework,
4251 codegenLanguage = codegenLanguage,
4352 parametrizedTestSource = parameterizedTestSource,
4453 staticsMocking = staticsMocking,
@@ -58,19 +67,23 @@ class CodeGenerator(
5867 fun generateAsStringWithTestReport (
5968 testSets : Collection <UtMethodTestSet >,
6069 testClassCustomName : String? = null,
61- ): TestsCodeWithTestReport {
70+ ): CodeGeneratorResult {
6271 val cgTestSets = testSets.map { CgMethodTestSet (it) }.toList()
6372 return generateAsStringWithTestReport(cgTestSets, testClassCustomName)
6473 }
6574
6675 private fun generateAsStringWithTestReport (
6776 cgTestSets : List <CgMethodTestSet >,
6877 testClassCustomName : String? = null,
69- ): TestsCodeWithTestReport = withCustomContext(testClassCustomName) {
78+ ): CodeGeneratorResult = withCustomContext(testClassCustomName) {
7079 context.withTestClassFileScope {
7180 val testClassModel = TestClassModel .fromTestSets(classUnderTest, cgTestSets)
7281 val testClassFile = CgTestClassConstructor (context).construct(testClassModel)
73- TestsCodeWithTestReport (renderClassFile(testClassFile), testClassFile.testsGenerationReport)
82+ CodeGeneratorResult (
83+ generatedCode = renderClassFile(testClassFile),
84+ utilClassKind = UtilClassKind .fromCgContextOrNull(context),
85+ testsGenerationReport = testClassFile.testsGenerationReport
86+ )
7487 }
7588 }
7689
@@ -92,12 +105,153 @@ class CodeGenerator(
92105 }
93106 }
94107
95- private fun renderClassFile (file : CgTestClassFile ): String {
108+ private fun renderClassFile (file : AbstractCgClassFile < * > ): String {
96109 val renderer = CgAbstractRenderer .makeRenderer(context)
97110 file.accept(renderer)
98111 return renderer.toString()
99112 }
100113}
101114
102- data class TestsCodeWithTestReport (val generatedCode : String , val testsGenerationReport : TestsGenerationReport )
115+ /* *
116+ * @property generatedCode the source code of the test class
117+ * @property utilClassKind the kind of util class if it is required, otherwise - null
118+ * @property testsGenerationReport some info about test generation process
119+ */
120+ data class CodeGeneratorResult (
121+ val generatedCode : String ,
122+ // null if no util class needed, e.g. when we are generating utils directly into test class
123+ val utilClassKind : UtilClassKind ? ,
124+ val testsGenerationReport : TestsGenerationReport ,
125+ )
126+
127+ /* *
128+ * A kind of util class. See the description of each kind at their respective classes.
129+ * @property utilMethodProvider a [UtilClassFileMethodProvider] containing information about
130+ * utilities that come from a separately generated UtUtils class
131+ * (as opposed to utils that are declared directly in the test class, for example).
132+ * @property mockFrameworkUsed a flag indicating if a mock framework was used.
133+ * For detailed description see [CgContextOwner.mockFrameworkUsed].
134+ * @property mockFramework a framework used to create mocks
135+ * @property priority when we generate multiple test classes, they can require different [UtilClassKind].
136+ * We will generate an util class corresponding to the kind with the greatest priority.
137+ * For example, one test class may not use mocks, but the other one does.
138+ * Then we will generate an util class with mocks, because it has a greater priority (see [UtUtilsWithMockito]).
139+ */
140+ sealed class UtilClassKind (
141+ internal val utilMethodProvider : UtilClassFileMethodProvider ,
142+ internal val mockFrameworkUsed : Boolean ,
143+ internal val mockFramework : MockFramework = MockFramework .MOCKITO ,
144+ private val priority : Int
145+ ) : Comparable<UtilClassKind> {
146+
147+ /* *
148+ * The version of util class being generated.
149+ * For more details see [UtilClassFileMethodProvider.UTIL_CLASS_VERSION].
150+ */
151+ val utilClassVersion: String
152+ get() = UtilClassFileMethodProvider .UTIL_CLASS_VERSION
153+
154+ /* *
155+ * The comment specifying the version of util class being generated.
156+ *
157+ * @see UtilClassFileMethodProvider.UTIL_CLASS_VERSION
158+ */
159+ val utilClassVersionComment: CgComment
160+ get() = CgSingleLineComment (" $UTIL_CLASS_VERSION_COMMENT_PREFIX${utilClassVersion} " )
161+
162+
163+ /* *
164+ * The comment specifying the kind of util class being generated.
165+ *
166+ * @see utilClassKindCommentText
167+ */
168+ val utilClassKindComment: CgComment
169+ get() = CgSingleLineComment (utilClassKindCommentText)
170+
171+ /* *
172+ * The text of comment specifying the kind of util class.
173+ * At the moment, there are two kinds: [RegularUtUtils] (without Mockito) and [UtUtilsWithMockito].
174+ *
175+ * This comment is needed when the plugin decides whether to overwrite an existing util class or not.
176+ * When making that decision, it is important to determine if the existing class uses mocks or not,
177+ * and this comment will help do that.
178+ */
179+ abstract val utilClassKindCommentText: String
103180
181+ /* *
182+ * A kind of regular UtUtils class. "Regular" here means that this class does not use a mock framework.
183+ */
184+ object RegularUtUtils : UtilClassKind(UtilClassFileMethodProvider , mockFrameworkUsed = false , priority = 0 ) {
185+ override val utilClassKindCommentText: String
186+ get() = " This is a regular UtUtils class (without mock framework usage)"
187+ }
188+
189+ /* *
190+ * A kind of UtUtils class that uses a mock framework. At the moment the framework is Mockito.
191+ */
192+ object UtUtilsWithMockito : UtilClassKind(UtilClassFileMethodProvider , mockFrameworkUsed = true , priority = 1 ) {
193+ override val utilClassKindCommentText: String
194+ get() = " This is UtUtils class with Mockito support"
195+ }
196+
197+ override fun compareTo (other : UtilClassKind ): Int {
198+ return priority.compareTo(other.priority)
199+ }
200+
201+ /* *
202+ * Construct an util class file as a [CgRegularClassFile] and render it.
203+ * @return the text of the generated util class file.
204+ */
205+ fun getUtilClassText (codegenLanguage : CodegenLanguage ): String {
206+ val utilClassFile = CgUtilClassConstructor .constructUtilsClassFile(this )
207+ val renderer = CgAbstractRenderer .makeRenderer(this , codegenLanguage)
208+ utilClassFile.accept(renderer)
209+ return renderer.toString()
210+ }
211+
212+ companion object {
213+
214+ /* *
215+ * Class UtUtils will contain a comment specifying the version of this util class
216+ * (if we ever change util methods, then util class will be different, hence the update of its version).
217+ * This is a prefix that will go before the version in the comment.
218+ */
219+ const val UTIL_CLASS_VERSION_COMMENT_PREFIX = " UtUtils class version: "
220+
221+ fun utilClassKindByCommentOrNull (comment : String ): UtilClassKind ? {
222+ return when (comment) {
223+ RegularUtUtils .utilClassKindCommentText -> RegularUtUtils
224+ UtUtilsWithMockito .utilClassKindCommentText -> UtUtilsWithMockito
225+ else -> null
226+ }
227+ }
228+
229+ /* *
230+ * Check if an util class is required, and if so, what kind.
231+ * @return `null` if [CgContext.utilMethodProvider] is not [UtilClassFileMethodProvider],
232+ * because it means that util methods will be taken from some other provider (e.g. [TestClassUtilMethodProvider]).
233+ */
234+ internal fun fromCgContextOrNull (context : CgContext ): UtilClassKind ? {
235+ if (context.requiredUtilMethods.isEmpty()) return null
236+ if (! context.mockFrameworkUsed) {
237+ return RegularUtUtils
238+ }
239+ return when (context.mockFramework) {
240+ MockFramework .MOCKITO -> UtUtilsWithMockito
241+ // in case we will add any other mock frameworks, newer Kotlin compiler versions
242+ // will report a non-exhaustive 'when', so we will not forget to support them here as well
243+ }
244+ }
245+
246+ const val UT_UTILS_PACKAGE_NAME = " org.utbot.runtime.utils"
247+ const val UT_UTILS_CLASS_NAME = " UtUtils"
248+ const val PACKAGE_DELIMITER = " ."
249+
250+ /* *
251+ * List of package names of UtUtils class.
252+ * See whole package name at [UT_UTILS_PACKAGE_NAME].
253+ */
254+ val utilsPackages: List <String >
255+ get() = UT_UTILS_PACKAGE_NAME .split(PACKAGE_DELIMITER )
256+ }
257+ }
0 commit comments