From d35ebfef74650f7e6b84ed121b275c93e5377ada Mon Sep 17 00:00:00 2001 From: George Tay Date: Mon, 19 Feb 2024 12:29:20 +0800 Subject: [PATCH 1/5] Implement cloneable RepoConfiguration and CliArguments --- src/main/java/reposense/model/Author.java | 11 +++++- .../reposense/model/AuthorConfiguration.java | 32 +++++++++++++++- .../java/reposense/model/CliArguments.java | 37 ++++++++++++++++++- src/main/java/reposense/model/CommitHash.java | 13 ++++++- src/main/java/reposense/model/FileType.java | 19 +++++++++- .../java/reposense/model/FileTypeManager.java | 13 ++++++- .../reposense/model/RepoConfiguration.java | 37 +++++++++++++++---- .../java/reposense/model/RepoLocation.java | 16 +++++++- .../reposense/model/ReportConfiguration.java | 7 +++- 9 files changed, 168 insertions(+), 17 deletions(-) diff --git a/src/main/java/reposense/model/Author.java b/src/main/java/reposense/model/Author.java index c790a01f72..faafabbcdb 100644 --- a/src/main/java/reposense/model/Author.java +++ b/src/main/java/reposense/model/Author.java @@ -10,7 +10,7 @@ /** * Represents a Git Author. */ -public class Author { +public class Author implements Cloneable { public static final String NAME_NO_AUTHOR_WITH_COMMITS_FOUND = "NO AUTHOR WITH COMMITS FOUND WITHIN THIS PERIOD OF TIME"; private static final String UNKNOWN_AUTHOR_GIT_ID = "-"; @@ -208,5 +208,14 @@ private void addStandardGitHostEmails(List emails) { emails.add(standardGitLabEmail); } } + + @Override + public Author clone() throws CloneNotSupportedException { + Author clone = (Author) super.clone(); + clone.emails = new ArrayList<>(this.emails); + clone.authorAliases = new ArrayList<>(this.authorAliases); + clone.ignoreGlobList = new ArrayList<>(this.ignoreGlobList); + return clone; + } } diff --git a/src/main/java/reposense/model/AuthorConfiguration.java b/src/main/java/reposense/model/AuthorConfiguration.java index bb2bdf85db..eff536aa46 100644 --- a/src/main/java/reposense/model/AuthorConfiguration.java +++ b/src/main/java/reposense/model/AuthorConfiguration.java @@ -13,7 +13,7 @@ /** * Represents author configuration information from CSV config file for a single repository. */ -public class AuthorConfiguration { +public class AuthorConfiguration implements Cloneable { public static final String DEFAULT_BRANCH = "HEAD"; public static final boolean DEFAULT_HAS_AUTHOR_CONFIG_FILE = false; private static final Logger logger = LogsManager.getLogger(AuthorConfiguration.class); @@ -336,4 +336,34 @@ public void setHasAuthorConfigFile(boolean hasAuthorConfigFile) { public boolean hasAuthorConfigFile() { return hasAuthorConfigFile; } + + /** + * Creates an identical deep copy of this {@code AuthorConfiguration} object. + * + * @return Deep copy of this {@code AuthorConfiguration} object. + * @throws CloneNotSupportedException if the cloning operation fails. + */ + @Override + public AuthorConfiguration clone() throws CloneNotSupportedException { + AuthorConfiguration clone = (AuthorConfiguration) super.clone(); + clone.location = this.location.clone(); + clone.authorList = new ArrayList<>(this.authorList); + clone.authorNamesToAuthorMap = new HashMap<>(); + clone.authorEmailsToAuthorMap = new HashMap<>(); + clone.authorDisplayNameMap = new HashMap<>(); + + for (Map.Entry entry : this.authorNamesToAuthorMap.entrySet()) { + clone.authorNamesToAuthorMap.put(entry.getKey(), entry.getValue().clone()); + } + + for (Map.Entry entry : this.authorEmailsToAuthorMap.entrySet()) { + clone.authorEmailsToAuthorMap.put(entry.getKey(), entry.getValue().clone()); + } + + for (Map.Entry entry : this.authorDisplayNameMap.entrySet()) { + clone.authorDisplayNameMap.put(entry.getKey().clone(), entry.getValue()); + } + + return clone; + } } diff --git a/src/main/java/reposense/model/CliArguments.java b/src/main/java/reposense/model/CliArguments.java index 18ca162285..aab89c7264 100644 --- a/src/main/java/reposense/model/CliArguments.java +++ b/src/main/java/reposense/model/CliArguments.java @@ -4,11 +4,13 @@ import java.nio.file.Paths; import java.time.LocalDateTime; import java.time.ZoneId; +import java.util.ArrayList; import java.util.List; import java.util.Objects; import reposense.parser.ArgsParser; import reposense.parser.AuthorConfigCsvParser; +import reposense.parser.ConfigurationBuildException; import reposense.parser.GroupConfigCsvParser; import reposense.parser.RepoConfigCsvParser; import reposense.parser.ReportConfigJsonParser; @@ -16,7 +18,7 @@ /** * Represents command line arguments user supplied when running the program. */ -public class CliArguments { +public class CliArguments implements Cloneable { private static final Path EMPTY_PATH = Paths.get(""); private final Path outputFilePath; @@ -25,7 +27,7 @@ public class CliArguments { private final LocalDateTime untilDate; private final boolean isSinceDateProvided; private final boolean isUntilDateProvided; - private final List formats; + private List formats; private final boolean isLastModifiedDateIncluded; private final boolean isShallowCloningPerformed; private final boolean isAutomaticallyLaunching; @@ -229,6 +231,36 @@ public boolean equals(Object other) { && Objects.equals(this.reportConfigFilePath, otherCliArguments.reportConfigFilePath); } + /** + * Clones the current instance of {@code CliArguments}. This method will only explicitly clone + * objects that are not immutable (e.g. List, Lists of mutable objects, other ordinary mutable + * objects). + * + * @return {@code CliArguments} instance that is semantically identical to this instance of + * {@code CliArguments}. + * @throws CloneNotSupportedException if cloning for certain objects is not permitted. + */ + @Override + public CliArguments clone() throws CloneNotSupportedException { + CliArguments clone = (CliArguments) super.clone(); + + // clone the formats, each FileType object needs to be individually cloned + clone.formats = new ArrayList<>(); + for (FileType ft : this.formats) { + clone.formats.add(ft.clone()); + } + + // clone the string list; its ok to do a shallow copy since strings are mutable + clone.locations = this.locations == null ? clone.locations : new ArrayList<>(this.locations); + + // clone the report config; use the clone method to clone the object for us + clone.reportConfiguration = this.reportConfiguration == null + ? clone.reportConfiguration + : this.reportConfiguration.clone(); + + return clone; + } + /** * Builder used to build CliArguments. */ @@ -506,6 +538,7 @@ public Builder reportConfiguration(ReportConfiguration reportConfiguration) { * Builds CliArguments. * * @return CliArguments + * @throws ConfigurationBuildException if the object cannot be cloned */ public CliArguments build() { return new CliArguments(this); diff --git a/src/main/java/reposense/model/CommitHash.java b/src/main/java/reposense/model/CommitHash.java index f5035f2177..5dd1c24858 100644 --- a/src/main/java/reposense/model/CommitHash.java +++ b/src/main/java/reposense/model/CommitHash.java @@ -10,7 +10,7 @@ /** * Represents a git commit hash in {@code RepoConfiguration}. */ -public class CommitHash { +public class CommitHash implements Cloneable { private static final String COMMIT_HASH_REGEX = "^[0-9a-f]+$"; private static final String COMMIT_RANGED_HASH_REGEX = "^[0-9a-f]+\\.\\.[0-9a-f]+$"; private static final String INVALID_COMMIT_HASH_MESSAGE = @@ -109,5 +109,16 @@ private static void validateCommit(String commitHash) throws IllegalArgumentExce throw new IllegalArgumentException(String.format(INVALID_COMMIT_HASH_MESSAGE, commitHash)); } } + + /** + * Creates a deep copy of this {@code CommitHash} object. + * + * @return Deep copy of this {@code CommitHash} object. + * @throws CloneNotSupportedException if the cloning operation fails. + */ + @Override + public CommitHash clone() throws CloneNotSupportedException { + return (CommitHash) super.clone(); + } } diff --git a/src/main/java/reposense/model/FileType.java b/src/main/java/reposense/model/FileType.java index 453ef18d4c..71e315f89c 100644 --- a/src/main/java/reposense/model/FileType.java +++ b/src/main/java/reposense/model/FileType.java @@ -4,6 +4,7 @@ import java.nio.file.FileSystems; import java.nio.file.PathMatcher; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.regex.Pattern; @@ -17,7 +18,7 @@ /** * Represents a file type for use in {@link FileTypeManager}. */ -public class FileType { +public class FileType implements Cloneable { private static final Pattern FILE_FORMAT_VALIDATION_PATTERN = Pattern.compile("^\\w+$"); private static final String MESSAGE_ILLEGAL_FILE_FORMAT = "The provided file format, %s, contains illegal " + "characters."; @@ -110,6 +111,22 @@ public boolean equals(Object other) { return this.label.equals(otherFileType.label) && this.paths.equals(otherFileType.paths); } + /** + * Clones this instance of {@code FileType}. + * + * @return New instance of {@code FileType} that is semantically similar to this instance + * of {@code FileType}. + * @throws CloneNotSupportedException if there are any objects that cannot be cloned. + */ + @Override + public FileType clone() throws CloneNotSupportedException { + // it suffices to do a shallow clone since strings are immutable + FileType clone = (FileType) super.clone(); + clone.paths = new ArrayList<>(this.paths); + + return clone; + } + /** * Overrides the Gson serializer to serialize only the label of each file type instead on the entire object. */ diff --git a/src/main/java/reposense/model/FileTypeManager.java b/src/main/java/reposense/model/FileTypeManager.java index 997c8c7b2f..212c49705a 100644 --- a/src/main/java/reposense/model/FileTypeManager.java +++ b/src/main/java/reposense/model/FileTypeManager.java @@ -8,7 +8,7 @@ * Holds a list of whitelisted formats and user-specified custom * groupings. */ -public class FileTypeManager { +public class FileTypeManager implements Cloneable { private static final String DEFAULT_GROUP = "other"; private static final FileType DEFAULT_GROUP_TYPE = new FileType(DEFAULT_GROUP, Collections.singletonList("**")); @@ -126,4 +126,15 @@ public boolean equals(Object other) { FileTypeManager otherFileType = (FileTypeManager) other; return this.groups.equals(otherFileType.groups) && this.formats.equals(otherFileType.formats); } + + @Override + public FileTypeManager clone() throws CloneNotSupportedException { + FileTypeManager clone = (FileTypeManager) super.clone(); + + // do a shallow copy since strings are immutable + clone.formats = new ArrayList<>(this.formats); + clone.groups = new ArrayList<>(this.groups); + + return clone; + } } diff --git a/src/main/java/reposense/model/RepoConfiguration.java b/src/main/java/reposense/model/RepoConfiguration.java index 8466cfb52b..784a941e85 100644 --- a/src/main/java/reposense/model/RepoConfiguration.java +++ b/src/main/java/reposense/model/RepoConfiguration.java @@ -20,7 +20,7 @@ /** * Represents configuration information from CSV config file for a single repository. */ -public class RepoConfiguration { +public class RepoConfiguration implements Cloneable { public static final String DEFAULT_BRANCH = "HEAD"; public static final String DEFAULT_EXTRA_OUTPUT_FOLDER_NAME = ""; public static final long DEFAULT_FILE_SIZE_LIMIT = 500000; @@ -60,6 +60,29 @@ public class RepoConfiguration { */ private RepoConfiguration() {} + /** + * Creates a deep copy of this {@code RepoConfiguration} object. + * + * @return Deep copy of this {@code RepoConfiguration} object. + * @throws CloneNotSupportedException if the cloning operation fails. + */ + @Override + public RepoConfiguration clone() throws CloneNotSupportedException { + RepoConfiguration clone = (RepoConfiguration) super.clone(); + clone.location = this.location == null ? clone.location : this.location.clone(); + clone.fileTypeManager = this.fileTypeManager == null ? clone.fileTypeManager : this.fileTypeManager.clone(); + clone.ignoreGlobList = new ArrayList<>(this.ignoreGlobList); + clone.ignoredAuthorsList = new ArrayList<>(this.ignoredAuthorsList); + clone.authorConfig = this.authorConfig == null ? clone.authorConfig : this.authorConfig.clone(); + clone.ignoreCommitList = new ArrayList<>(); + + for (CommitHash hash : this.ignoreCommitList) { + clone.ignoreCommitList.add(hash.clone()); + } + + return clone; + } + /** * Builds the necessary configurations for RepoConfiguration. * Obeys the Builder pattern as described in {@link CliArguments}. @@ -426,13 +449,11 @@ public RepoConfiguration build() { this.processNames(); // save a reference to the current built object - RepoConfiguration toReturn = this.repoConfiguration; - - // reset the internal reference to avoid aliasing - this.repoConfiguration = new RepoConfiguration(); - - // return the reference to the built RepoConfiguration object - return toReturn; + try { + return this.repoConfiguration.clone(); + } catch (CloneNotSupportedException ex) { + throw new ConfigurationBuildException(); + } } /** diff --git a/src/main/java/reposense/model/RepoLocation.java b/src/main/java/reposense/model/RepoLocation.java index 2472f32af5..92e026edab 100644 --- a/src/main/java/reposense/model/RepoLocation.java +++ b/src/main/java/reposense/model/RepoLocation.java @@ -20,7 +20,7 @@ /** * Represents a repository location. */ -public class RepoLocation { +public class RepoLocation implements Cloneable { protected static final String UNSUPPORTED_DOMAIN_NAME = "NOT_RECOGNIZED"; private static final String MESSAGE_INVALID_LOCATION = "%s is an invalid location."; @@ -269,4 +269,18 @@ public boolean equals(Object other) { public int hashCode() { return location.hashCode(); } + + /** + * Clones this {@code RepoLocation} instance. + * + * @return A new instance of {@code RepoLocation} that is sematically identical to this + * {@code RepoLocation} instance. + * @throws CloneNotSupportedException if there is a problem when cloning this + * {@code RepoLocation} object. + */ + @Override + public RepoLocation clone() throws CloneNotSupportedException { + // regular shallow clone will suffice since strings are immutable + return (RepoLocation) super.clone(); + } } diff --git a/src/main/java/reposense/model/ReportConfiguration.java b/src/main/java/reposense/model/ReportConfiguration.java index 4f6e166c34..c5ac2e5060 100644 --- a/src/main/java/reposense/model/ReportConfiguration.java +++ b/src/main/java/reposense/model/ReportConfiguration.java @@ -3,11 +3,16 @@ /** * Represents configuration information from JSON config file for generated report. */ -public class ReportConfiguration { +public class ReportConfiguration implements Cloneable { private static final String DEFAULT_TITLE = "RepoSense Report"; private String title; public String getTitle() { return (title == null) ? DEFAULT_TITLE : title; } + + @Override + public ReportConfiguration clone() throws CloneNotSupportedException { + return (ReportConfiguration) super.clone(); + } } From cf58b9ad0edc47b4d735d1ea756b94123d66607e Mon Sep 17 00:00:00 2001 From: George Tay Date: Mon, 19 Feb 2024 12:29:37 +0800 Subject: [PATCH 2/5] Implement unit test cases --- src/test/java/reposense/model/AuthorTest.java | 6 ++++++ src/test/java/reposense/model/CommitHashTest.java | 6 ++++++ src/test/java/reposense/model/FileTypeManagerTest.java | 7 +++++++ src/test/java/reposense/model/FileTypeTest.java | 6 ++++++ .../java/reposense/model/RepoConfigurationTest.java | 10 ++++++++++ src/test/java/reposense/model/RepoLocationTest.java | 6 ++++++ 6 files changed, 41 insertions(+) diff --git a/src/test/java/reposense/model/AuthorTest.java b/src/test/java/reposense/model/AuthorTest.java index 25faa1efbb..0676f22ce2 100644 --- a/src/test/java/reposense/model/AuthorTest.java +++ b/src/test/java/reposense/model/AuthorTest.java @@ -108,4 +108,10 @@ public void appendIgnoreGlobList_appendOrOperator_throwIllegalArgumentException( Assertions.assertThrows(IllegalArgumentException.class, () -> author.importIgnoreGlobList( Arrays.asList(ignoreGlobs))); } + + @Test + public void author_cloneAuthor_success() throws Exception { + Author author = new Author("Try"); + Assertions.assertNotSame(author.clone(), author.clone()); + } } diff --git a/src/test/java/reposense/model/CommitHashTest.java b/src/test/java/reposense/model/CommitHashTest.java index aefc0b6221..20bd0aebeb 100644 --- a/src/test/java/reposense/model/CommitHashTest.java +++ b/src/test/java/reposense/model/CommitHashTest.java @@ -23,4 +23,10 @@ public void validateCommits_nonAlphanumeric_throwIllegalArgumentException() { Assertions.assertThrows(IllegalArgumentException.class, () -> CommitHash.validateCommits( Arrays.asList("!d0ac2ee20f04dce8df0591caed460gffacb65a4"))); } + + @Test + public void commitHash_cloneCommitHash_success() throws Exception { + CommitHash hash = new CommitHash("8d0ac2ee20f04dce8df0591caed460bffacb65a4"); + Assertions.assertNotSame(hash.clone(), hash.clone()); + } } diff --git a/src/test/java/reposense/model/FileTypeManagerTest.java b/src/test/java/reposense/model/FileTypeManagerTest.java index f3537664e3..6d67f4bfa7 100644 --- a/src/test/java/reposense/model/FileTypeManagerTest.java +++ b/src/test/java/reposense/model/FileTypeManagerTest.java @@ -49,4 +49,11 @@ public void isInsideFormatsWhiteList_notWhitelistedFormat_success() { fileTypeManager.setFormats(FileTypeTest.DEFAULT_TEST_FORMATS); Assertions.assertFalse(fileTypeManager.isInsideWhitelistedFormats("test.cpp")); } + + @Test + public void fileTypeManager_cloneFileTypeManager_success() throws Exception { + Assertions.assertNotSame( + this.fileTypeManager.clone(), this.fileTypeManager.clone() + ); + } } diff --git a/src/test/java/reposense/model/FileTypeTest.java b/src/test/java/reposense/model/FileTypeTest.java index e41fafe85b..0c9fb91a1c 100644 --- a/src/test/java/reposense/model/FileTypeTest.java +++ b/src/test/java/reposense/model/FileTypeTest.java @@ -60,4 +60,10 @@ public void isFileGlobMatching_nonMatchingGroup_success() { FileType fileType = new FileType("test", Collections.singletonList("**/test/*")); Assertions.assertFalse(fileType.isFileGlobMatching("test/main.java")); } + + @Test + public void fileType_cloneFileType_success() throws Exception { + FileType fileType = new FileType("test", Collections.singletonList("**/test/*")); + Assertions.assertNotSame(fileType.clone(), fileType.clone()); + } } diff --git a/src/test/java/reposense/model/RepoConfigurationTest.java b/src/test/java/reposense/model/RepoConfigurationTest.java index 60b2892660..9078dcffce 100644 --- a/src/test/java/reposense/model/RepoConfigurationTest.java +++ b/src/test/java/reposense/model/RepoConfigurationTest.java @@ -960,4 +960,14 @@ public void repoBuilder_isLastModifiedDateIncluded_success() throws Exception { public void repoBuilder_buildWithInvalid_failure() { Assertions.assertThrows(ConfigurationBuildException.class, () -> new RepoConfiguration.Builder().build()); } + + @Test + public void repoBuilder_cloneRepoConfiguration_success() throws Exception { + RepoConfiguration.Builder actualConfig = new RepoConfiguration.Builder() + .isLastModifiedDateIncluded(true) + .location(new RepoLocation(TEST_REPO_MINIMAL_STANDALONE_CONFIG)) + .branch("master"); + + Assertions.assertNotSame(actualConfig.build(), actualConfig.build()); + } } diff --git a/src/test/java/reposense/model/RepoLocationTest.java b/src/test/java/reposense/model/RepoLocationTest.java index 11a9980b69..968e4c36a8 100644 --- a/src/test/java/reposense/model/RepoLocationTest.java +++ b/src/test/java/reposense/model/RepoLocationTest.java @@ -190,6 +190,12 @@ public void getDomainNameFromMatcher_parseInvalidDomain_throwsInvalidLocationExc AssertUtil.assertThrows(InvalidLocationException.class, () -> getDomainNameFromDomain("github.")); } + @Test + public void repoLocation_cloneRepoLocation_success() throws Exception { + RepoLocation location = new RepoLocation(LOCAL_REPO_VALID_WITH_DOT_GIT_TWO); + Assertions.assertNotSame(location.clone(), location.clone()); + } + /** * Compares the information of {@code rawLocation} parsed by the RepoLocation model with {@code expectedRepoName} * and {@code expectedOrganization}. From 1a82daef5f108c67725bb8c1af16f3dbb89eb1ff Mon Sep 17 00:00:00 2001 From: George Tay Date: Thu, 22 Feb 2024 23:37:14 +0800 Subject: [PATCH 3/5] Add new test case --- .../model/RepoConfigurationTest.java | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/test/java/reposense/model/RepoConfigurationTest.java b/src/test/java/reposense/model/RepoConfigurationTest.java index 9078dcffce..2dfda1402e 100644 --- a/src/test/java/reposense/model/RepoConfigurationTest.java +++ b/src/test/java/reposense/model/RepoConfigurationTest.java @@ -962,7 +962,7 @@ public void repoBuilder_buildWithInvalid_failure() { } @Test - public void repoBuilder_cloneRepoConfiguration_success() throws Exception { + public void repoBuilder_cloneRepoConfigurationBuild_success() throws Exception { RepoConfiguration.Builder actualConfig = new RepoConfiguration.Builder() .isLastModifiedDateIncluded(true) .location(new RepoLocation(TEST_REPO_MINIMAL_STANDALONE_CONFIG)) @@ -970,4 +970,48 @@ public void repoBuilder_cloneRepoConfiguration_success() throws Exception { Assertions.assertNotSame(actualConfig.build(), actualConfig.build()); } + + @Test + public void repoBuilder_cloneRepoConfigurationDirectly_success() throws Exception { + RepoConfiguration config = new RepoConfiguration.Builder() + .isLastModifiedDateIncluded(true) + .location(new RepoLocation(TEST_REPO_MINIMAL_STANDALONE_CONFIG)) + .branch("master") + .build(); + + Assertions.assertNotSame(config, config.clone()); + } + + @Test + public void repoBuilder_cloneRepoConfigurationDeeply_success() throws Exception { + List ignoreGlobList = Arrays.asList("test"); + List ignoredAuthorsList = Arrays.asList("test_author"); + List ignoreCommitList = Arrays.asList( + new CommitHash("8d0ac2ee20f04dce8df0591caed460bffacb65a4"), + new CommitHash("8d0ac2ee20f04dce8df0591caed460bffacb65a4") + ); + RepoLocation location = new RepoLocation("location"); + List fileTypeManager = Arrays.asList(new FileType("docx", Arrays.asList("path"))); + AuthorConfiguration authorConfiguration = new AuthorConfiguration( + location + ); + + RepoConfiguration config = new RepoConfiguration.Builder() + .location(location) + .ignoreGlobList(ignoreGlobList) + .ignoredAuthorsList(ignoredAuthorsList) + .ignoreCommitList(ignoreCommitList) + .fileTypeManager(fileTypeManager) + .authorConfig(authorConfiguration) + .build(); + + RepoConfiguration clonedConfig = config.clone(); + + Assertions.assertNotSame(config.getLocation(), clonedConfig.getLocation()); + Assertions.assertNotSame(config.getIgnoreGlobList(), clonedConfig.getIgnoreGlobList()); + Assertions.assertNotSame(config.getIgnoredAuthorsList(), clonedConfig.getIgnoredAuthorsList()); + Assertions.assertNotSame(config.getIgnoreCommitList(), clonedConfig.getIgnoreCommitList()); + Assertions.assertNotSame(config.getFileTypeManager(), clonedConfig.getFileTypeManager()); + Assertions.assertNotSame(config.getAuthorConfig(), clonedConfig.getAuthorConfig()); + } } From af2816c8b946a2b4e736ac42b7e35baeb05c9b65 Mon Sep 17 00:00:00 2001 From: George Tay Date: Fri, 23 Feb 2024 12:16:43 +0800 Subject: [PATCH 4/5] Add new unit test cases --- .../reposense/model/AuthorConfiguration.java | 6 +- .../reposense/model/RepoConfiguration.java | 2 +- .../model/AuthorConfigurationTest.java | 94 +++++++++++++++++++ src/test/java/reposense/model/AuthorTest.java | 2 +- .../reposense/model/CliArgumentsTest.java | 35 +++++++ .../java/reposense/model/FileTypeTest.java | 2 +- .../model/ReportConfigurationTest.java | 12 +++ 7 files changed, 149 insertions(+), 4 deletions(-) create mode 100644 src/test/java/reposense/model/AuthorConfigurationTest.java create mode 100644 src/test/java/reposense/model/CliArgumentsTest.java create mode 100644 src/test/java/reposense/model/ReportConfigurationTest.java diff --git a/src/main/java/reposense/model/AuthorConfiguration.java b/src/main/java/reposense/model/AuthorConfiguration.java index eff536aa46..9f4380ac25 100644 --- a/src/main/java/reposense/model/AuthorConfiguration.java +++ b/src/main/java/reposense/model/AuthorConfiguration.java @@ -347,10 +347,14 @@ public boolean hasAuthorConfigFile() { public AuthorConfiguration clone() throws CloneNotSupportedException { AuthorConfiguration clone = (AuthorConfiguration) super.clone(); clone.location = this.location.clone(); - clone.authorList = new ArrayList<>(this.authorList); clone.authorNamesToAuthorMap = new HashMap<>(); clone.authorEmailsToAuthorMap = new HashMap<>(); clone.authorDisplayNameMap = new HashMap<>(); + clone.authorList = new ArrayList<>(); + + for (Author a : this.authorList) { + clone.authorList.add(a.clone()); + } for (Map.Entry entry : this.authorNamesToAuthorMap.entrySet()) { clone.authorNamesToAuthorMap.put(entry.getKey(), entry.getValue().clone()); diff --git a/src/main/java/reposense/model/RepoConfiguration.java b/src/main/java/reposense/model/RepoConfiguration.java index 784a941e85..77fa29f40c 100644 --- a/src/main/java/reposense/model/RepoConfiguration.java +++ b/src/main/java/reposense/model/RepoConfiguration.java @@ -91,7 +91,7 @@ public static class Builder { private String displayName; private String outputFolderName; private String repoFolderName; - private RepoConfiguration repoConfiguration; + private final RepoConfiguration repoConfiguration; /** * Returns an empty instance of the RepoConfiguration Builder. diff --git a/src/test/java/reposense/model/AuthorConfigurationTest.java b/src/test/java/reposense/model/AuthorConfigurationTest.java new file mode 100644 index 0000000000..ad77578523 --- /dev/null +++ b/src/test/java/reposense/model/AuthorConfigurationTest.java @@ -0,0 +1,94 @@ +package reposense.model; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import reposense.parser.exceptions.InvalidLocationException; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class AuthorConfigurationTest { + private static final List authorNames = Arrays.asList("fake", "fake2", "fake3"); + private static final List aliases = Arrays.asList("hello", "world", "!!!"); + private static final List emails = Arrays.asList("author1@email.com", "author2@email.com", "author3@email.com"); + private static final List displayNames = Arrays.asList("author1", "author2", "author3"); + + @Test + public void clone_cloneAuthorConfiguration_success() throws Exception { + AuthorConfiguration authorConfiguration = this.setUpAuthorConfiguration(); + AuthorConfiguration clonedAuthorConfiguration = authorConfiguration.clone(); + + // test for object equality deeply + List originalAuthorList = authorConfiguration.getAuthorList(); + List clonedAuthorList = clonedAuthorConfiguration.getAuthorList(); + Map authorNameToAuthorOriginal = authorConfiguration.getAuthorNamesToAuthorMap(); + Map authorNameToAuthorOther = clonedAuthorConfiguration.getAuthorNamesToAuthorMap(); + Map authorEmailToAuthorOriginal = authorConfiguration.getAuthorEmailsToAuthorMap(); + Map authorEmailToAuthorOther = clonedAuthorConfiguration.getAuthorEmailsToAuthorMap(); + Map authorDisplayAuthorOriginal = authorConfiguration.getAuthorDisplayNameMap(); + Map authorDisplayAuthorOther = clonedAuthorConfiguration.getAuthorDisplayNameMap(); + + Assertions.assertNotSame(originalAuthorList, clonedAuthorList); + Assertions.assertNotSame(authorConfiguration.getLocation(), clonedAuthorConfiguration.getLocation()); + Assertions.assertNotSame(authorNameToAuthorOriginal, authorNameToAuthorOther); + Assertions.assertNotSame(authorDisplayAuthorOriginal, authorDisplayAuthorOther); + Assertions.assertNotSame(authorEmailToAuthorOriginal, authorEmailToAuthorOther); + + for (int i = 0; i < originalAuthorList.size(); i++) { + Assertions.assertNotSame(originalAuthorList.get(i), clonedAuthorList.get(i)); + } + + for (String s : displayNames) { + Assertions.assertNotSame(authorNameToAuthorOriginal.get(s), authorNameToAuthorOther.get(s)); + } + + for (String s : emails) { + Assertions.assertNotSame(authorEmailToAuthorOriginal.get(s), authorEmailToAuthorOther.get(s)); + } + } + + private List setUpAuthors() { + List authors = new ArrayList<>(); + + for (int i = 0; i < 3; i++) { + Author a = new Author(authorNames.get(i)); + a.setAuthorAliases(Arrays.asList(aliases.get(i))); + a.setEmails(Arrays.asList(emails.get(i))); + a.setDisplayName(displayNames.get(i)); + authors.add(a); + } + + return authors; + } + + private AuthorConfiguration setUpAuthorConfiguration() { + try { + AuthorConfiguration authorConfiguration = new AuthorConfiguration( + new RepoLocation("location"), "branch" + ); + List authors = this.setUpAuthors(); + + Map nametoAuthorMap = new HashMap<>(); + Map emailToAuthorMap = new HashMap<>(); + Map authorToDisplayNameMap = new HashMap<>(); + + for (Author author : authors) { + nametoAuthorMap.put(author.getDisplayName(), author); + emailToAuthorMap.put(author.getEmails().get(0), author); + authorToDisplayNameMap.put(author, author.getDisplayName()); + } + + authorConfiguration.setAuthorList(authors); + authorConfiguration.setAuthorEmailsToAuthorMap(emailToAuthorMap); + authorConfiguration.setAuthorNamesToAuthorMap(nametoAuthorMap); + authorConfiguration.setAuthorDisplayNameMap(authorToDisplayNameMap); + return authorConfiguration; + } catch (InvalidLocationException ex) { + throw new AssertionError("Test failed: Location was invalid and used"); + } + } + +} diff --git a/src/test/java/reposense/model/AuthorTest.java b/src/test/java/reposense/model/AuthorTest.java index 0676f22ce2..b4b0e299f5 100644 --- a/src/test/java/reposense/model/AuthorTest.java +++ b/src/test/java/reposense/model/AuthorTest.java @@ -112,6 +112,6 @@ public void appendIgnoreGlobList_appendOrOperator_throwIllegalArgumentException( @Test public void author_cloneAuthor_success() throws Exception { Author author = new Author("Try"); - Assertions.assertNotSame(author.clone(), author.clone()); + Assertions.assertNotSame(author, author.clone()); } } diff --git a/src/test/java/reposense/model/CliArgumentsTest.java b/src/test/java/reposense/model/CliArgumentsTest.java new file mode 100644 index 0000000000..eaa2e5c067 --- /dev/null +++ b/src/test/java/reposense/model/CliArgumentsTest.java @@ -0,0 +1,35 @@ +package reposense.model; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +public class CliArgumentsTest { + private static final CliArguments TEST_CLI_ARGUMENTS = new CliArguments.Builder() + .formats(Arrays.asList(new FileType("docx", Arrays.asList("path")))) + .locations(Arrays.asList("location", "location2")) + .reportConfiguration(new ReportConfiguration()) + .build(); + @Test + public void clone_cloneCliArguments_success() throws Exception { + Assertions.assertNotSame(CliArgumentsTest.TEST_CLI_ARGUMENTS, CliArgumentsTest.TEST_CLI_ARGUMENTS.clone()); + } + + @Test + public void clone_cloneCliArgumentsDeeply_success() throws Exception { + CliArguments clonedCliArguments = CliArgumentsTest.TEST_CLI_ARGUMENTS.clone(); + List formatsListOriginal = CliArgumentsTest.TEST_CLI_ARGUMENTS.getFormats(); + List formatsListClone = clonedCliArguments.getFormats(); + + for (int i = 0; i < formatsListOriginal.size(); i++) { + Assertions.assertNotSame(formatsListOriginal.get(i), formatsListClone.get(i)); + } + + Assertions.assertNotSame(formatsListOriginal, formatsListClone); + Assertions.assertNotSame(CliArgumentsTest.TEST_CLI_ARGUMENTS.getLocations(), clonedCliArguments.getLocations()); + Assertions.assertNotSame(CliArgumentsTest.TEST_CLI_ARGUMENTS.getReportConfiguration(), + clonedCliArguments.getReportConfiguration()); + } +} diff --git a/src/test/java/reposense/model/FileTypeTest.java b/src/test/java/reposense/model/FileTypeTest.java index 0c9fb91a1c..a0124568cc 100644 --- a/src/test/java/reposense/model/FileTypeTest.java +++ b/src/test/java/reposense/model/FileTypeTest.java @@ -64,6 +64,6 @@ public void isFileGlobMatching_nonMatchingGroup_success() { @Test public void fileType_cloneFileType_success() throws Exception { FileType fileType = new FileType("test", Collections.singletonList("**/test/*")); - Assertions.assertNotSame(fileType.clone(), fileType.clone()); + Assertions.assertNotSame(fileType, fileType.clone()); } } diff --git a/src/test/java/reposense/model/ReportConfigurationTest.java b/src/test/java/reposense/model/ReportConfigurationTest.java new file mode 100644 index 0000000000..0762059534 --- /dev/null +++ b/src/test/java/reposense/model/ReportConfigurationTest.java @@ -0,0 +1,12 @@ +package reposense.model; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ReportConfigurationTest { + @Test + public void clone_cloneReportConfiguration_success() throws Exception { + ReportConfiguration reportConfiguration = new ReportConfiguration(); + Assertions.assertNotSame(reportConfiguration, reportConfiguration.clone()); + } +} From 6b4e668584fba2ecdc040db6c019f0ab5a056c8b Mon Sep 17 00:00:00 2001 From: George Tay Date: Fri, 23 Feb 2024 12:20:38 +0800 Subject: [PATCH 5/5] Fix Checkstyle --- .../reposense/model/AuthorConfigurationTest.java | 13 ++++++++----- src/test/java/reposense/model/CliArgumentsTest.java | 6 +++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/test/java/reposense/model/AuthorConfigurationTest.java b/src/test/java/reposense/model/AuthorConfigurationTest.java index ad77578523..2106b3fe81 100644 --- a/src/test/java/reposense/model/AuthorConfigurationTest.java +++ b/src/test/java/reposense/model/AuthorConfigurationTest.java @@ -1,19 +1,22 @@ package reposense.model; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import reposense.parser.exceptions.InvalidLocationException; - import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import reposense.parser.exceptions.InvalidLocationException; + + public class AuthorConfigurationTest { private static final List authorNames = Arrays.asList("fake", "fake2", "fake3"); private static final List aliases = Arrays.asList("hello", "world", "!!!"); - private static final List emails = Arrays.asList("author1@email.com", "author2@email.com", "author3@email.com"); + private static final List emails = Arrays.asList("author1@email.com", "author2@email.com", + "author3@email.com"); private static final List displayNames = Arrays.asList("author1", "author2", "author3"); @Test diff --git a/src/test/java/reposense/model/CliArgumentsTest.java b/src/test/java/reposense/model/CliArgumentsTest.java index eaa2e5c067..334fec07a4 100644 --- a/src/test/java/reposense/model/CliArgumentsTest.java +++ b/src/test/java/reposense/model/CliArgumentsTest.java @@ -1,11 +1,11 @@ package reposense.model; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - import java.util.Arrays; import java.util.List; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + public class CliArgumentsTest { private static final CliArguments TEST_CLI_ARGUMENTS = new CliArguments.Builder() .formats(Arrays.asList(new FileType("docx", Arrays.asList("path"))))