-
Notifications
You must be signed in to change notification settings - Fork 193
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
Fix #4156 - Support nested subfolders in approved Measure directories #4417
Conversation
…ecause currently it does path.filename() so it's write "docs/subfolder/a.txt" as "a.txt" with usageType = doc, not subfolder/a.txt This requires breaking API, not sure where I do it like this in the Ctor to BCLFileReference or just in BCLFileReference::writeValues
BCLFileReference::BCLFileReference(const openstudio::path& measureRootDir, const openstudio::path& relativePath, const bool setMembers) | ||
: m_measureRootDir(openstudio::filesystem::system_complete(measureRootDir)), | ||
m_path(openstudio::filesystem::system_complete(measureRootDir / relativePath)) { |
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.
API break. I have to somehow track the measureRootDir so I can change filename() below.
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.
This was discussed on Slack, and I agree with @tijcolem that we can probably be a little more tolerant of occasional API changes in BCL, compared to the Model API. I'd recommend merge.
std::string usageType = this->usageType(); | ||
openstudio::path baseDir = m_measureRootDir; | ||
if (usageType == "doc") { | ||
baseDir /= "docs"; | ||
} else if (usageType == "resource") { | ||
baseDir /= "resources"; | ||
} else if (usageType == "test") { | ||
baseDir /= "tests"; | ||
} | ||
|
||
return toString(openstudio::filesystem::relative(m_path, baseDir)); |
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.
Actual required change for API break above
static constexpr std::array<std::pair<std::string_view, std::string_view>, 4> rootToUsageTypeMap{{ | ||
// fileName, usageType | ||
{"measure.rb", "script"}, | ||
{"LICENSE.md", "license"}, | ||
{"README.md", "readme"}, | ||
{"README.md.erb", "readmeerb"} | ||
// ".gitkeep" // assuming .gitkeep outside a subfolder makes zero sense... | ||
// "measure.xml" // not included in itself! | ||
}}; |
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.
Approved rootFiles (and their automatic usageType) are declared in this single location
static constexpr std::array<std::pair<std::string_view, std::string_view>, 3> approvedSubFolderToUsageMap{{ | ||
{"docs", "doc"}, | ||
{"tests", "test"}, | ||
{"resources", "resource"}, | ||
}}; | ||
|
||
static constexpr std::array<std::string_view, 1> ignoredSubFolders{"tests/output"}; |
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.
Approved subfolders (and their automatic usageType) are declared in this single location, along with exclusions (one right now) to ingore the result of running openstudio measure --run-tests .
// TODO: do we want to keep ignoring the docs/ directory? | ||
static constexpr std::array<std::string_view, 1> usageTypesIgnoredOnClone{"doc"}; |
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.
- TODO: do we keep ignoring the docs/ directory when we clone the measure?
src/utilities/bcl/BCLMeasure.cpp
Outdated
// TODO: The logic here is weird. why are we creating then removing it? | ||
// removeDirectory(newDir); |
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.
// TODO: The logic here is weird. why are we creating then removing it? | |
// removeDirectory(newDir); |
src/utilities/bcl/BCLMeasure.cpp
Outdated
// TODO: isn't it better to copy only what's inside measure.xml? | ||
// The only caveat is what if the measure.xml is outdated? As a reminder, The BCL measure wouldn't have been loaded first, which updates the XML | ||
// So the only potential issue is if the user loads the measure (in IRB/PRY for eg), then continues to make changes to the filesystem, then much | ||
// later calls measure.clone. I don't think this is an issue | ||
|
||
openstudio::path tests = toPath("tests"); | ||
if (exists(this->directory() / tests) && !this->copyDirectory(this->directory() / tests, newDir / tests)) { | ||
return boost::none; | ||
} | ||
// Copy the measure.xml | ||
openstudio::filesystem::copy_file_no_throw(m_directory / openstudio::path{"measure.xml"}, newDir / openstudio::path{"measure.xml"}); | ||
|
||
// Then copy whatever is referneced in the measure.xml | ||
for (const BCLFileReference& file : this->files()) { | ||
|
||
openstudio::path resources = toPath("resources"); | ||
if (exists(this->directory() / resources) && !this->copyDirectory(this->directory() / resources, newDir / resources)) { | ||
return boost::none; | ||
if (std::find_if(usageTypesIgnoredOnClone.cbegin(), usageTypesIgnoredOnClone.cend(), | ||
[&file](const auto& sv) { return file.usageType() == std::string(sv); }) | ||
== usageTypesIgnoredOnClone.cend()) { | ||
|
||
// BCLFileReference::path() is absolute | ||
openstudio::path oriPath = file.path(); | ||
openstudio::path relativePath = openstudio::filesystem::relative(oriPath, m_directory); | ||
openstudio::path destination = newDir / relativePath; | ||
// Create parent directories in destination if need be | ||
openstudio::filesystem::create_directories(destination.parent_path()); | ||
openstudio::filesystem::copy_file_no_throw(oriPath, destination); | ||
} |
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.
This is a change. Instead of relying on the files on disk, we should just rely on the fact that we've got a clean BCLXML... My comment is wrong though, checkForUpdatesFiles isn't called... perhaps we should just do that?
- TODO: clone should call checkForUpdatesFiles() first?
path toPath(std::string_view s) { | ||
return toPath(std::string(s)); | ||
} |
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.
new toPath for std::string_view
TEST_F(BCLFixture, isApprovedFile) { | ||
|
||
EXPECT_TRUE(BCLMeasure::isIgnoredFileName(".hello")); | ||
EXPECT_TRUE(BCLMeasure::isIgnoredFileName("")); | ||
EXPECT_TRUE(BCLMeasure::isIgnoredFileName(".")); | ||
EXPECT_TRUE(BCLMeasure::isIgnoredFileName("..")); | ||
EXPECT_FALSE(BCLMeasure::isIgnoredFileName(".gitkeep")); | ||
EXPECT_FALSE(BCLMeasure::isIgnoredFileName("hello")); | ||
|
||
openstudio::path measureDir = "/usr/output/my_measure"; | ||
|
||
std::vector<std::pair<openstudio::path, bool>> tests{ | ||
{fs::path("measure.rb"), true}, | ||
{fs::path("somethingelse.rb"), false}, // Non approved root file | ||
{fs::path("docs/hello.rb"), true}, | ||
{fs::path("docs/subfolder/hello.rb"), true}, | ||
{fs::path("tests/hello.rb"), true}, | ||
{fs::path("tests/subfolder/hello.rb"), true}, | ||
{fs::path("tests/output/hello.rb"), false}, // excluded subfolder | ||
{fs::path("resources/hello.rb"), true}, | ||
{fs::path("resources/subfolder/hello.rb"), true}, | ||
{fs::path("resources/output/hello.rb"), true}, | ||
|
||
// Not an approved subfolder | ||
{fs::path("non_approved_subfolder/hello.rb"), false}, | ||
{fs::path("non_approved_subfolder/subfolder/hello.rb"), false}, | ||
{fs::path("non_approved_subfolder/output/hello.rb"), false}, | ||
}; | ||
|
||
for (const auto& [relativeFilePath, expectedAllowed] : tests) { | ||
openstudio::path absoluteFilePath = measureDir / relativeFilePath; | ||
EXPECT_EQ(expectedAllowed, BCLMeasure::isApprovedFile(absoluteFilePath, measureDir)); | ||
} | ||
} |
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.
Test isApprovedFile
} | ||
} | ||
|
||
TEST_F(BCLFixture, 4156_TestRecursive) { |
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.
Plenty of more testing, it should be commented enough, so I won't bother adding review comments here. It's quite long too.
…e need to discuss this and keep only one branch
@jmarrec This looks good but I see one failing unit test
I built this on my local mac as well and confirm that it is failing. On thing that stands is the number of files in an equality test. 6 vs 5.
Would you mind looking at this failure? Otherwise I think this good to go. |
if (m_bclXML.hasFile(srcItemPath) || (xmlPath == openstudio::filesystem::system_complete(srcItemPath))) { | ||
if (!openstudio::filesystem::copy_file_no_throw(srcItemPath, dstItemPath)) { | ||
return false; | ||
if (parentPath == measureDir) { |
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.
This is where the unit test is failing @tijcolem .
Once again I got bit by the fact that toPath("/path/to/dir/") != toPath("/path/to/dir")
in boost::filesystem::path...
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.
(Path handling is absolutely infuriating in C++, whether that's boost::filesystem or std::filesystem)
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.
5eff870 should have fixed this.
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.
@jmarrec Thanks for addressing this. That is supper annoying that you can't compare equality using == with either of those. It looks like your last commit cleared up Mac and Linux but of course Windows is being finicky and still failing on the same test. I tried to launch a full build to make sure, but yep, still failing.
// TODO: isn't it better to copy only what's inside measure.xml? | ||
constexpr bool weCareAboutOutdatedMeasureXML = true; | ||
// The only caveat is what if the measure.xml is outdated with regards to <files>? | ||
// Currently the BCLMeasure(const path&) ctor **does NOT** call checkUpdatesFiles() | ||
// | ||
// We could call it here by that would require a const_cast and it would increment the measure.xml versionId of the original measure, | ||
// and update the list of files, which isn't a big deal if the user doesn't then save it too? Even if the user did it, wouldn't we be helping out anyways? | ||
|
||
// If this is really not wanted, we can go back to scanning the files on disk, with a similar logic as in checkForUpdatesFiles: | ||
// loop on all files on disk, and if isApproved is true, then copy them | ||
|
||
// Copy the measure.xml | ||
openstudio::filesystem::copy_file_no_throw(m_directory / openstudio::path{"measure.xml"}, newDir / openstudio::path{"measure.xml"}); |
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.
1d35fa7
to
5eff870
Compare
…les in the xml. And separate into two tests for readability
CI Results for 7b9161d:
|
Pull request overview
Fix #4156 - Support nested subfolders in approved Measure directories
TODO
BCLMeasure(const openstudio::path&)
callcheckForUpdatesFiles
? ShouldBCLMeasure::clone(const openstudio::path&)
callcheckForUpdatesFiles()
first so we have a clean BCLXML to use for copying the files?Pull Request Author
src/utilities/test
)Labels:
IDDChange
APIChange
Pull Request - Ready for CI
so that CI builds your PRReview Checklist
This will not be exhaustively relevant to every PR.