-
Notifications
You must be signed in to change notification settings - Fork 31
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
smarter action shuffler #39
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
# This is a Gradle generated file for dependency locking. | ||
# Manual edits can break the build and are not advised. | ||
# This file is expected to be part of source control. | ||
org.jetbrains.kotlin:kotlin-reflect:1.2.70 | ||
org.jetbrains.kotlin:kotlin-stdlib-common:1.2.70 | ||
org.jetbrains.kotlin:kotlin-stdlib:1.2.70 | ||
org.jetbrains:annotations:13.0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
# This is a Gradle generated file for dependency locking. | ||
# Manual edits can break the build and are not advised. | ||
# This file is expected to be part of source control. | ||
org.jetbrains.kotlin:kotlin-reflect:1.2.70 | ||
org.jetbrains.kotlin:kotlin-stdlib-common:1.2.70 | ||
org.jetbrains.kotlin:kotlin-stdlib:1.2.70 | ||
org.jetbrains:annotations:13.0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package com.atlassian.performance.tools.jiraactions.api.scenario | ||
|
||
import com.atlassian.performance.tools.jiraactions.api.SeededRandom | ||
import com.atlassian.performance.tools.jiraactions.api.WebJira | ||
import com.atlassian.performance.tools.jiraactions.api.action.Action | ||
import com.atlassian.performance.tools.jiraactions.api.action.BrowseProjectsAction | ||
import com.atlassian.performance.tools.jiraactions.api.action.CreateIssueAction | ||
import com.atlassian.performance.tools.jiraactions.api.action.SearchJqlAction | ||
import com.atlassian.performance.tools.jiraactions.api.measure.ActionMeter | ||
import com.atlassian.performance.tools.jiraactions.api.memories.IssueKeyMemory | ||
import com.atlassian.performance.tools.jiraactions.api.memories.JqlMemory | ||
import com.atlassian.performance.tools.jiraactions.api.page.IssuePage | ||
import kotlin.reflect.KClass | ||
|
||
internal class ActionShuffler { | ||
private class FixedJqlMemory(val jql: String) : JqlMemory { | ||
override fun observe(issuePage: IssuePage) { | ||
throw UnsupportedOperationException() | ||
} | ||
|
||
override fun recall(): String? { | ||
return jql | ||
} | ||
|
||
override fun remember(memories: Collection<String>) { | ||
throw UnsupportedOperationException() | ||
} | ||
} | ||
|
||
companion object { | ||
fun createRandomisedScenario(seededRandom: SeededRandom, actionProportions: Map<Action, Int>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the problem we're trying to solve here? Do skipped actions malform the fun recall(): String {
if (issueKeys.isEmpty()) {
SearchJqlAction(jira, throwawayActionMeter, IssueJqlMemory(), this).run()
}
} This way you can avoid skips with local logic rather than a global order-controlling scheme. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. I want to preserve the requested proportions. During our runs, ~1500 view issue calls are skipped. This looks like a minimally invasive way of achieving this, without altering the way scenarios are created. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same problem affects ViewBoard, btw. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's try a different question: is there any benefit to keeping a fully-randomised shuffle in the scenario? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤔 |
||
issueKeyMemoriser: Action): List<Action> { | ||
//createIssue needs a project - browserProject goes first | ||
//viewIssue needs to have issues - createIssues goes second | ||
return createRandomisedScenario(seededRandom, actionProportions, issueKeyMemoriser, BrowseProjectsAction::class, CreateIssueAction::class) | ||
} | ||
|
||
fun findIssueKeysWithJql(jira: WebJira, meter: ActionMeter, issueKeyMemory: IssueKeyMemory): SearchJqlAction { | ||
return SearchJqlAction( | ||
jira = jira, | ||
meter = meter, | ||
jqlMemory = FixedJqlMemory("project is not EMPTY"), | ||
issueKeyMemory = issueKeyMemory | ||
) | ||
} | ||
|
||
private fun createRandomisedScenario(seededRandom: SeededRandom, actionProportions: Map<Action, Int>, | ||
issueKeyDiscoverer: Action, vararg actions: KClass<out Action>): List<Action> { | ||
val initialActions = findActions(actionProportions, *actions) | ||
|
||
val scenario: MutableList<Action> = mutableListOf() | ||
|
||
val actionProportionsToRandomise = deductActionCount(actionProportions, initialActions) | ||
actionProportionsToRandomise.entries.forEach { scenario.addMultiple(element = it.key, repeats = it.value) } | ||
scenario.shuffle(seededRandom.random) | ||
|
||
//viewIssue needs to remember isssues - issueKeyDiscoverer goes after all actions | ||
scenario.addAll(0, initialActions.plus(issueKeyDiscoverer)) | ||
return scenario | ||
} | ||
|
||
private fun findActions(actionProportions: Map<Action, Int>, vararg actions: KClass<out Action>): List<Action> { | ||
return actions | ||
.mapNotNull { findAction(actionProportions, it) } | ||
} | ||
|
||
private fun deductActionCount(actionProportions: Map<Action, Int>, actions: List<Action>): MutableMap<Action, Int> { | ||
val modifiedActionProportions = actionProportions.toMutableMap() | ||
|
||
actions.forEach { | ||
val originalCount = actionProportions.getValue(it) | ||
modifiedActionProportions[it] = originalCount-1 | ||
} | ||
return modifiedActionProportions | ||
} | ||
|
||
private fun <T : Action> findAction(actionProportions: Map<Action, Int>, kClass: KClass<T>): T? { | ||
@Suppress("UNCHECKED_CAST") | ||
return actionProportions.keys.find { kClass.java.isInstance(it) } as T? | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uh oh, reflection = danger zone. We're bypassing a lot of compilation-time guarantees.