diff --git a/.gitignore b/.gitignore index 259148f..f3b51ca 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,8 @@ *.exe *.out *.app + +# Macos things +*.DS_Store +*.idea/ +*cmake-build-debug diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..960be4d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,57 @@ +language: cpp +os: linux +dist: xenial +compiler: gcc + + +before_script: + - cd iz1 + - mkdir build + - cd build + +jobs: + # + # Valgrind + # + - os: linux + env: + - TEST="Valgrind" + addons: + apt: + packages: + - valgrind + script: + - cmake .. + - make + - ctest -T memcheck + + # + # CppCheck + # + - os: linux + env: + - TEST="CppCheck" + + script: + - cmake -DENABLE_CPPCHECK=ON .. + - make + - make check + + # + # Coverage + # + # - os: linux + # env: + # - TEST="GCovr" + # addons: + # apt: + # packages: + # - gcov + # before_script: + # - pip3 install gcovr + # script: + # - cmake -DENABLE_GCOV + # - make + # - ctest + # - cd .. && gcovr + diff --git a/iz1/CMakeLists.txt b/iz1/CMakeLists.txt new file mode 100644 index 0000000..4cb7fee --- /dev/null +++ b/iz1/CMakeLists.txt @@ -0,0 +1,104 @@ +cmake_minimum_required(VERSION 3.1) + +project(iz1 C CXX) + +set(CMAKE_C_STANDARD 99) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# +### GCOV +# + +if (ENABLE_GCOV) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftest-coverage -fprofile-arcs") +endif () + + +## +### Valgrind +## + +set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --leak-check=full") +set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --track-fds=yes") +set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --trace-children=yes") +set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --error-exitcode=1") + + +## +### Test definitions ### +## + +include(CTest) + +configure_file(CMakeLists.txt.in + googletest-download/CMakeLists.txt) +execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download) +execute_process(COMMAND ${CMAKE_COMMAND} --build . + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download) + +add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src + ${CMAKE_BINARY_DIR}/googletest-build) + +enable_testing() +add_subdirectory(test) + + +## +### Cppecheck +## + +if (ENABLE_CPPCHECK) + + list(APPEND CPPCHECK_CMAKE_ARGS + "-DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}" + ) + include(ExternalProject) + ExternalProject_Add( + cppcheck + GIT_REPOSITORY https://github.com/danmar/cppcheck.git + GIT_TAG 1.79 + GIT_SHALLOW 1 + CMAKE_ARGS ${CPPCHECK_CMAKE_ARGS} + PREFIX ${CMAKE_BINARY_DIR}/external/cppcheck/prefix + TMP_DIR ${CMAKE_BINARY_DIR}/external/cppcheck/tmp + STAMP_DIR ${CMAKE_BINARY_DIR}/external/cppcheck/stamp + DOWNLOAD_DIR ${CMAKE_BINARY_DIR}/external/cppcheck/download + SOURCE_DIR ${CMAKE_BINARY_DIR}/external/cppcheck/src + BINARY_DIR ${CMAKE_BINARY_DIR}/external/cppcheck/build + ) + + list(APPEND CPPCHECK_ARGS + --enable=warning,style,performance,portability,unusedFunction + --std=c99 + --verbose + --error-exitcode=1 + --language=c + -DMAIN=main + -I ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/include/*.h + ${CMAKE_SOURCE_DIR}/src/*.c + ) + + add_custom_target( + check + COMMAND ${CMAKE_BINARY_DIR}/bin/cppcheck ${CPPCHECK_ARGS} + COMMENT "running cppcheck" + ) + +endif () + +## +### Source definitions ### +## + + +include_directories("${PROJECT_SOURCE_DIR}/include") + +file(GLOB sources + "${PROJECT_SOURCE_DIR}/include/html_tag/*.h" + "${PROJECT_SOURCE_DIR}/include/my_str/*.h" + "${PROJECT_SOURCE_DIR}/src/*.c") + +add_executable(iz1 ${sources}) diff --git a/iz1/CMakeLists.txt.in b/iz1/CMakeLists.txt.in new file mode 100644 index 0000000..7499d73 --- /dev/null +++ b/iz1/CMakeLists.txt.in @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.1) + +project(extern-download NONE) + + +include(ExternalProject) +ExternalProject_Add( +googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.8.1 + SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" + BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/iz1/README.MD b/iz1/README.MD new file mode 100644 index 0000000..0b68772 --- /dev/null +++ b/iz1/README.MD @@ -0,0 +1,20 @@ +# Индивидуальное задание 1 + +ИЗ1 посвящено приобретению навыков безопасной работы с памятью на языке C с использованием базовых структур данных и налаживания базовой инфраструктуры для автоматической проверки кода. В качестве результата ИЗ2 ожидается: +* грамотное разбиение проекта на файлы; +* использование безопасного стиля программирования - проверка возможных ошибок, корректное завершение программы в случае их возникновения и правильная работа с памятью; +* максимальное покрытие кода юнит-тестами; +* рабочий CI, включающего в себя автоматическую сборку проекта, статический анализ кода, прохождение линтеров, valgrind, запуск юнит-тестов и получение отчёта о покрытии кода тестами +* все автоматические проверки должны проходить на итоговой версии, которая проходит ревью +Вариант #23 +Создать структуру для хранения информации об HTML-теге: его имени, признаке «открывающий/закрывающий» и атрибутах тега. Составить с ее использованием программу, включающую в себя функцию, принимающую на вход текстовую строку с одним тегом. На выход функция должна возвращать указатель на инициализированную структуру. + +Требования к оформлению: +Программа должна быть реализована на языке C и работать для произвольных наборов входных данных (в том числе и ошибочных), вводимых пользователем с клавиатуры. Должна быть выполнена грамотная декомпозиция на функции и файлы. +Помимо самой программы необходимо: +– разработать набор юнит-тестов, проверяющих корректную работу реализованных функций. Обеспечить максимальное покрытие исходного кода тестами; +– оформить задачу в виде Merge Request отдельной ветки в основную ветку проекта. +Внимание: в основной ветке проекта никакого кода быть не должно! +– развернуть CI, в рамках которого автоматизировать сборку проекта, прохождение юнит-тестов под valgrind, генерацию отчёта о покрытии кода тестами, линтера и статического анализатора исходного кода; +– после прохождения всех проверок необходимо отправить код на ревью своему преподавателю; +– ревью - процесс итерационный. До наступления дедлайна можно проходить несколько итераций, улучшая свою оценку. Решения после дедлайна не принимаются; diff --git a/iz1/include/html_tag/attr.h b/iz1/include/html_tag/attr.h new file mode 100644 index 0000000..d4ecce5 --- /dev/null +++ b/iz1/include/html_tag/attr.h @@ -0,0 +1,24 @@ +#ifndef IZ1_ATTR_H +#define IZ1_ATTR_H + +#include +#include + +typedef struct +{ + char *name; + char *value; +} tag_attr_t; + +// Функция проверки аттрибута тега (тег располагается в начале строкии и +// заканчивается либо символом '>', либо пробелом. Конец аттрибута записывается +// в переменную attr_end +bool check_attr_format(const char *str, size_t *attr_end); + +tag_attr_t *parse_attr(const char *str, int *attr_end); + +tag_attr_t **parse_attr_arr(const char *str, size_t count); + +void free_attr(tag_attr_t **attr); + +#endif //IZ1_ATTR_H diff --git a/iz1/include/html_tag/html_tag.h b/iz1/include/html_tag/html_tag.h new file mode 100644 index 0000000..439552e --- /dev/null +++ b/iz1/include/html_tag/html_tag.h @@ -0,0 +1,39 @@ +/* + * Создать структуру для хранения информации об HTML-теге: его имени, признаке «открывающий/закрывающий» + * и атрибутах тега. Составить с ее использованием программу, включающую в себя функцию, принимающую на + * вход текстовую строку с одним тегом. На выход функция должна возвращать указатель на инициализированную структуру. + * + * Примеры тегов + * + * + * + * + * + * + */ + +#ifndef IZ1_HTML_TAG_H +#define IZ1_HTML_TAG_H + +#include +#include + +#include "attr.h" + +typedef struct +{ + char *name; + bool is_opening; + size_t attributes_count; + tag_attr_t **attributes; +} html_tag; + +bool check_html_tag_format(const char *str); + +html_tag *parse_tag(const char *str); + +size_t parse_status(const char *str, html_tag *tag); + +void free_tag(html_tag **tag); + +#endif //IZ1_HTML_TAG_H diff --git a/iz1/include/my_str/alg.h b/iz1/include/my_str/alg.h new file mode 100644 index 0000000..0ce08e4 --- /dev/null +++ b/iz1/include/my_str/alg.h @@ -0,0 +1,12 @@ +#ifndef IZ1_ALG_H +#define IZ1_ALG_H + +#define ASCII_NAME_ALLOWED "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789\"-" + +int str_find(const char *str, char c); + +int str_first_char_occurence(const char *str, const char *c); + +int str_count(const char *str, char c); + +#endif //IZ1_ALG_H diff --git a/iz1/include/my_str/mem.h b/iz1/include/my_str/mem.h new file mode 100644 index 0000000..216f72f --- /dev/null +++ b/iz1/include/my_str/mem.h @@ -0,0 +1,12 @@ +#ifndef IZ1_MEM_H +#define IZ1_MEM_H + +#include +#include + + +size_t str_create_ncopy(char **to_allocate, const char *to_copy, size_t n); + +size_t str_create_word(char **to_allocate, const char *src, const char *sep); + +#endif //IZ1_MEM_H diff --git a/iz1/src/alg.c b/iz1/src/alg.c new file mode 100644 index 0000000..2b2cedd --- /dev/null +++ b/iz1/src/alg.c @@ -0,0 +1,34 @@ +#include "my_str/alg.h" + +int str_find(const char *str, char c) +{ + if (!str) + return -1; + + int i = 0; + while (str[i] != c && str[i] != '\0') + ++i; + return i; +} + +int str_first_char_occurence(const char *str, const char *c) +{ + if (!str) + return -1; + + int i = 0; + while (c[str_find(c, str[i])] == '\0' && str[i] != '\0') + ++i; + return i; +} + +int str_count(const char *str, char c) +{ + if (!str) + return -1; + + int count = 0; + while (*str != '\0') + count += (*(str++) == c); + return count; +} diff --git a/iz1/src/attr.c b/iz1/src/attr.c new file mode 100644 index 0000000..c2a9cf9 --- /dev/null +++ b/iz1/src/attr.c @@ -0,0 +1,68 @@ +#include "html_tag/attr.h" + +#include + +#include "my_str/mem.h" +#include "my_str/alg.h" + +bool check_attr_format(const char *str, size_t *attr_end) +{ + if (!str || !attr_end) + return false; + + size_t eq_pos = str_find(str, '='); + if (str[eq_pos] == '\0') + return false; + + for (int i = 0; i < eq_pos; ++i) + { + if (str[i] == ' ') + return false; + } + + eq_pos++; + if (str[eq_pos] == '\"') + *attr_end = str_find(str + eq_pos + 1, '\"') + 2; + else + *attr_end = str_first_char_occurence(str + eq_pos, " >"); + + *attr_end += eq_pos; + return str[*attr_end] != '\0'; +} + +void free_attr(tag_attr_t **attr) +{ + free((*attr)->name); + free((*attr)->value); + free(*attr); + *attr = NULL; +} + +tag_attr_t *parse_attr(const char *str, int *attr_end) +{ + const char *str_start = str; + tag_attr_t *attr = (tag_attr_t *) malloc(sizeof(tag_attr_t)); + + str += str_create_word(&attr->name, str, "="); + if (*str == '"') + str += str_create_word(&attr->value, str + 1, "\"") + 2; + else + str += str_create_word(&attr->value, str, " >"); + + ptrdiff_t attr_end_ = str - str_start; + *attr_end = (int) (attr_end_); + + return attr; +} + +tag_attr_t **parse_attr_arr(const char *str, size_t count) +{ + tag_attr_t **attr_arr = (tag_attr_t **) malloc(count * sizeof(tag_attr_t *)); + for (size_t i = 0; i < count; ++i) + { + int attr_end = 0; + attr_arr[i] = parse_attr(str, &attr_end); + str += attr_end; + } + return attr_arr; +} diff --git a/iz1/src/html_tag.c b/iz1/src/html_tag.c new file mode 100644 index 0000000..bf4ba45 --- /dev/null +++ b/iz1/src/html_tag.c @@ -0,0 +1,87 @@ +#include "html_tag/html_tag.h" + +#include "my_str/mem.h" +#include "my_str/alg.h" + +bool check_html_tag_format(const char *str) +{ + bool ok = str && str[0] == '<'; + if (ok && str[1] == '/') + { + size_t closing_pos = str_find(str, '>'); + for (size_t i = 2; ok && i < closing_pos; ++i) + ok = (ASCII_NAME_ALLOWED[str_find(ASCII_NAME_ALLOWED, str[i])] != '\0'); + } else if (ok) + { + size_t name_end = str_first_char_occurence(str, " >"); + for (size_t i = 1; ok && i < name_end; ++i) + ok = (ASCII_NAME_ALLOWED[str_find(ASCII_NAME_ALLOWED, str[i])] != '\0'); + + if (ok) + { + size_t closing_pos = str_find(str, '>'); + ok = (str[closing_pos] == '>'); + } + + if (ok) + { + size_t attr_end = name_end; + str += attr_end; + while (ok && str[0] != '>') + { + ok = check_attr_format(str + 1, &attr_end); + str += attr_end + 1; + } + } + } + return ok; +} + +html_tag *parse_tag(const char *str) +{ + if (!check_html_tag_format(str)) + return NULL; + + html_tag *tag = (html_tag *) malloc(sizeof(html_tag)); + + if (tag == NULL) + return NULL; + + str += parse_status(str, tag); + tag->attributes_count = str_count(str, '='); + + if (!tag->is_opening || tag->attributes_count == 0) + str += str_create_word(&tag->name, str, ">"); + else + str += str_create_word(&tag->name, str, " "); + + tag->attributes = parse_attr_arr(str, tag->attributes_count); + + return tag; +} + +size_t parse_status(const char *str, html_tag *tag) +{ + int i = 0; + if (str[i] == '<') + { + ++i; + if (str[i] == '/') + { + tag->is_opening = false; + ++i; + } else + tag->is_opening = true; + } + return i; +} + +void free_tag(html_tag **tag) +{ + free((*tag)->name); + for (size_t i = 0; i < (*tag)->attributes_count; ++i) + free_attr((*tag)->attributes + i); + free((*tag)->attributes); + free(*tag); + *tag = NULL; +} diff --git a/iz1/src/main.c b/iz1/src/main.c new file mode 100644 index 0000000..96ceb78 --- /dev/null +++ b/iz1/src/main.c @@ -0,0 +1,35 @@ +/* + * Создать структуру для хранения информации об HTML-теге: его имени, признаке «открывающий/закрывающий» + * и атрибутах тега. Составить с ее использованием программу, включающую в себя функцию, принимающую на + * вход текстовую строку с одним тегом. На выход функция должна возвращать указатель на инициализированную структуру. + */ +#include + +#include "html_tag/html_tag.h" + +#define MAX_STR_LEN 256 + +int main() +{ + char str[MAX_STR_LEN]; + fgets(str, sizeof(str), stdin); + html_tag *tag = parse_tag(str); + if (!tag) + puts("Wrong tag format!"); + else + { + printf("Tag name = \"%s\"\n", tag->name); + if (tag->is_opening) + puts("Opening tag"); + else + puts("Closing tag"); + if (tag->attributes_count) + { + puts("Attributes:"); + for (size_t i = 0; i < tag->attributes_count; ++i) + printf("%s=\"%s\"\n", tag->attributes[i]->name, tag->attributes[i]->value); + } + free_tag(&tag); + } + return 0; +} \ No newline at end of file diff --git a/iz1/src/mem.c b/iz1/src/mem.c new file mode 100644 index 0000000..c90fa97 --- /dev/null +++ b/iz1/src/mem.c @@ -0,0 +1,30 @@ +#include "my_str/mem.h" + +#include "my_str/alg.h" + + +size_t str_create_ncopy(char **to_allocate, const char *to_copy, size_t n) +{ + if (!to_copy || !to_allocate) + return 0; + + size_t to_copy_len = strlen(to_copy); + size_t allocate_size = (to_copy_len > n ? n : to_copy_len) + 1; + *to_allocate = (char *) malloc(allocate_size); + int i; + for (i = 0; i < n && to_copy[i] != '\0'; ++i) + (*to_allocate)[i] = to_copy[i]; + + (*to_allocate)[i] = '\0'; + + return allocate_size; +} + +size_t str_create_word(char **to_allocate, const char *src, const char *sep) +{ + if (!src || !to_allocate) + return 0; + + int word_end = str_first_char_occurence(src, sep); + return str_create_ncopy(to_allocate, src, word_end); +} diff --git a/iz1/test/CMakeLists.txt b/iz1/test/CMakeLists.txt new file mode 100644 index 0000000..50c75f3 --- /dev/null +++ b/iz1/test/CMakeLists.txt @@ -0,0 +1,23 @@ +include_directories("${PROJECT_SOURCE_DIR}/include") + +file(GLOB sources "${PROJECT_SOURCE_DIR}/src/*.c") +list(REMOVE_ITEM sources "${PROJECT_SOURCE_DIR}/src/main.c") + +file(GLOB tests "${PROJECT_SOURCE_DIR}/test/*.cpp") +list(REMOVE_ITEM tests "${PROJECT_SOURCE_DIR}/test/main.cpp") + +if (ENABLE_GCOV) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftest-coverage -fprofile-arcs") +endif () + + +foreach (file ${tests}) + set(name) + get_filename_component(name ${file} NAME_WE) + add_executable("${name}_tests" + ${sources} + ${file} + "${PROJECT_SOURCE_DIR}/test/main.cpp") + target_link_libraries("${name}_tests" gtest_main) + add_test(NAME ${name} COMMAND "${name}_tests") +endforeach () \ No newline at end of file diff --git a/iz1/test/attr.cpp b/iz1/test/attr.cpp new file mode 100644 index 0000000..d3d0a4f --- /dev/null +++ b/iz1/test/attr.cpp @@ -0,0 +1,42 @@ +#include "gtest/gtest.h" +#include + +extern "C" { +#include "html_tag/attr.h" +} + +TEST(correct_input, no_quotes) +{ + char correct_name[] = "attr1"; + char correct_value[] = "value1"; + char line[] = "attr1=value1"; + int attr_end = 0; + tag_attr_t *attr = parse_attr(line, &attr_end); + ASSERT_EQ(strcmp(attr->name, correct_name), 0); + ASSERT_EQ(strcmp(attr->value, correct_value), 0); + free_attr(&attr); +} + +TEST(correct_input, quotes_one) +{ + char correct_name[] = "attr1"; + char correct_value[] = "value1"; + char line[] = "attr1=\"value1\""; + int attr_end = 0; + tag_attr_t *attr = parse_attr(line, &attr_end); + ASSERT_EQ(strcmp(attr->name, correct_name), 0); + ASSERT_EQ(strcmp(attr->value, correct_value), 0); + free_attr(&attr); +} + +TEST(correct_input, quotes_two) +{ + char correct_name[] = "attr1"; + char correct_value[] = "value1 value2"; + char line[] = "attr1=\"value1 value2\""; + int attr_end = 0; + tag_attr_t *attr = parse_attr(line, &attr_end); + ASSERT_EQ(strcmp(attr->name, correct_name), 0); + ASSERT_EQ(strcmp(attr->value, correct_value), 0); + free_attr(&attr); +} diff --git a/iz1/test/check_attr_format.cpp b/iz1/test/check_attr_format.cpp new file mode 100644 index 0000000..b5d2e83 --- /dev/null +++ b/iz1/test/check_attr_format.cpp @@ -0,0 +1,63 @@ +#include "gtest/gtest.h" +#include + +extern "C" { +#include "html_tag/attr.h" +} + +TEST(format_checker_correct, test1) +{ + char str[] = "attr1=value1 "; + size_t attr_end; + ASSERT_EQ(check_attr_format(str, &attr_end), true); +} + +TEST(format_checker_correct, test2) +{ + char str[] = "attr1=\"value1\" "; + size_t attr_end; + ASSERT_EQ(check_attr_format(str, &attr_end), true); +} + +TEST(format_checker_correct, test3) +{ + char str[] = "attr1=\"value1 value2\" "; + size_t attr_end; + ASSERT_EQ(check_attr_format(str, &attr_end), true); +} + + +TEST(format_checker_correct, test1_last) +{ + char str[] = "attr1=value1>"; + size_t attr_end; + ASSERT_EQ(check_attr_format(str, &attr_end), true); +} + +TEST(format_checker_correct, test2_last) +{ + char str[] = "attr1=\"value1\">"; + size_t attr_end; + ASSERT_EQ(check_attr_format(str, &attr_end), true); +} + +TEST(format_checker_correct, test3_last) +{ + char str[] = "attr1=\"value1 value2\">"; + size_t attr_end; + ASSERT_EQ(check_attr_format(str, &attr_end), true); +} + +TEST(format_checker_incorrect, random_word) +{ + char str[] = "random_word"; + size_t attr_end; + ASSERT_EQ(check_attr_format(str, &attr_end), false); +} + +TEST(format_checker_incorrect, space_before_eq) +{ + char str[] = "word attr=val"; + size_t attr_end; + ASSERT_EQ(check_attr_format(str, &attr_end), false); +} diff --git a/iz1/test/check_html_tag_format.cpp b/iz1/test/check_html_tag_format.cpp new file mode 100644 index 0000000..a505351 --- /dev/null +++ b/iz1/test/check_html_tag_format.cpp @@ -0,0 +1,93 @@ +#include "gtest/gtest.h" +#include + +extern "C" { +#include "html_tag/html_tag.h" +} + +TEST(tag_format_check_correct, simple_opening) +{ + char str[] = ""; + ASSERT_EQ(check_html_tag_format(str), true); +} + +TEST(tag_format_check_correct, simple_closing) +{ + char str[] = ""; + ASSERT_EQ(check_html_tag_format(str), true); +} + +TEST(tag_format_check_correct, opening_one_attr) +{ + char str[] = ""; + ASSERT_EQ(check_html_tag_format(str), true); +} + +TEST(tag_format_check_correct, opening_two_attr) +{ + char str[] = ""; + ASSERT_EQ(check_html_tag_format(str), true); +} + +TEST(tag_format_check_correct, opening_two_quoted_attr) +{ + char str[] = ""; + ASSERT_EQ(check_html_tag_format(str), true); +} + +TEST(tag_format_check_correct, opening_two_quoted_attr_r) +{ + char str[] = ""; + ASSERT_EQ(check_html_tag_format(str), true); +} + + +TEST(tag_format_check_correct, opening_two_quoted_attr_) +{ + char str[] = R"()"; + ASSERT_EQ(check_html_tag_format(str), true); +} + +TEST(tag_format_check_correct, opening_three_quoted_attr) +{ + char str[] = R"()"; + ASSERT_EQ(check_html_tag_format(str), true); +} + +TEST(tag_format_check_correct, opening_three_quoted_attr_) +{ + char str[] = R"()"; + ASSERT_EQ(check_html_tag_format(str), true); +} + +TEST(tag_format_check_incorrect, no_name) +{ + char str[] = R"()"; + ASSERT_EQ(check_html_tag_format(str), false); +} + +TEST(tag_format_check_incorrect, closing_tag_attrs) +{ + char str[] = R"()"; + ASSERT_EQ(check_html_tag_format(str), false); +} + +TEST(tag_format_check_incorrect, random_symbols) +{ + char str[] = R"(lskdjfo isdfo)"; + ASSERT_EQ(check_html_tag_format(str), false); +} + +TEST(tag_format_check_incorrect, random_things_inside_tag) +{ + char str[] = R"()"; + ASSERT_EQ(check_html_tag_format(str), false); +} + + + + + + + + diff --git a/iz1/test/html_tag.cpp b/iz1/test/html_tag.cpp new file mode 100644 index 0000000..c71fe47 --- /dev/null +++ b/iz1/test/html_tag.cpp @@ -0,0 +1,127 @@ +#include "gtest/gtest.h" +#include + +extern "C" { +#include "html_tag/html_tag.h" +} + +TEST(tag_correct_input, no_quotes) +{ + char src[] = ""; + char expected_name[] = "opening_tag"; + bool expected_opening = true; + size_t expected_attr_count = 1; + char expected_attr_names[][10] = {"attr1"}; + char expected_attr_values[][10] = {"value1"}; + + html_tag *res = parse_tag(src); + ASSERT_EQ(strcmp(res->name, expected_name), 0); + ASSERT_EQ(res->is_opening, expected_opening); + ASSERT_EQ(res->attributes_count, expected_attr_count); + for (size_t i = 0; i < res->attributes_count; ++i) + { + ASSERT_EQ(strcmp(res->attributes[i]->name, expected_attr_names[i]), 0); + ASSERT_EQ(strcmp(res->attributes[i]->value, expected_attr_values[i]), 0); + } + free_tag(&res); +} + +TEST(tag_correct_input, quotes_one) +{ + char src[] = ""; + char expected_name[] = "opening_tag"; + bool expected_opening = true; + size_t expected_attr_count = 1; + char expected_attr_names[][10] = {"attr1"}; + char expected_attr_values[][10] = {"value1"}; + + html_tag *res = parse_tag(src); + ASSERT_EQ(strcmp(res->name, expected_name), 0); + ASSERT_EQ(res->is_opening, expected_opening); + ASSERT_EQ(res->attributes_count, expected_attr_count); + for (size_t i = 0; i < res->attributes_count; ++i) + { + ASSERT_EQ(strcmp(res->attributes[i]->name, expected_attr_names[i]), 0); + ASSERT_EQ(strcmp(res->attributes[i]->value, expected_attr_values[i]), 0); + } + free_tag(&res); +} + +TEST(tag_correct_input, quotes_two) +{ + char src[] = ""; + char expected_name[] = "opening_tag"; + bool expected_opening = true; + size_t expected_attr_count = 1; + char expected_attr_names[][20] = {"attr1"}; + char expected_attr_values[][20] = {"value1 value2"}; + + html_tag *res = parse_tag(src); + ASSERT_EQ(strcmp(res->name, expected_name), 0); + ASSERT_EQ(res->is_opening, expected_opening); + ASSERT_EQ(res->attributes_count, expected_attr_count); + for (size_t i = 0; i < res->attributes_count; ++i) + { + ASSERT_EQ(strcmp(res->attributes[i]->name, expected_attr_names[i]), 0); + ASSERT_EQ(strcmp(res->attributes[i]->value, expected_attr_values[i]), 0); + } + free_tag(&res); +} + + +TEST(tag_correct_input, two_attrs) +{ + char src[] = ""; + char expected_name[] = "opening_tag"; + bool expected_opening = true; + size_t expected_attr_count = 2; + char expected_attr_names[][20] = {"attr1", "attr2"}; + char expected_attr_values[][20] = {"value1 value2", "value3"}; + + html_tag *res = parse_tag(src); + ASSERT_EQ(strcmp(res->name, expected_name), 0); + ASSERT_EQ(res->is_opening, expected_opening); + ASSERT_EQ(res->attributes_count, expected_attr_count); + for (size_t i = 0; i < res->attributes_count; ++i) + { + ASSERT_EQ(strcmp(res->attributes[i]->name, expected_attr_names[i]), 0); + ASSERT_EQ(strcmp(res->attributes[i]->value, expected_attr_values[i]), 0); + } + free_tag(&res); +} + +TEST(tag_correct_input, three_attrs) +{ + char src[] = R"()"; + char expected_name[] = "opening_tag"; + bool expected_opening = true; + size_t expected_attr_count = 3; + char expected_attr_names[][20] = {"attr1", "attr2", "attr3"}; + char expected_attr_values[][20] = {"value1 value2", "value3", "value4"}; + + html_tag *res = parse_tag(src); + ASSERT_EQ(strcmp(res->name, expected_name), 0); + ASSERT_EQ(res->is_opening, expected_opening); + ASSERT_EQ(res->attributes_count, expected_attr_count); + for (size_t i = 0; i < res->attributes_count; ++i) + { + ASSERT_EQ(strcmp(res->attributes[i]->name, expected_attr_names[i]), 0); + ASSERT_EQ(strcmp(res->attributes[i]->value, expected_attr_values[i]), 0); + } + free_tag(&res); +} + + +TEST(tag_correct_input, closing) +{ + char src[] = ""; + char expected_name[] = "closing_tag"; + bool expected_opening = false; + size_t expected_attr_count = 0; + + html_tag *res = parse_tag(src); + ASSERT_EQ(strcmp(res->name, expected_name), 0); + ASSERT_EQ(res->is_opening, expected_opening); + ASSERT_EQ(res->attributes_count, expected_attr_count); + free_tag(&res); +} diff --git a/iz1/test/main.cpp b/iz1/test/main.cpp new file mode 100644 index 0000000..b42b721 --- /dev/null +++ b/iz1/test/main.cpp @@ -0,0 +1,6 @@ +#include "gtest/gtest.h" + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file