mirror of
https://github.com/clementine-player/Clementine
synced 2024-12-18 12:28:31 +01:00
Add mostly done M3UParser with basic unit tests.
Add lots of test infrastructure.
This commit is contained in:
parent
7e500a2fa8
commit
c043eaba0c
@ -53,6 +53,7 @@ set(CLEMENTINE-SOURCES
|
|||||||
shortcutsdialog.cpp
|
shortcutsdialog.cpp
|
||||||
albumcovermanager.cpp
|
albumcovermanager.cpp
|
||||||
albumcoverloader.cpp
|
albumcoverloader.cpp
|
||||||
|
m3uparser.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# Header files that have Q_OBJECT in
|
# Header files that have Q_OBJECT in
|
||||||
@ -99,6 +100,7 @@ set(CLEMENTINE-MOC-HEADERS
|
|||||||
shortcutsdialog.h
|
shortcutsdialog.h
|
||||||
albumcovermanager.h
|
albumcovermanager.h
|
||||||
albumcoverloader.h
|
albumcoverloader.h
|
||||||
|
m3uparser.h
|
||||||
)
|
)
|
||||||
|
|
||||||
# UI files
|
# UI files
|
||||||
@ -148,6 +150,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
|
|||||||
|
|
||||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/qtsingleapplication")
|
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/qtsingleapplication")
|
||||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/qxt")
|
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/qxt")
|
||||||
|
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/gtest/include")
|
||||||
|
|
||||||
set(EXECUTABLE_OUTPUT_PATH ..)
|
set(EXECUTABLE_OUTPUT_PATH ..)
|
||||||
add_library(clementine_lib
|
add_library(clementine_lib
|
||||||
|
91
src/m3uparser.cpp
Normal file
91
src/m3uparser.cpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#include "m3uparser.h"
|
||||||
|
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
|
M3UParser::M3UParser(QIODevice* device, QDir directory, QObject* parent)
|
||||||
|
: QObject(parent),
|
||||||
|
device_(device),
|
||||||
|
type_(STANDARD),
|
||||||
|
directory_(directory) {
|
||||||
|
}
|
||||||
|
|
||||||
|
const QList<Song>& M3UParser::Parse() {
|
||||||
|
QString line = QString::fromLatin1(device_->readLine());
|
||||||
|
if (line == "#EXTM3U") {
|
||||||
|
// This is in extended M3U format.
|
||||||
|
type_ = EXTENDED;
|
||||||
|
line = QString::fromLatin1(device_->readLine());
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (line.startsWith('#')) {
|
||||||
|
// Extended info or comment.
|
||||||
|
if (type_ == EXTENDED && line.startsWith("#EXT")) {
|
||||||
|
if (!ParseMetadata(line, ¤t_metadata_)) {
|
||||||
|
qWarning() << "Failed to parse metadata: " << line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Track location.
|
||||||
|
QUrl url;
|
||||||
|
if (!ParseTrackLocation(line, &url)) {
|
||||||
|
qWarning() << "Failed to parse location: " << line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line = QString::fromLatin1(device_->readLine());
|
||||||
|
} while (device_->canReadLine());
|
||||||
|
|
||||||
|
return songs_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool M3UParser::ParseMetadata(QString line, M3UParser::Metadata* metadata) const {
|
||||||
|
// Extended info, eg.
|
||||||
|
// #EXTINF:123,Sample Artist - Sample title
|
||||||
|
QString info = line.section(':', 1);
|
||||||
|
QString l = info.section(',', 0, 0);
|
||||||
|
bool ok = false;
|
||||||
|
int length = l.toInt(&ok);
|
||||||
|
if (!ok) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QString track_info = info.section(',', 1);
|
||||||
|
QStringList list = track_info.split('-');
|
||||||
|
if (list.size() <= 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
metadata->artist = list[0].trimmed();
|
||||||
|
metadata->title = list[1].trimmed();
|
||||||
|
metadata->length = length;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool M3UParser::ParseTrackLocation(QString line, QUrl* url) const {
|
||||||
|
if (line.contains(QRegExp("^[a-z]+://"))) {
|
||||||
|
// Looks like a url.
|
||||||
|
QUrl temp(line);
|
||||||
|
if (temp.isValid()) {
|
||||||
|
*url = temp;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be a local path.
|
||||||
|
if (line.contains(QRegExp("^([a-zA-Z]:\\)|(/)"))) {
|
||||||
|
// Absolute path.
|
||||||
|
// Fix windows \.
|
||||||
|
QString proper_path = line.replace('\\', '/');
|
||||||
|
if (!proper_path.startsWith('/')) {
|
||||||
|
proper_path.prepend("/");
|
||||||
|
}
|
||||||
|
*url = "file://" + proper_path;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// Relative path.
|
||||||
|
QString proper_path = line.replace('\\', '/');
|
||||||
|
QString absolute_path = directory_.absoluteFilePath(proper_path);
|
||||||
|
*url = "file://" + absolute_path;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
49
src/m3uparser.h
Normal file
49
src/m3uparser.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#ifndef M3UPARSER_H
|
||||||
|
#define M3UPARSER_H
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include "gtest/gtest_prod.h"
|
||||||
|
|
||||||
|
#include "song.h"
|
||||||
|
|
||||||
|
class QIODevice;
|
||||||
|
|
||||||
|
class M3UParser : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
M3UParser(QIODevice* device, QDir directory = QDir(), QObject* parent = 0);
|
||||||
|
virtual ~M3UParser() {}
|
||||||
|
|
||||||
|
const QList<Song>& Parse();
|
||||||
|
|
||||||
|
struct Metadata {
|
||||||
|
QString artist;
|
||||||
|
QString title;
|
||||||
|
int length;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum M3UType {
|
||||||
|
STANDARD = 0,
|
||||||
|
EXTENDED, // Includes extended info (track, artist, etc.)
|
||||||
|
LINK // Points to a directory.
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ParseMetadata(QString line, Metadata* metadata) const;
|
||||||
|
bool ParseTrackLocation(QString line, QUrl* url) const;
|
||||||
|
|
||||||
|
FRIEND_TEST(M3UParserTest, ParsesMetadata);
|
||||||
|
FRIEND_TEST(M3UParserTest, ParsesTrackLocation);
|
||||||
|
|
||||||
|
QIODevice* device_;
|
||||||
|
M3UType type_;
|
||||||
|
QDir directory_;
|
||||||
|
Metadata current_metadata_;
|
||||||
|
|
||||||
|
QList<Song> songs_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // M3UPARSER_H
|
@ -12,20 +12,27 @@ set(GTEST-SOURCES
|
|||||||
../3rdparty/gtest/src/gtest-port.cc
|
../3rdparty/gtest/src/gtest-port.cc
|
||||||
../3rdparty/gtest/src/gtest-test-part.cc
|
../3rdparty/gtest/src/gtest-test-part.cc
|
||||||
../3rdparty/gtest/src/gtest-typed-test.cc
|
../3rdparty/gtest/src/gtest-typed-test.cc
|
||||||
|
test_utils.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(gtest ${GTEST-SOURCES})
|
add_library(gtest ${GTEST-SOURCES})
|
||||||
|
|
||||||
|
|
||||||
set(SONGTEST-SOURCES
|
|
||||||
../3rdparty/gtest/src/gtest_main.cc
|
|
||||||
../src/song.cpp
|
|
||||||
song_test.cpp)
|
|
||||||
|
|
||||||
add_executable(song_test ${SONGTEST-SOURCES})
|
|
||||||
target_link_libraries(song_test clementine_lib gtest)
|
|
||||||
|
|
||||||
add_custom_target(test
|
add_custom_target(test
|
||||||
./song_test
|
echo "Running tests"
|
||||||
WORKING_DIRECTORY ${CURRENT_BINARY_DIR}
|
WORKING_DIRECTORY ${CURRENT_BINARY_DIR}
|
||||||
DEPENDS song_test)
|
DEPENDS song_test m3uparser_test)
|
||||||
|
|
||||||
|
# Given a file foo_test.cpp, creates a target foo_test and adds it to the test target.
|
||||||
|
macro(add_test_file test_source)
|
||||||
|
get_filename_component(TEST_NAME ${ARGV0} NAME_WE)
|
||||||
|
add_executable(${TEST_NAME}
|
||||||
|
${ARGV0}
|
||||||
|
../3rdparty/gtest/src/gtest_main.cc)
|
||||||
|
target_link_libraries(${TEST_NAME} clementine_lib gtest)
|
||||||
|
add_custom_command(TARGET test POST_BUILD
|
||||||
|
COMMAND ./${TEST_NAME})
|
||||||
|
endmacro (add_test_file)
|
||||||
|
|
||||||
|
|
||||||
|
add_test_file(m3uparser_test.cpp)
|
||||||
|
add_test_file(song_test.cpp)
|
||||||
|
29
tests/m3uparser_test.cpp
Normal file
29
tests/m3uparser_test.cpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "test_utils.h"
|
||||||
|
|
||||||
|
#include "m3uparser.h"
|
||||||
|
|
||||||
|
class M3UParserTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
M3UParserTest()
|
||||||
|
: parser_(NULL) {
|
||||||
|
}
|
||||||
|
|
||||||
|
M3UParser parser_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(M3UParserTest, ParsesMetadata) {
|
||||||
|
QString line("#EXTINF:123,Foo artist - Foo track");
|
||||||
|
M3UParser::Metadata metadata;
|
||||||
|
ASSERT_TRUE(parser_.ParseMetadata(line, &metadata));
|
||||||
|
EXPECT_EQ("Foo artist", metadata.artist.toStdString());
|
||||||
|
EXPECT_EQ("Foo track", metadata.title.toStdString());
|
||||||
|
EXPECT_EQ(123, metadata.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(M3UParserTest, ParsesTrackLocation) {
|
||||||
|
QString line("/foo/bar.mp3");
|
||||||
|
QUrl url;
|
||||||
|
ASSERT_TRUE(parser_.ParseTrackLocation(line, &url));
|
||||||
|
EXPECT_EQ(QUrl("file:///foo/bar.mp3"), url);
|
||||||
|
}
|
@ -3,10 +3,7 @@
|
|||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& stream, const QString& str) {
|
#include "test_utils.h"
|
||||||
stream << str.toStdString();
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
15
tests/test_utils.cpp
Normal file
15
tests/test_utils.cpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include "test_utils.h"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& stream, const QString& str) {
|
||||||
|
stream << str.toStdString();
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator <<(std::ostream& stream, const QUrl& url) {
|
||||||
|
stream << url.toString().toStdString();
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
12
tests/test_utils.h
Normal file
12
tests/test_utils.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ifndef TEST_UTILS_H
|
||||||
|
#define TEST_UTILS_H
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
class QString;
|
||||||
|
class QUrl;
|
||||||
|
|
||||||
|
std::ostream& operator <<(std::ostream& stream, const QString& str);
|
||||||
|
std::ostream& operator <<(std::ostream& stream, const QUrl& url);
|
||||||
|
|
||||||
|
#endif // TEST_UTILS_H
|
Loading…
Reference in New Issue
Block a user