Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add KSP plugin for generating Expect<..> accessors #1358

Open
vlsi opened this issue Mar 17, 2023 · 8 comments
Open

Add KSP plugin for generating Expect<..> accessors #1358

vlsi opened this issue Mar 17, 2023 · 8 comments

Comments

@vlsi
Copy link
Collaborator

vlsi commented Mar 17, 2023

Platform (all, jvm, js): all
Extension (none, kotlin 1.3): none

Code related feature

Suppose the user creates a class

@AtriumExpectations
data class Person(val firstName: String, val lastName: String, val isStudent: Boolean) {
    fun fullName() = "$firstName $lastName"
    fun nickname(includeLastName: Boolean) = when (includeLastName) {
        false -> "Mr. $firstName"
        true -> "$firstName aka. $lastName"
    }
}

val myPerson = Person("Robert", "Stoll", false)

Then Atrium could automatically generate expectation accessors:

val Expect<Person>.firstName: Expect<String>
fun Expect<Person>.firstName(assertionCreator: Expect<String>.() -> Unit): Expect<Person>

val Expect<Person>.lastName: Expect<String>
fun Expect<Person>.lastName(assertionCreator: Expect<String>.() -> Unit): Expect<Person>

val Expect<Person>.isStudent: Expect<Boolean>
fun Expect<Person>.isStudent(assertionCreator: Expect<Boolean>.() -> Unit): Expect<Person>

fun Expect<Person>.fullName(): Expect<String>
fun Expect<Person>.fullName(assertionCreator: Expect<String>.() -> Unit): Expect<Person>

fun Expect<Person>.nickname(includeLastName: Boolean): Expect<String>
fun Expect<Person>.nickname(includeLastName: Boolean, assertionCreator: Expect<String>.() -> Unit): Expect<Person>
@robstoll
Copy link
Owner

robstoll commented Mar 17, 2023

Thanks for the idea. We were considering this in the past. Back then we came to the conclusion that maintenance vs. benefit does pay out. I am kind of glad we decided that way back then as we would have used Kapt which now is deprecated. I am not sure how stable KSP is already so that we can really built upon it. On the other hand, a migration to another tool in a few years is most likely simple.

I am open for the topic but we should re-asses if it is worth it, especially considering that we plan to introduce a compiler plugin.
Following a few questions including how I see it

  1. Does expect(person).firstName.toEqual(...) read nicer than expect(person).its { firstName }.toEqual(...)
    IMO it does, especially if you want to use an expecation group firstName { ... } vs its({ firstName }) { ... }`
  2. Do we want to generate feature extractors only for public properties or also methods?
    I think we should have an option in the Annotation, something like @AtriumExpectations(targets=arrayOf(Properties, Methods)
  3. Should there be a way to skip generation for specific properties?
    I think there should be because some feature extractors might not be safe and one might want to implement a more specific version. Maybe we could have a second annotation, something like @AtriumSkipExtractor
  4. Do we want to use an annotation vs. configuration file?
    I already know that some people dislike that one has to put a test-annotation on classes in main. Maybe it would be better to use a configuration file instead? Personally, I don't mind the annotation approach. I would like to avoid to have both, annotation and config file.
  5. Should we support to generate feature extractor for third-party code?
    I think that would be a huge benefit. This is of course only possible if we use a config file approach in point 4.

@vlsi
Copy link
Collaborator Author

vlsi commented Mar 17, 2023

especially considering that we plan to introduce a compiler plugin.

I believe they complement each other.

A compiler plugin can't contribute to IDE's autocomplete.
In other words, the code should be "compilable", and the code should not use "non-existing function calls".

KSP plugin can help here by generating boilerplate functions and property accessors, so the user can write code without installing IDE plugins.

In other words,

  • compiler plugin can make "generic" feature(..) calls easier to use since you can't forsee accessors for every possible use case
  • KSP plugin can generate boilerplate accessors for well-known cases. Of course, the generated accessors can use verbose apis, and they do not need the compiler plugin

@AtriumSkipExtractor

@AtriumIgnore (see https://fasterxml.github.io/jackson-annotations/javadoc/2.5/com/fasterxml/jackson/annotation/JsonIgnore.html )

Should we support to generate feature extractor for third-party code?

I would start with annotations first, and then evaluate how it goes.

@vlsi
Copy link
Collaborator Author

vlsi commented Mar 17, 2023

@robstoll
Copy link
Owner

exactly from VisibleForTesting I know that there is a split between developers. One are strongly against and others don't care to much. but since our annotation does not require to open up things, I think it is less of a concern.

I suggest we generate feature extractors also for internal properties.

I rethought if we should include methods, I think it us enough to start with properties only.

@vlsi
Copy link
Collaborator Author

vlsi commented May 3, 2023

It looks like the case for generating feature extractors for third-party code is pretty real: #1421

@robstoll
Copy link
Owner

robstoll commented May 3, 2023

yapp, that's what I expected. thus configuration file seems more appropriate. suggestion regarding format of the configuration file? I dislike yaml due to all the indentation problems I already encountered.

@vlsi
Copy link
Collaborator Author

vlsi commented May 3, 2023

I wonder if semething like jackon's mixin would be enough. Then the user could declare a dummy class and configure "theat its annotations as if they were specified at SQLException"

@robstoll
Copy link
Owner

robstoll commented May 3, 2023

I guess we could somehow support an annotation based approach but I think a configuration file will be a better fit for this as you can for instance define excludes (or includes) via regex and the like and I think we will quickly need this flexiblity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants