Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Fix m3u list parsing with /r/n end of line #4547

Merged
merged 22 commits into from
Dec 17, 2021
Merged

Conversation

daschuer
Copy link
Member

This fixes a regression form
a006552

@Swiftb0y
Copy link
Member

Wdyt about adding a test to make sure this regression does not happen again? Should be relatively straightforward.
I guess only main is affected right?

src/library/parserm3u.cpp Outdated Show resolved Hide resolved
@daschuer
Copy link
Member Author

Test is in place now.

However I have discovered a kind of regression due to:

c1dcdcc

@uklotzde you have moved the existence check from a central point into the m3u parser. This removed the existence check from all other playlist types. What is you recommendation to fix this issue?

@uklotzde
Copy link
Contributor

Test is in place now.

However I have discovered a kind of regression due to:

c1dcdcc

@uklotzde you have moved the existence check from a central point into the m3u parser. This removed the existence check from all other playlist types. What is you recommendation to fix this issue?

I don't spot any regression? Invoking TrackFile::checkExists() when needed instead of moving an anonymous boolean value around is safer and less error prone.

@daschuer
Copy link
Member Author

I just had a closer look. The issue is that ParserCsv::parse() has now no existence check. Not existing tracks are leading to a debug assertion violation:

�[91mcritical�[0m [�[97mMain�[0m] DEBUG ASSERT: "sourceSynchronizedAt.isValid()" in function bool mixxx::TrackRecord::replaceMetadataFromSource(mixxx::TrackMetadata&&, const QDateTime&) at /home/sperry/workspace/2.3/src/track/trackrecord.cpp:184

I have misread the code around ParserPls::parse() which also removes not existing tracks. Now I have found the check.

The anonymous boolean was only a temporary solution, to show the issue.

I think this requires some more refactoring to make the code testable and keep the code in a good shape at the same time. A creeping scope of this PR :-/ .. but OK, right?

src/test/playlisttest.cpp Outdated Show resolved Hide resolved
@daschuer
Copy link
Member Author

This fixes now also the debug assert with non existing csv entries.
I have also added tests for pls and csv.
csv files can now also be used if no or a translated header line is found.

src/library/parser.cpp Outdated Show resolved Hide resolved
src/library/parser.cpp Outdated Show resolved Hide resolved
@daschuer
Copy link
Member Author

I have now introduced the named function: parseAllLocations() this is even better than the anonymous lambda.

@Swiftb0y
Copy link
Member

well, the lambda was basically the C++ workaround for what would otherwise just be a simple block expression in rust, but extracting it into a function is good too.

Copy link
Member

@Swiftb0y Swiftb0y left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR seems done, right?
@Holzhaus we still need your approval/dismissal since you requested changes.


ParserPls::ParserPls() : Parser() {
}
QString getFilePath(QTextStream* stream) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the ptr instead of a const-ref?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It modifies the stream (moves the seek position) so I think a ptr is semantically appropriate.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right, so the text stream must be mutable, but that still does not explain why were passing a pointer instead of a (mutable) reference. AFAIK they are semantically the same, so is the pointer just used because it was there in the first place?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep in mind that this is not a request for changes, I'm must asking because I'd like improve my C++ knowledge.

Copy link
Member Author

@daschuer daschuer Dec 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will change "stream" to "pStream" ...

From the C++ rules we may pass here a non const reference:
QString getFilePath(QTextStream& stream); But it is a matter of code style.

Than we see:
stream.readLine();
and not
pStream->readLine();

The later makes clear, that we do not have a local copy and all work with the pointed object will have an outside effect.

In case of QString getFilePath(cosnt QTextStream& stream); it does not matter if it is a copy by value or a reference, It has never an outside effect, because of the constantnes

This a fundamental difference between C++ and Java or C#, where objects are always a references and where thy are no pointers.

This was also part of the Google style. However they have recently dropped the strict rule.

See also:
https://github.com/mixxxdj/mixxx/wiki/Coding-Guidelines#non-const-references

(I have updated the text linking the original google rule)

Copy link
Member

@Swiftb0y Swiftb0y Dec 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation. From a type-safety perspective, when passing a pointer, while passing nullptr would not make sense, it is still permitted. Wouldn't it make sense to use a mutable reference instead since those can't be null?
Moreover, in this particular case, we don't treat the incoming QTextStream as an output variable, but rather consume the textstream and expect it to not be used in the caller afterwards, correct? Because then the parameter rather seems like it should semantically be a QTextStream&&. I'm aware that QTextStream does not have a move or copy constructor. turns out this assumption is wrong.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it make sense to use a mutable reference instead since those can't be null?

That is a valid argument for the other code style. Both styles are valid, we prefere the reliable value vs. pointer syntax style. By the way: in C++ a mutable reference can be null. It is only by convention never null. Pointers and references are exactly the same if you look at the produced assembler code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a valid argument for the other code style. Both styles are valid, we prefere the reliable value vs. pointer syntax style.

Thanks for the clarification. I guess I'd prefer the the other style but I can adopt the mixxx style as well.

By the way: in C++ a mutable reference can be null. It is only by convention never null. Pointers and references are exactly the same if you look at the produced assembler code.

I know, but I thought the compiler does not allow references to be set to null (at compile time, there aren't any runtime checks unless you use something like gsl::not_null).

src/test/playlisttest.cpp Outdated Show resolved Hide resolved
src/test/playlisttest.cpp Outdated Show resolved Hide resolved
//qDebug() << "ParserCsv: Starting to parse.";
if (file.open(QIODevice::ReadOnly)) {
QByteArray ba = file.readAll();

QList<QList<QString> > tokens = tokenize(ba, ',');

// detect Location column
int loc_coll = 0x7fffffff;
int loc_col = -1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you rename the variable anyway, let's name it locationColumnIndex or something like that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well the current naming is consistent with the entire function, so does it make sense to change just this one? Is worth to widen the scope yet again to rename all variables in that function?


ParserPls::ParserPls() : Parser() {
}
QString getFilePath(QTextStream* stream) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It modifies the stream (moves the seek position) so I think a ptr is semantically appropriate.

@Holzhaus
Copy link
Member

Holzhaus commented Dec 2, 2021

This PR seems done, right?
@Holzhaus we still need your approval/dismissal since you requested changes.

Yes, I just re-reviewed and chose comment instead of request changes to clarify I'm okay with merging.

@Swiftb0y
Copy link
Member

Swiftb0y commented Dec 2, 2021

This PR seems done, right?
@Holzhaus we still need your approval/dismissal since you requested changes.

Yes, I just re-reviewed and chose comment instead of request changes to clarify I'm okay with merging.

Thanks for clarification. For some reason, GitHub does not show that when you look into the reviewers section at the top right of the PR. Maybe you need to dismiss the review or overwrite it via an explicit approval?

@daschuer
Copy link
Member Author

daschuer commented Dec 5, 2021

Done.

virtual ~ParserCsv();
/**Overwriting function parse in class Parser**/
QList<QString> parse(const QString&);
class ParserCsv : Parser {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the consequence of leaving out the inheritance visibility specifier? Previously it was inheriting from Parser publicly, now its defaulting to something (I don't know what without having to look it up).
From pure gut feeling, leaving the specifier out seems like bad style to me, though I have 0 idea in practice.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It defaults to private.
But to be honest, I have just forget to write it explicitly. I will fix it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, that was my concern. Seems like no outside component depends on the inheritance but I think it makes sense for the relationship to be public regardless.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@Swiftb0y
Copy link
Member

Swiftb0y commented Dec 7, 2021

Any idea whats tripping up clang-format? Related to #4557?

@uklotzde
Copy link
Contributor

uklotzde commented Dec 7, 2021

Any idea whats tripping up clang-format? Related to #4557?

We now require Standard: c++17 and >> is valid syntax since C++11. Legacy code using > > gets fixed.

@uklotzde
Copy link
Contributor

uklotzde commented Dec 7, 2021

It is probably a good idea to replace all occurrences of > > with >> in a subsequent PR. It is a small and trivial change that does not need to be deferred by the no mass reformatting rule.

@daschuer
Copy link
Member Author

daschuer commented Dec 7, 2021

It is probably a good idea to replace all occurrences of > > with >> in a subsequent PR.

I have no interest to do that. The > > does not hurt my eyes and there is a good chance of creating conflicts with existing PRs.

@@ -56,3 +57,52 @@ TEST_F(PlaylistTest, Relative) {
EXPECT_EQ(QString("base/folder/../../bar.mp3"),
parser.playlistEntryToFilePath("../../bar.mp3", "base/folder"));
}

TEST_F(PlaylistTest, m3uEndOfLine) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everywhere else we use PascalCase for the test names, let's keep this pattern here:

Suggested change
TEST_F(PlaylistTest, m3uEndOfLine) {
TEST_F(PlaylistTest, M3UEndOfLine) {

Holzhaus added a commit to Holzhaus/mixxx that referenced this pull request Dec 8, 2021
The space is unnecessary and clang-format complains about it. See
mixxxdj#4547 (comment).

This patch was created using:

    $ find src \( -name "*.h" -o -name "*.cpp" \) -exec sed -i 's/> >/>>/' {} \;
@Holzhaus
Copy link
Member

Holzhaus commented Dec 8, 2021

It is probably a good idea to replace all occurrences of > > with >> in a subsequent PR.

I have no interest to do that.

No problem, I took care of it here: #4559

@daschuer
Copy link
Member Author

Conflict resolved, merge?

@Holzhaus Holzhaus merged commit 7be89a4 into mixxxdj:main Dec 17, 2021
@daschuer daschuer deleted the m3u_fix branch May 4, 2022 09:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants