forked from j62/ctbrec
Add playground dialog for post-processing variables/functions and update the documentation
This commit is contained in:
parent
e63107cd93
commit
f2df8deb0c
|
@ -278,7 +278,8 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
||||||
Group.of("Post-Processing",
|
Group.of("Post-Processing",
|
||||||
Setting.of("Threads", postProcessingThreads),
|
Setting.of("Threads", postProcessingThreads),
|
||||||
Setting.of("Steps", postProcessingStepPanel),
|
Setting.of("Steps", postProcessingStepPanel),
|
||||||
Setting.of("", createHelpButton("Post-Processing Help", "http://localhost:5689/docs/PostProcessing.md")))),
|
Setting.of("", createHelpButton("Post-Processing Help", "http://localhost:5689/docs/PostProcessing.md")),
|
||||||
|
Setting.of("", createVariablePlayGroundButton()))),
|
||||||
Category.of("Events & Actions", new ActionSettingsPanel(recorder)), Category.of("Ignore List", ignoreList),
|
Category.of("Events & Actions", new ActionSettingsPanel(recorder)), Category.of("Ignore List", ignoreList),
|
||||||
Category.of("Sites", siteCategories.toArray(new Category[0])),
|
Category.of("Sites", siteCategories.toArray(new Category[0])),
|
||||||
Category.of("Proxy",
|
Category.of("Proxy",
|
||||||
|
@ -377,6 +378,12 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
||||||
return postProcessingHelpButton;
|
return postProcessingHelpButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Button createVariablePlayGroundButton() {
|
||||||
|
var postProcessingHelpButton = new Button("Variable Playground");
|
||||||
|
postProcessingHelpButton.setOnAction(e -> new VariablePlayGroundDialogFactory().openDialog(this.getTabPane().getScene(), config, recorder));
|
||||||
|
return postProcessingHelpButton;
|
||||||
|
}
|
||||||
|
|
||||||
private void bindEnabledProperty(Setting s, BooleanExpression bindTo) {
|
private void bindEnabledProperty(Setting s, BooleanExpression bindTo) {
|
||||||
try {
|
try {
|
||||||
s.getGui().disableProperty().bind(bindTo);
|
s.getGui().disableProperty().bind(bindTo);
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
package ctbrec.ui.settings;
|
||||||
|
|
||||||
|
import ctbrec.Config;
|
||||||
|
import ctbrec.Recording;
|
||||||
|
import ctbrec.StringUtil;
|
||||||
|
import ctbrec.UnknownModel;
|
||||||
|
import ctbrec.recorder.Recorder;
|
||||||
|
import ctbrec.recorder.postprocessing.PostProcessingContext;
|
||||||
|
import ctbrec.sites.chaturbate.Chaturbate;
|
||||||
|
import ctbrec.ui.controls.Dialogs;
|
||||||
|
import ctbrec.ui.tabs.DownloadPostprocessor;
|
||||||
|
import ctbrec.variableexpansion.functions.AntlrSyntacErrorAdapter;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
import org.antlr.v4.runtime.RecognitionException;
|
||||||
|
import org.antlr.v4.runtime.Recognizer;
|
||||||
|
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
public class VariablePlayGroundDialogFactory {
|
||||||
|
public void openDialog(Scene parent, Config config, Recorder recorder) {
|
||||||
|
Chaturbate chaturbate = new Chaturbate();
|
||||||
|
UnknownModel unknownModel = new UnknownModel();
|
||||||
|
unknownModel.setName("Pussy_Galore");
|
||||||
|
unknownModel.setDisplayName("Pussy Galore");
|
||||||
|
unknownModel.setSite(chaturbate);
|
||||||
|
Recording recording = new Recording();
|
||||||
|
recording.setAbsoluteFile(Paths.get("ctbrec", "recs", "pussy_galore", "2023-02-26_14-05-56").toFile());
|
||||||
|
recording.setStartDate(Instant.now());
|
||||||
|
recording.setStatus(Recording.State.POST_PROCESSING);
|
||||||
|
recording.setNote("notes about the recording");
|
||||||
|
recording.setModel(unknownModel);
|
||||||
|
PostProcessingContext ctx = new PostProcessingContext();
|
||||||
|
ctx.setConfig(config);
|
||||||
|
ctx.setRecorder(recorder);
|
||||||
|
ctx.setRecording(recording);
|
||||||
|
DownloadPostprocessor postprocessor = new DownloadPostprocessor();
|
||||||
|
|
||||||
|
GridPane pane = new GridPane();
|
||||||
|
Label result = new Label();
|
||||||
|
Label error = new Label();
|
||||||
|
|
||||||
|
pane.add(new Label("Expression"), 0, 0);
|
||||||
|
TextField input = new TextField();
|
||||||
|
input.setMinWidth(600);
|
||||||
|
pane.add(input, 1, 0);
|
||||||
|
GridPane.setHgrow(input, Priority.ALWAYS);
|
||||||
|
input.setOnKeyTyped(evt -> {
|
||||||
|
String r = postprocessor.fillInPlaceHolders(input.getText(), ctx, new AntlrSyntacErrorAdapter() {
|
||||||
|
@Override
|
||||||
|
public void syntaxError(Recognizer<?, ?> recognizer, Object o, int line, int pos, String s, RecognitionException e) {
|
||||||
|
error.setText(String.format("Syntax error at %d:%d %s", line, pos, s));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
result.setText(r);
|
||||||
|
if (StringUtil.isNotBlank(r)) {
|
||||||
|
error.setText("");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pane.add(error, 0, 1);
|
||||||
|
GridPane.setColumnSpan(error, 2);
|
||||||
|
|
||||||
|
pane.add(result, 0, 2);
|
||||||
|
GridPane.setColumnSpan(result, 2);
|
||||||
|
|
||||||
|
pane.setHgap(5);
|
||||||
|
pane.vgapProperty().bind(pane.hgapProperty());
|
||||||
|
Dialogs.showCustomInput(parent, "Playground", pane, (obs, oldV, newV) -> {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
#### Post-Processing
|
#### Post-Processing
|
||||||
|
|
||||||
The post-processing gives you the possibility to execute different actions after a recording has finished. You can use that to convert
|
The post-processing gives you the possibility to execute different actions after a recording has finished. You can use that to convert
|
||||||
the files to another format, create preview images, rename / move the file etc.
|
the files to another format, create preview images, rename / move the file etc.
|
||||||
|
|
||||||
##### Available Steps
|
##### Available Steps
|
||||||
|
|
||||||
- **create a copy** - Creates a copy of the original recording. All following post-processing steps are executed on the copy, not on the
|
- **create a copy** - Creates a copy of the original recording. All following post-processing steps are executed on the copy, not on the
|
||||||
original recording. This means, that the post-processing can be rerun in case a step failed, because the original recording is still
|
original recording. This means, that the post-processing can be rerun in case a step failed, because the original recording is still
|
||||||
available.
|
available.
|
||||||
|
@ -21,17 +23,20 @@ the files to another format, create preview images, rename / move the file etc.
|
||||||
- **create contactsheet** - create a contact sheet with preview images of the recording
|
- **create contactsheet** - create a contact sheet with preview images of the recording
|
||||||
|
|
||||||
#### Planned for future releases
|
#### Planned for future releases
|
||||||
|
|
||||||
- **call a webhook** - call a URL once a recording is finished
|
- **call a webhook** - call a URL once a recording is finished
|
||||||
- **create timeline thumbnails** - create a small thumbnail for every second or every few seconds, which can be used to very fast
|
- **create timeline thumbnails** - create a small thumbnail for every second or every few seconds, which can be used to very fast
|
||||||
scan through a recording
|
scan through a recording
|
||||||
|
|
||||||
#### How to configure the server to do post-processing
|
#### How to configure the server to do post-processing
|
||||||
|
|
||||||
There is currently no user interface to configure the post-processing for the server. It has to be added manually to the server config.
|
There is currently no user interface to configure the post-processing for the server. It has to be added manually to the server config.
|
||||||
I suggest to start the app and configure the post-processing steps in the settings. Afterwards you close the app and copy the
|
I suggest to start the app and configure the post-processing steps in the settings. Afterwards you close the app and copy the
|
||||||
post-processing section from the settings.json to your server.json file. To find out, where these files are on your system, read
|
post-processing section from the settings.json to your server.json file. To find out, where these files are on your system, read
|
||||||
[Configuration File](ConfigurationFile.md).
|
[Configuration File](ConfigurationFile.md).
|
||||||
|
|
||||||
The part you have to copy is
|
The part you have to copy is
|
||||||
|
|
||||||
```
|
```
|
||||||
postProcessors: [
|
postProcessors: [
|
||||||
...
|
...
|
||||||
|
@ -43,16 +48,13 @@ The part you have to copy is
|
||||||
<a id="variables" />
|
<a id="variables" />
|
||||||
|
|
||||||
###### Available variables:
|
###### Available variables:
|
||||||
|
|
||||||
- **${modelName}** - the name of the recorded model
|
- **${modelName}** - the name of the recorded model
|
||||||
- **${modelDisplayName}** - the name of the recorded model, which is shown on the webpage. Might be the same as
|
- **${modelDisplayName}** - the name of the recorded model, which is shown on the webpage. Might be the same as
|
||||||
${modelName}
|
${modelName}
|
||||||
- **${modelSanitizedName}** - sanitized name of the model. The following characters are replaced by an underscore:
|
|
||||||
\\, /, ', " and space
|
|
||||||
- **${modelGroupName}** - name of the model group, if the model is part of a group
|
- **${modelGroupName}** - name of the model group, if the model is part of a group
|
||||||
- **${modelGroupId}** - the unique ID of the model group, if the model is part of a group
|
- **${modelGroupId}** - the unique ID of the model group, if the model is part of a group
|
||||||
- **${siteName}** - the name of the cam site, the model streams on
|
- **${siteName}** - the name of the cam site, the model streams on
|
||||||
- **${siteSanitizedName}** - sanitized name of the site. The following characters are replaced by an underscore:
|
|
||||||
\\, /, ', " and space
|
|
||||||
- **${fileSuffix}** - the file extension of the recording. E.g. ts or mp4. In case of a standard server recording,
|
- **${fileSuffix}** - the file extension of the recording. E.g. ts or mp4. In case of a standard server recording,
|
||||||
this will be empty
|
this will be empty
|
||||||
- **${epochSecond}** - timestamp of the recording in seconds since 1970-01-01 (unixtime)
|
- **${epochSecond}** - timestamp of the recording in seconds since 1970-01-01 (unixtime)
|
||||||
|
@ -66,10 +68,25 @@ The part you have to copy is
|
||||||
a server recording)
|
a server recording)
|
||||||
- **${absoluteParentPath}** - the absolute path to the parent directory of the recording in the filesystem (or the
|
- **${absoluteParentPath}** - the absolute path to the parent directory of the recording in the filesystem (or the
|
||||||
recording dir in case of a server recording)
|
recording dir in case of a server recording)
|
||||||
- **${utcDateTime}** and **${localDateTime}** - the timestamp of the recording in the UTC or your local timezone. If no
|
- **${utcDateTime}** and **${localDateTime}** - the timestamp of the recording in the UTC or your local timezone
|
||||||
pattern is given, the default ```yyyy-MM-dd_HH-mm-ss``` is used. You can also define your own pattern using the following
|
|
||||||
symbols. For example ```${localDateTime(yyyyMMdd-HHmmss)}``` would lead to 20200928-173605.
|
#### Functions
|
||||||
|
|
||||||
|
- **$trim** - removes all leading and trailing space - `$trim( hello world )` becomes `hello world`
|
||||||
|
- **$upper** - converts all of the characters to upper case - `$upper(hello world)` becomes `HELLO WORLD`
|
||||||
|
- **$lower** - converts all of the characters to lower case - `$lower(hElLo WORLD)` becomes `hello world`
|
||||||
|
- **$capitalize** - capitalizes words changing the first character to upper case - `$capitalize(hElLo WorLD)` becomes `HElLo WorLD`
|
||||||
|
- **$sanitize** - removes problematic characters - `$sanitize(hEl'Lo / WO"RLD)` becomes `hEl_Lo___WO_RLD`. The following characters are replaced by an
|
||||||
|
underscore:
|
||||||
|
\\, /, ', " and space
|
||||||
|
- **$orElse** - provide an alternative in case a variable is not set - `$orElse(${variable},someValue)` - becomes `${variable}`, if it is set or `someValue`, if
|
||||||
|
`${variable}` is not set
|
||||||
|
- **$format** - formats a date, can be used with a pattern or without.
|
||||||
|
Examples:
|
||||||
|
- `$format(${localDateTime})` - becomes something like `2023-02-26_12-23-15`
|
||||||
|
- `$format(${localDateTime},yyyyMMdd-HHmmss)` would lead to `20200928-173605`
|
||||||
<table class="table-striped">
|
<table class="table-striped">
|
||||||
|
|
||||||
<thead>
|
<thead>
|
||||||
<tr><th scope="col">Symbol</th> <th scope="col">Meaning</th> <th scope="col">Presentation</th> <th scope="col">Examples</th>
|
<tr><th scope="col">Symbol</th> <th scope="col">Meaning</th> <th scope="col">Presentation</th> <th scope="col">Examples</th>
|
||||||
</tr></thead>
|
</tr></thead>
|
||||||
|
@ -119,15 +136,9 @@ The part you have to copy is
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
For more information see: [DateTimeFormatter](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html)
|
For more information see: [DateTimeFormatter](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html)
|
||||||
###### Fallback values:
|
|
||||||
You can define a fallback value for each variable in case there is no value available for the variable. The syntax is
|
|
||||||
|
|
||||||
${placeholder?foobar}
|
#### Full Example
|
||||||
|
|
||||||
Let's for example say you have created some model groups. For models, which are part of a group, you want to use the group name. But for models, which
|
`$orElse(${modelGroupName},$sanitize(${modelName}))_$format(${localDateTime})_${recordingNotes}`
|
||||||
are not part of a group you want to use the sanitized name. You can achieve that by using the following expression:
|
|
||||||
|
|
||||||
${modelGroupName?${modelSanitizedName}}
|
|
||||||
|
|
||||||
It can be read like "use the modelGroupName, but if that is not available use modelSanitizedName".
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ public class Settings {
|
||||||
public int defaultPriority = 50;
|
public int defaultPriority = 50;
|
||||||
public boolean determineResolution = false;
|
public boolean determineResolution = false;
|
||||||
public List<String> disabledSites = new ArrayList<>();
|
public List<String> disabledSites = new ArrayList<>();
|
||||||
public String downloadFilename = "${modelSanitizedName}-$format(${localDateTime})";
|
public String downloadFilename = "$sanitize(${modelName})_$format(${localDateTime})";
|
||||||
public List<EventHandlerConfiguration> eventHandlers = new ArrayList<>();
|
public List<EventHandlerConfiguration> eventHandlers = new ArrayList<>();
|
||||||
public boolean eventsSuspended = false;
|
public boolean eventsSuspended = false;
|
||||||
public boolean fastScrollSpeed = true;
|
public boolean fastScrollSpeed = true;
|
||||||
|
|
|
@ -10,7 +10,9 @@ import ctbrec.variableexpansion.antlr.PostProcessingLexer;
|
||||||
import ctbrec.variableexpansion.antlr.PostProcessingParser;
|
import ctbrec.variableexpansion.antlr.PostProcessingParser;
|
||||||
import ctbrec.variableexpansion.functions.AntlrSyntacErrorAdapter;
|
import ctbrec.variableexpansion.functions.AntlrSyntacErrorAdapter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.antlr.v4.runtime.*;
|
import org.antlr.v4.runtime.CharStream;
|
||||||
|
import org.antlr.v4.runtime.CharStreams;
|
||||||
|
import org.antlr.v4.runtime.CommonTokenStream;
|
||||||
import org.antlr.v4.runtime.tree.ParseTree;
|
import org.antlr.v4.runtime.tree.ParseTree;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -28,6 +30,10 @@ import static java.util.Optional.ofNullable;
|
||||||
public abstract class AbstractPlaceholderAwarePostProcessor extends AbstractPostProcessor {
|
public abstract class AbstractPlaceholderAwarePostProcessor extends AbstractPostProcessor {
|
||||||
|
|
||||||
public String fillInPlaceHolders(String input, PostProcessingContext ctx) {
|
public String fillInPlaceHolders(String input, PostProcessingContext ctx) {
|
||||||
|
return this.fillInPlaceHolders(input, ctx, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String fillInPlaceHolders(String input, PostProcessingContext ctx, AntlrSyntacErrorAdapter errorListener) {
|
||||||
Recording rec = ctx.getRecording();
|
Recording rec = ctx.getRecording();
|
||||||
Config config = ctx.getConfig();
|
Config config = ctx.getConfig();
|
||||||
|
|
||||||
|
@ -42,21 +48,16 @@ public abstract class AbstractPlaceholderAwarePostProcessor extends AbstractPost
|
||||||
variables.put("utcDateTime", getUtcDateTime(rec));
|
variables.put("utcDateTime", getUtcDateTime(rec));
|
||||||
variables.put("localDateTime", getLocalDateTime(rec));
|
variables.put("localDateTime", getLocalDateTime(rec));
|
||||||
|
|
||||||
return fillInPlaceHolders(input, variables);
|
return fillInPlaceHolders(input, variables, errorListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String fillInPlaceHolders(String input, Map<String, Optional<Object>> variables) {
|
private String fillInPlaceHolders(String input, Map<String, Optional<Object>> variables, AntlrSyntacErrorAdapter errorListener) {
|
||||||
try (StringReader reader = new StringReader(input)) {
|
try (StringReader reader = new StringReader(input)) {
|
||||||
CharStream s = CharStreams.fromReader(reader);
|
CharStream s = CharStreams.fromReader(reader);
|
||||||
PostProcessingLexer lexer = new PostProcessingLexer(s);
|
PostProcessingLexer lexer = new PostProcessingLexer(s);
|
||||||
CommonTokenStream tokens = new CommonTokenStream(lexer);
|
CommonTokenStream tokens = new CommonTokenStream(lexer);
|
||||||
PostProcessingParser parser = new PostProcessingParser(tokens);
|
PostProcessingParser parser = new PostProcessingParser(tokens);
|
||||||
parser.addErrorListener(new AntlrSyntacErrorAdapter() {
|
Optional.ofNullable(errorListener).ifPresent(parser::addErrorListener);
|
||||||
@Override
|
|
||||||
public void syntaxError(Recognizer<?, ?> recognizer, Object o, int line, int pos, String s, RecognitionException e) {
|
|
||||||
log.warn("Syntax error at {}:{} {}", line, pos, s);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ParseTree parseTree = parser.line();
|
ParseTree parseTree = parser.line();
|
||||||
ParserVisitor visitor = new ParserVisitor(variables);
|
ParserVisitor visitor = new ParserVisitor(variables);
|
||||||
return visitor.visit(parseTree);
|
return visitor.visit(parseTree);
|
||||||
|
|
|
@ -16,7 +16,7 @@ public class Move extends AbstractPlaceholderAwarePostProcessor {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(Move.class);
|
private static final Logger LOG = LoggerFactory.getLogger(Move.class);
|
||||||
public static final String PATH_TEMPLATE = "path.template";
|
public static final String PATH_TEMPLATE = "path.template";
|
||||||
public static final String DEFAULT = "${modelSanitizedName}" + File.separatorChar + "$format(${localDateTime})";
|
public static final String DEFAULT = "$sanitize(${modelName})" + File.separatorChar + "$format(${localDateTime})";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
|
|
@ -13,8 +13,8 @@ public class Rename extends AbstractPlaceholderAwarePostProcessor {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(Rename.class);
|
private static final Logger LOG = LoggerFactory.getLogger(Rename.class);
|
||||||
public static final String FILE_NAME_TEMPLATE = "filename.template";
|
public static final String FILE_NAME_TEMPLATE = "filename.template";
|
||||||
public static final String DEFAULT = "${modelSanitizedName}_$format(${localDateTime}).${fileSuffix}";
|
public static final String DEFAULT = "$sanitize(${modelName})_$format(${localDateTime}).${fileSuffix}";
|
||||||
public static final String DEFAULT_DIR = "${modelSanitizedName}_$format(${localDateTime})";
|
public static final String DEFAULT_DIR = "$sanitize(${modelName})_$format(${localDateTime})";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
|
|
@ -18,10 +18,8 @@ public class ModelVariableExpander extends AbstractVariableExpander {
|
||||||
Optional<ModelGroup> modelGroup = Optional.ofNullable(recorder).flatMap(r -> r.getModelGroup(model));
|
Optional<ModelGroup> modelGroup = Optional.ofNullable(recorder).flatMap(r -> r.getModelGroup(model));
|
||||||
placeholderValueSuppliers.put("modelName", ofNullable(model.getName()));
|
placeholderValueSuppliers.put("modelName", ofNullable(model.getName()));
|
||||||
placeholderValueSuppliers.put("modelDisplayName", ofNullable(model.getDisplayName()));
|
placeholderValueSuppliers.put("modelDisplayName", ofNullable(model.getDisplayName()));
|
||||||
placeholderValueSuppliers.put("modelSanitizedName",getSanitizedName(model));
|
|
||||||
placeholderValueSuppliers.put("modelNotes", getSanitizedModelNotes(config, model));
|
placeholderValueSuppliers.put("modelNotes", getSanitizedModelNotes(config, model));
|
||||||
placeholderValueSuppliers.put("siteName", ofNullable(model).map(Model::getSite).map(Site::getName));
|
placeholderValueSuppliers.put("siteName", ofNullable(model).map(Model::getSite).map(Site::getName));
|
||||||
placeholderValueSuppliers.put("siteSanitizedName",getSanitizedSiteName(model));
|
|
||||||
placeholderValueSuppliers.put("modelGroupName", modelGroup.map(ModelGroup::getName));
|
placeholderValueSuppliers.put("modelGroupName", modelGroup.map(ModelGroup::getName));
|
||||||
placeholderValueSuppliers.put("modelGroupId", modelGroup.map(ModelGroup::getId).map(UUID::toString));
|
placeholderValueSuppliers.put("modelGroupId", modelGroup.map(ModelGroup::getId).map(UUID::toString));
|
||||||
}
|
}
|
||||||
|
@ -30,19 +28,6 @@ public class ModelVariableExpander extends AbstractVariableExpander {
|
||||||
return fillInPlaceHolders(input, placeholderValueSuppliers);
|
return fillInPlaceHolders(input, placeholderValueSuppliers);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<Object> getSanitizedName(Model model) {
|
|
||||||
String name = model.getSanitizedNamed();
|
|
||||||
if (StringUtil.isBlank(name)) {
|
|
||||||
return Optional.empty();
|
|
||||||
} else {
|
|
||||||
return Optional.of(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<Object> getSanitizedSiteName(Model model) {
|
|
||||||
return ofNullable(model).map(Model::getSite).map(Site::getName).map(StringUtil::sanitize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<Object> getSanitizedModelNotes(Config config, Model m) {
|
private Optional<Object> getSanitizedModelNotes(Config config, Model m) {
|
||||||
return ofNullable(config.getModelNotes(m)).map(StringUtil::sanitize);
|
return ofNullable(config.getModelNotes(m)).map(StringUtil::sanitize);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,10 @@ import java.util.Optional;
|
||||||
|
|
||||||
public class Capitalize implements VarArgsFunction<Object, String> {
|
public class Capitalize implements VarArgsFunction<Object, String> {
|
||||||
@Override
|
@Override
|
||||||
public String apply(Object[] params) {
|
public String apply(Object... params) {
|
||||||
|
if (params == null || params.length == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
return Optional.ofNullable(params).map(p -> p[0]).map(String.class::cast).map(StringUtil::capitalize).orElse("");
|
return Optional.ofNullable(params).map(p -> p[0]).map(String.class::cast).map(StringUtil::capitalize).orElse("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,14 +9,18 @@ import java.util.Optional;
|
||||||
|
|
||||||
public class Format implements VarArgsFunction<Object, String> {
|
public class Format implements VarArgsFunction<Object, String> {
|
||||||
@Override
|
@Override
|
||||||
public String apply(Object[] params) {
|
public String apply(Object... params) {
|
||||||
return Optional.ofNullable(params).map(p -> {
|
return Optional.ofNullable(params).map(p -> {
|
||||||
|
try {
|
||||||
|
if (p.length == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
String format = "yyyy-MM-dd_HH-mm-ss";
|
String format = "yyyy-MM-dd_HH-mm-ss";
|
||||||
if (p.length > 1) {
|
if (p.length > 1) {
|
||||||
format = (String) p[1];
|
format = (String) p[1];
|
||||||
}
|
}
|
||||||
ZonedDateTime zdt;
|
ZonedDateTime zdt;
|
||||||
if (p[0] instanceof String date) {
|
if (p != null && p.length > 0 && p[0] instanceof String date) {
|
||||||
try {
|
try {
|
||||||
zdt = ZonedDateTime.parse(date);
|
zdt = ZonedDateTime.parse(date);
|
||||||
} catch (DateTimeParseException e) {
|
} catch (DateTimeParseException e) {
|
||||||
|
@ -26,6 +30,9 @@ public class Format implements VarArgsFunction<Object, String> {
|
||||||
zdt = (ZonedDateTime) p[0];
|
zdt = (ZonedDateTime) p[0];
|
||||||
}
|
}
|
||||||
return DateTimeFormatter.ofPattern(format).format(zdt);
|
return DateTimeFormatter.ofPattern(format).format(zdt);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.orElse("");
|
.orElse("");
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,10 @@ import java.util.Optional;
|
||||||
|
|
||||||
public class Lower implements VarArgsFunction<Object, String> {
|
public class Lower implements VarArgsFunction<Object, String> {
|
||||||
@Override
|
@Override
|
||||||
public String apply(Object[] params) {
|
public String apply(Object... params) {
|
||||||
|
if (params == null || params.length == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
return Optional.ofNullable(params).map(sa -> sa[0]).map(String.class::cast).map(String::toLowerCase).orElse("");
|
return Optional.ofNullable(params).map(sa -> sa[0]).map(String.class::cast).map(String::toLowerCase).orElse("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,15 +8,19 @@ import java.util.Optional;
|
||||||
public class OrElse implements VarArgsFunction<Object, String> {
|
public class OrElse implements VarArgsFunction<Object, String> {
|
||||||
@Override
|
@Override
|
||||||
public String apply(Object... params) {
|
public String apply(Object... params) {
|
||||||
if (params.length < 2) {
|
if (params == null || params.length < 1) {
|
||||||
throw new IllegalArgumentException("OrElse needs two parameters");
|
return "";
|
||||||
|
}
|
||||||
|
String fallback = "";
|
||||||
|
if (params.length >= 2) {
|
||||||
|
fallback = String.valueOf(params[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<String> result = Optional.ofNullable(params[0]).map(String::valueOf);
|
Optional<String> result = Optional.ofNullable(params[0]).map(String::valueOf);
|
||||||
if (result.isPresent() && StringUtil.isNotBlank(result.get())) {
|
if (result.isPresent() && StringUtil.isNotBlank(result.get())) {
|
||||||
return result.get();
|
return result.get();
|
||||||
} else {
|
} else {
|
||||||
return String.valueOf(params[1]);
|
return fallback;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,10 @@ import java.util.Optional;
|
||||||
|
|
||||||
public class Sanitize implements VarArgsFunction<Object, String> {
|
public class Sanitize implements VarArgsFunction<Object, String> {
|
||||||
@Override
|
@Override
|
||||||
public String apply(Object[] params) {
|
public String apply(Object... params) {
|
||||||
|
if (params == null || params.length == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
return Optional.ofNullable(params).map(p -> p[0]).map(String.class::cast).map(StringUtil::sanitize).orElse("");
|
return Optional.ofNullable(params).map(p -> p[0]).map(String.class::cast).map(StringUtil::sanitize).orElse("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,10 @@ import java.util.Optional;
|
||||||
|
|
||||||
public class Trim implements VarArgsFunction<Object, String> {
|
public class Trim implements VarArgsFunction<Object, String> {
|
||||||
@Override
|
@Override
|
||||||
public String apply(Object[] params) {
|
public String apply(Object... params) {
|
||||||
|
if (params == null || params.length == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
return Optional.ofNullable(params).map(sa -> sa[0]).map(String.class::cast).map(String::trim).orElse("");
|
return Optional.ofNullable(params).map(sa -> sa[0]).map(String.class::cast).map(String::trim).orElse("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,10 @@ import java.util.Optional;
|
||||||
|
|
||||||
public class Upper implements VarArgsFunction<Object, String> {
|
public class Upper implements VarArgsFunction<Object, String> {
|
||||||
@Override
|
@Override
|
||||||
public String apply(Object[] params) {
|
public String apply(Object... params) {
|
||||||
|
if (params == null || params.length == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
return Optional.ofNullable(params).map(sa -> sa[0]).map(String.class::cast).map(String::toUpperCase).orElse("");
|
return Optional.ofNullable(params).map(sa -> sa[0]).map(String.class::cast).map(String::toUpperCase).orElse("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ class AbstractPlaceholderAwarePostProcessorTest extends AbstractPpTest {
|
||||||
assertEquals("asdf_Mockita Boobilicious_asdf", placeHolderAwarePp.fillInPlaceHolders(input, createPostProcessingContext(rec, null, config)));
|
assertEquals("asdf_Mockita Boobilicious_asdf", placeHolderAwarePp.fillInPlaceHolders(input, createPostProcessingContext(rec, null, config)));
|
||||||
input = "asdf_${modelDisplayName}_asdf";
|
input = "asdf_${modelDisplayName}_asdf";
|
||||||
assertEquals("asdf_Mockita Boobilicious_asdf", placeHolderAwarePp.fillInPlaceHolders(input, createPostProcessingContext(rec, null, config)));
|
assertEquals("asdf_Mockita Boobilicious_asdf", placeHolderAwarePp.fillInPlaceHolders(input, createPostProcessingContext(rec, null, config)));
|
||||||
input = "asdf_${modelSanitizedName}_asdf";
|
input = "asdf_$sanitize(${modelName})_asdf";
|
||||||
assertEquals("asdf_Mockita_Boobilicious_asdf", placeHolderAwarePp.fillInPlaceHolders(input, createPostProcessingContext(rec, null, config)));
|
assertEquals("asdf_Mockita_Boobilicious_asdf", placeHolderAwarePp.fillInPlaceHolders(input, createPostProcessingContext(rec, null, config)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ class AbstractPlaceholderAwarePostProcessorTest extends AbstractPpTest {
|
||||||
void testSiteNameReplacement() {
|
void testSiteNameReplacement() {
|
||||||
String input = "asdf_${siteName}_asdf";
|
String input = "asdf_${siteName}_asdf";
|
||||||
assertEquals("asdf_Chaturbate_asdf", placeHolderAwarePp.fillInPlaceHolders(input, createPostProcessingContext(rec, null, config)));
|
assertEquals("asdf_Chaturbate_asdf", placeHolderAwarePp.fillInPlaceHolders(input, createPostProcessingContext(rec, null, config)));
|
||||||
input = "asdf_${siteSanitizedName}_asdf";
|
input = "asdf_$sanitize(${siteName})_asdf";
|
||||||
assertEquals("asdf_Chaturbate_asdf", placeHolderAwarePp.fillInPlaceHolders(input, createPostProcessingContext(rec, null, config)));
|
assertEquals("asdf_Chaturbate_asdf", placeHolderAwarePp.fillInPlaceHolders(input, createPostProcessingContext(rec, null, config)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +137,7 @@ class AbstractPlaceholderAwarePostProcessorTest extends AbstractPpTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testPlaceholderDefaultValues() {
|
void testPlaceholderDefaultValues() {
|
||||||
String input = "asdf_$orElse(${modelGroupName},$orElse(${modelSanitizedName},anonymous))_asdf";
|
String input = "asdf_$orElse(${modelGroupName},$orElse($sanitize(${modelName}),anonymous))_asdf";
|
||||||
PostProcessingContext ctx = createPostProcessingContext(rec, null, config);
|
PostProcessingContext ctx = createPostProcessingContext(rec, null, config);
|
||||||
ctx.getRecording().getModel().setName(null);
|
ctx.getRecording().getModel().setName(null);
|
||||||
assertEquals("asdf_anonymous_asdf", placeHolderAwarePp.fillInPlaceHolders(input, ctx));
|
assertEquals("asdf_anonymous_asdf", placeHolderAwarePp.fillInPlaceHolders(input, ctx));
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
package ctbrec.recorder.postprocessing;
|
package ctbrec.recorder.postprocessing;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import ctbrec.Config;
|
||||||
import static org.mockito.ArgumentMatchers.*;
|
import ctbrec.Model;
|
||||||
import static org.mockito.Mockito.*;
|
import ctbrec.Recording;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import ctbrec.Config;
|
import static org.mockito.Mockito.*;
|
||||||
import ctbrec.Model;
|
|
||||||
import ctbrec.Recording;
|
|
||||||
|
|
||||||
class RenameDirectoryTest extends AbstractPpTest {
|
class RenameDirectoryTest extends AbstractPpTest {
|
||||||
|
|
||||||
|
@ -29,7 +28,7 @@ class RenameDirectoryTest extends AbstractPpTest {
|
||||||
pp.postprocess(createPostProcessingContext(rec, recordingManager, config));
|
pp.postprocess(createPostProcessingContext(rec, recordingManager, config));
|
||||||
|
|
||||||
Matcher m = Pattern.compile("Mockita_Boobilicious_\\d{4}-\\d{2}-\\d{2}_\\d{2}-\\d{2}-\\d{2}").matcher(rec.getAbsoluteFile().getName());
|
Matcher m = Pattern.compile("Mockita_Boobilicious_\\d{4}-\\d{2}-\\d{2}_\\d{2}-\\d{2}-\\d{2}").matcher(rec.getAbsoluteFile().getName());
|
||||||
assertTrue(m.matches());
|
assertTrue(m.matches(), () -> rec.getAbsoluteFile().getName() + " does not match");
|
||||||
assertEquals(rec.getAbsoluteFile(), rec.getPostProcessedFile());
|
assertEquals(rec.getAbsoluteFile(), rec.getPostProcessedFile());
|
||||||
assertNotEquals(rec.getAbsoluteFile(), original);
|
assertNotEquals(rec.getAbsoluteFile(), original);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
package ctbrec.variableexpansion.functions;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class CapitalizeTest {
|
||||||
|
Capitalize capitalize = new Capitalize();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNullParams() {
|
||||||
|
assertEquals("", capitalize.apply((Object[]) null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testEmptyParams() {
|
||||||
|
assertEquals("", capitalize.apply());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNormalStrings() {
|
||||||
|
assertEquals("Hello World", capitalize.apply("hello world"));
|
||||||
|
assertEquals("HElLo WoRlD", capitalize.apply("hElLo woRlD"));
|
||||||
|
assertEquals("HELLO WORLD", capitalize.apply("HELLO WORLD"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSpecialCharacters() {
|
||||||
|
assertEquals("He{LLo Wo\n$rlD", capitalize.apply("he{LLo wo\n$rlD"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package ctbrec.variableexpansion.functions;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class FormatTest {
|
||||||
|
|
||||||
|
Format format = new Format();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNullParams() {
|
||||||
|
assertEquals("", format.apply((Object[]) null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testEmptyParams() {
|
||||||
|
assertEquals("", format.apply());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testZonedDateTimeParameter() {
|
||||||
|
String expected = "2023-02-26_17-11-23";
|
||||||
|
String zdt = "2023-02-26T17:11:23.363270467+01:00[Europe/Berlin]";
|
||||||
|
assertEquals(expected, format.apply(ZonedDateTime.parse(zdt)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testStringParameter() {
|
||||||
|
String expected = "2023-02-26_17-11-23";
|
||||||
|
String zdt = "2023-02-26T17:11:23.363270467+01:00[Europe/Berlin]";
|
||||||
|
assertEquals(expected, format.apply(zdt));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testInvalidStringParameter() {
|
||||||
|
assertEquals("", format.apply("bullshit"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testIStringParameterWithPattern() {
|
||||||
|
String expected = "20230226-171123";
|
||||||
|
String zdt = "2023-02-26T17:11:23.363270467+01:00[Europe/Berlin]";
|
||||||
|
assertEquals(expected, format.apply(zdt, "yyyyMMdd-HHmmss"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testIStringParameterWithInvalidPattern() {
|
||||||
|
String expected = "";
|
||||||
|
String zdt = "2023-02-26T17:11:23.363270467+01:00[Europe/Berlin]";
|
||||||
|
assertEquals(expected, format.apply(zdt, "asdft"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package ctbrec.variableexpansion.functions;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class LowerTest {
|
||||||
|
Lower lower = new Lower();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNullParams() {
|
||||||
|
assertEquals("", lower.apply((Object[]) null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testEmptyParams() {
|
||||||
|
assertEquals("", lower.apply());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNormalStrings() {
|
||||||
|
assertEquals("hello world", lower.apply("hello world"));
|
||||||
|
assertEquals("hello world", lower.apply("hElLo woRlD"));
|
||||||
|
assertEquals("hello world", lower.apply("HELLO WORLD"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSpecialCharacters() {
|
||||||
|
assertEquals("he{llo\nwo$rld", lower.apply("he{LLo\nwo$rlD"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package ctbrec.variableexpansion.functions;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class OrElseTest {
|
||||||
|
OrElse orElse = new OrElse();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNullParams() {
|
||||||
|
assertEquals("", orElse.apply((Object[]) null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testEmptyParams() {
|
||||||
|
assertEquals("", orElse.apply());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testHappyPath() {
|
||||||
|
assertEquals("world", orElse.apply("world", "hello"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFallback() {
|
||||||
|
assertEquals("hello", orElse.apply("", "hello"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package ctbrec.variableexpansion.functions;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class SanitizeTest {
|
||||||
|
Sanitize sanitize = new Sanitize();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNullParams() {
|
||||||
|
assertEquals("", sanitize.apply((Object[]) null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testEmptyParams() {
|
||||||
|
assertEquals("", sanitize.apply());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNormalStrings() {
|
||||||
|
assertEquals("hel!lo", sanitize.apply("hel!lo"));
|
||||||
|
assertEquals("hE$lLo", sanitize.apply("hE$lLo"));
|
||||||
|
assertEquals("HELL%O", sanitize.apply("HELL%O"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSpecialCharacters() {
|
||||||
|
assertEquals("asdf_asdf", sanitize.apply("asdf asdf"));
|
||||||
|
assertEquals("asdf_asdf", sanitize.apply("asdf\"asdf"));
|
||||||
|
assertEquals("asdf_asdf", sanitize.apply("asdf'asdf"));
|
||||||
|
assertEquals("asdf_asdf", sanitize.apply("asdf\\asdf"));
|
||||||
|
assertEquals("asdf_asdf", sanitize.apply("asdf/asdf"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package ctbrec.variableexpansion.functions;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class TrimTest {
|
||||||
|
Trim trim = new Trim();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNullParams() {
|
||||||
|
assertEquals("", trim.apply((Object[]) null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testEmptyParams() {
|
||||||
|
assertEquals("", trim.apply());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNormalStrings() {
|
||||||
|
assertEquals("hello", trim.apply("hello"));
|
||||||
|
assertEquals("hello", trim.apply(" hello "));
|
||||||
|
assertEquals("hello", trim.apply(" hello "));
|
||||||
|
assertEquals("hello", trim.apply("\thello\t"));
|
||||||
|
assertEquals("hello", trim.apply("\nhello\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSpecialCharacters() {
|
||||||
|
assertEquals("he{LLo\nwo $rlD", trim.apply("he{LLo\nwo $rlD"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package ctbrec.variableexpansion.functions;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class UpperTest {
|
||||||
|
Upper upper = new Upper();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNullParams() {
|
||||||
|
assertEquals("", upper.apply((Object[]) null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testEmptyParams() {
|
||||||
|
assertEquals("", upper.apply());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testNormalStrings() {
|
||||||
|
assertEquals("HELLO WORLD", upper.apply("hello world"));
|
||||||
|
assertEquals("HELLO WORLD", upper.apply("hElLo woRlD"));
|
||||||
|
assertEquals("HELLO WORLD", upper.apply("HELLO WORLD"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSpecialCharacters() {
|
||||||
|
assertEquals("HE{LLO\nWO$RLD", upper.apply("he{LLo\nwo$rlD"));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue