\documentclass[a4paper, final]{article}
%\usepackage{literat} % Нормальные шрифты
\usepackage[14pt]{extsizes} % для того чтобы задать нестандартный 14-ый размер шрифта
\usepackage{tabularx}
\usepackage[T2A]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[russian]{babel}
\usepackage{amsmath}
\usepackage[left=25mm, top=20mm, right=20mm, bottom=20mm, footskip=10mm]{geometry}
\usepackage{ragged2e} %для растягивания по ширине
\usepackage{setspace} %для межстрочно   го интервала
\usepackage{moreverb} %для работы с листингами
\usepackage{indentfirst} % для абзацного отступа
\usepackage{moreverb} %для печати в листинге исходного кода программ
\usepackage{pdfpages} %для вставки других pdf файлов
\usepackage{tikz}
\usepackage{graphicx}
\usepackage{afterpage}
\usepackage{longtable}
\usepackage{float}



% \usepackage[paper=A4,DIV=12]{typearea}
\usepackage{pdflscape}
% \usepackage{lscape}

\usepackage{array}
\usepackage{multirow}

\renewcommand\verbatimtabsize{4\relax}
\renewcommand\listingoffset{0.2em} %отступ от номеров строк в листинге
\renewcommand{\arraystretch}{1.4} % изменяю высоту строки в таблице
\usepackage[font=small, singlelinecheck=false, justification=centering, format=plain, labelsep=period]{caption} %для настройки заголовка таблицы
\usepackage{listings} %листинги
\usepackage{xcolor} % цвета
\usepackage{hyperref}% для гиперссылок
\usepackage{enumitem} %для перечислений

\newcommand{\specialcell}[2][l]{\begin{tabular}[#1]{@{}l@{}}#2\end{tabular}}


\setlist[enumerate,itemize]{leftmargin=1.2cm} %отступ в перечислениях

\hypersetup{colorlinks,
    allcolors=[RGB]{010 090 200}} %красивые гиперссылки (не красные)

% подгружаемые языки — подробнее в документации listings (это всё для листингов)
\lstloadlanguages{ SQL}
% включаем кириллицу и добавляем кое−какие опции
\lstset{tabsize=2,
    breaklines,
    basicstyle=\footnotesize,
    columns=fullflexible,
    flexiblecolumns,
    numbers=left,
    numberstyle={\footnotesize},
    keywordstyle=\color{blue},
    inputencoding=cp1251,
    extendedchars=true
}
\lstdefinelanguage{MyC}{
    language=SQL,
%    ndkeywordstyle=\color{darkgray}\bfseries,
%    identifierstyle=\color{black},
%    morecomment=[n]{/**}{*/},
%    commentstyle=\color{blue}\ttfamily,
%    stringstyle=\color{red}\ttfamily,
%    morestring=[b]",
%    showstringspaces=false,
%    morecomment=[l][\color{gray}]{//},
    keepspaces=true,
    escapechar=\%,
    texcl=true
}

\textheight=24cm % высота текста
\textwidth=16cm % ширина текста
\oddsidemargin=0pt % отступ от левого края
\topmargin=-1.5cm % отступ от верхнего края
\parindent=24pt % абзацный отступ
\parskip=5pt % интервал между абзацами
\tolerance=2000 % терпимость к "жидким" строкам
\flushbottom % выравнивание высоты страниц


% Настройка листингов
\lstset{
    language=python,
    extendedchars=\true,
    inputencoding=utf8,
    keepspaces=true,
    % captionpos=b, % подписи листингов снизу
}

\begin{document} % начало документа
    


    % НАЧАЛО ТИТУЛЬНОГО ЛИСТА
    \begin{center}
        \hfill \break
        \hfill \break
        \normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ\\
            федеральное государственное автономное образовательное учреждение высшего образования «Санкт-Петербургский политехнический университет Петра Великого»\\[10pt]}
        \normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt] 
        \normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt] 
        \normalsize{Направление: 02.03.01 <<Математика и компьютерные науки>>}\\
        
        \hfill \break
        \hfill \break
        \hfill \break
        \hfill \break
        \large{Лабораторная работа №3}\\
        \large{<<Статический анализ кода приложений>>}\\
        \large{по дисциплине}\\
        \large{<<Методы тестирования программного обеспечения>>}\\
        \hfill \break
        
        % \hfill \break
        \hfill \break
    \end{center}
    
    \small{ 
        \begin{tabular}{lrrl}
            \!\!\!Студент, & \hspace{2cm} & & \\
            \!\!\!группы 5130201/20102 & \hspace{2cm} & \underline{\hspace{3cm}} &Тищенко А. А. \\\\
            \!\!\!Преподаватель & \hspace{2cm} &  \underline{\hspace{3cm}} &  Курочкин М. А. \\\\
            &&\hspace{4cm}
        \end{tabular}
        \begin{flushright}
            <<\underline{\hspace{1cm}}>>\underline{\hspace{2.5cm}} 2025г.
        \end{flushright}
    }
    
    \hfill \break
    % \hfill \break
    \begin{center} \small{Санкт-Петербург, 2025} \end{center}
    \thispagestyle{empty} % выключаем отображение номера для этой страницы
    
    % КОНЕЦ ТИТУЛЬНОГО ЛИСТА
    \newpage
    
    \tableofcontents


    \newpage
    
    \section{Постановка задачи}
    \subsection{Задачи лабораторной работы}
    Задачи лабораторной работы:
    \begin{itemize}
        \item изучить методы статического тестирование;
        \item провести статическое тестирование программы;
        \item проанализировать полученный результат;
        \item рассмотреть рекомендации статического анализатора и при необходимости внести изменения в программу.
    \end{itemize}


    \newpage
    \section {Статическое тестирование}
    Статическое тестирование — это оценка элемента тестирования, при которой не происходит выполнения кода, и которая может быть проведена вручную или с помощью инструментариев. Объектом тестирования может быть документация или исходный код, а сам процесс возможен на любом этапе жизненного цикла ПО (Согласно ГОСТ Р 56920-2024 (раздел 5.5.2)).

    Статическое тестирование включает в себя:
    \begin{itemize}
        \item Проверку документации: требования, спецификации, архитектурные решения.
        \item Анализ исходного кода: поиск синтаксических ошибок, нарушений стандартов
        кодирования и потенциальных уязвимостей.        
    \end{itemize}

    Цель статического тестирования — выявление дефектов на ранних стадиях разработки, что снижает затраты на их исправление. Как отмечает Гленфорд Майерс
    в книге «Искусство тестирования», до 60\% ошибок можно обнаружить до запуска
    программы. Это делает статическое тестирование критически важным инструментом для повышения качества ПО.

    \subsection{Основные формы статического тестирования}
    Используются специальные инструменты -- «статические анализаторы», которые автоматически:

    \begin{itemize}
        \item Проверяют соответствие кода стандартам кодирования (Code Style, Coding
        Guidelines).
        \item Ищут потенциальные ошибки (например, неиспользуемые переменные, некорректные приведения типов, опасные конструкции).
        \item Указывают на потенциальные уязвимости в безопасности (например, возможности для SQL-инъекций, потенциальные переполнения буфера).
        \item Анализируют потоки данных, чтобы понять, где значения могут принимать
        нежелательные (NullPointerException и др.) значения.
    \end{itemize}

    
    \subsection{Разница между статическим анализатором и инспекцией кода за столом}
    \subsubsection*{Способ выполнения}
    \begin{itemize}
        \item Статический анализатор: запускается автоматически на исходном коде и выдает отчёт об обнаруженных проблемах. Анализатор следует набору заранее
        заданных правил (линейный и/или межпроцедурный анализ, анализ потока
        данных и т. д.).
        \item Инспекция кода за столом: проводится людьми (разработчиками, тестировщиками). Участники встречи просматривают код построчно (или анализируют его логические куски) и обсуждают архитектурные, логические, стилевые и
        другие аспекты.
    \end{itemize}

    \subsubsection*{Область охвата}
    \begin{itemize}
        \item  Статический анализатор:
        \begin{itemize}
            \item  Ориентирован в основном на типичные ошибки и «сигнализирует» о
            потенциальных проблемах, отклонениях от правил кодирования, уязвимостях в безопасности.
            \item Хорошо справляется с рутинным поиском большого количества распространённых проблем (например, неиспользуемые переменные, неочевидные «if» без «else», выход за границы массива и т. п.).            
        \end{itemize}

        \item Инспекция кода:
        \begin{itemize}
            \item Позволяет вскрыть более сложные логические ошибки, несоответствие
            требованиям, некорректную бизнес-логику.
            \item Во время обсуждения могут выявиться проблемы, которые невозможно уловить статическим анализатором: «Почему этот алгоритм выбран
            именно так?», «Соответствует ли это бизнес-требованиям?», «Оптимальна ли структура данных?», «Легко ли будет поддерживать этот код?».
        \end{itemize}
    \end{itemize}

    \subsubsection*{Глубина и виды обнаруживаемых дефектов}
    \begin{itemize}
        \item Статический анализатор: находит скорее «структурные» и «синтаксические»
        дефекты и уязвимости (неиспользуемые переменные, неправильные операции
        с памятью, отсутствие проверок). Может не понимать, «хорош ли» сам алгоритм.
        \item Инспекция кода: ориентирована на логику, архитектуру, читаемость, потенциальные проблемы взаимодействия модулей. Тут важны не только дефекты
        самого кода, но и соответствует ли он требованиям или лучшим практикам
        проектирования.
    \end{itemize}

    \subsubsection*{Скорость и автоматизация}
    \begin{itemize}
        \item Статический анализатор: работает быстро (особенно если хорошо интегрирован в CI/CD); выдаёт отчёты сразу после запуска.
        \item Инспекция кода: процесс требует участия людей и времени на обсуждение.
        Однако именно в этом процессе выявляются «глубинные» проблемы, которые
        не найдёт автоматизированный инструмент.
    \end{itemize}

    \subsubsection*{Результаты и интерпретация}
    \begin{itemize}
        \item Статический анализатор:
        \begin{itemize}
            \item Даёт отчёты (логи, списки ошибок/предупреждений) -- но они нуждаются в
            интерпретации человеком, поскольку есть ложные срабатывания (false
            positives).
            \item Для принятия решения о серьёзности проблемы часто всё равно приходится просматривать код.
        \end{itemize}
        \item Инспекция кода:
        \begin{itemize}
            \item Часто приводит не только к обнаружению ошибок, но и к улучшению
            совместной экспертизы в команде.
            \item Может завершиться рекомендациями по рефакторингу, изменению архитектуры, или даже пересмотром требований.
        \end{itemize}
    \end{itemize}


    \newpage
    \section {Описание приложения и среды разработки}
    Название программы: Генератор паролей.
    
    Задача программы: Сгенерировать пароль с параметрами, заданными пользователем.

    Дано: 
    \begin{itemize}
        \item число -- длина генерируемого пароля;
        \item строка <<yes>> или <<no>>, если пользователь введёт строку <<yes>, то в пароле будут использоваться строчные буквы;
        \item строка <<yes>> или <<no>>, если пользователь введёт строку <<yes>, то в пароле будут использоваться заглавные буквы;
        \item строка <<yes>> или <<no>>, если пользователь введёт строку <<yes>, то в пароле будут использоваться цифры;
        \item строка <<yes>> или <<no>>, если пользователь введёт строку <<yes>, то в пароле будут использоваться спецсимволы;
        \item число -- минимальное количество строчных букв в пароле;
        \item число -- минимальное количество заглавных букв в пароле;
        \item число -- минимальное количество цифр в пароле;
        \item число -- минимальное количество спецсимволов;
    \end{itemize} 

    Ограничения: 
    \begin{itemize}
        \item допустимая длина пароля -- от 1 до 100 символов;
        \item минимальное количество строчных букв -- 0;
        \item минимальное количество заглавных букв -- 0;
        \item минимальное количество цифр -- 0;
        \item минимальное количество спецсимволов -- 0;
        \item сумма минимального количества строчных букв, заглавных букв, цифр и спецсимволов не может превышать длину пароля.
    \end{itemize}

    Программа была написана на языке Python. В качестве среды разработки использовалось лицензионное программное обеспечение для редактирования текста -- Microsoft Visual Studio Code.


    \newpage
    \subsection{Спецификация тестируемой программы}

    Спецификация программы представлена в таблице~\ref{tbl:spec}.

    \begin{table}[h!]
        \centering
        \caption{Спецификация.}
        \footnotesize
        \begin{tabularx}{\textwidth}{|X|X|X|}
        \hline
        \textbf{Входные данные} & \textbf{Выходные данные} & \textbf{Комментарий} \\
        \hline
        10 y y y y 1 1 1 1 & 6KoL4Tfn*M & Программа сгенерировала и вывела на экран пароль с заданными параметрами. \\
        \hline
        -10 y y y y 1 1 1 1 & Ошибка: значение должно быть не меньше 1. Попробуйте снова. & Программа вывела на экран сообщение о некорректном пользовательском вводе. \\
        \hline
        10 abc y y y 1 1 1 1 & Ошибка: введите 'yes' (или 'y') или 'no' (или 'n'). & Программа вывела на экран сообщение о некорректном пользовательском вводе. \\
        \hline
        10 abc y y y -1 1 1 1 & Ошибка: значение должно быть не меньше 0. Попробуйте снова. & Программа вывела на экран сообщение о некорректном пользовательском вводе. \\
        \hline
        10 abc y y y 5 5 5 5 & Ошибка: сумма минимальных значений (103) превышает длину пароля (10). Пожалуйста, введите параметры заново. & Программа вывела на экран сообщение о некорректном пользовательском вводе. \\
        \hline
        \end{tabularx}
        \label{tbl:spec}
    \end{table}

    \subsection{Исходный код программы тестируемой программы}

    Исходный код программы представлен в листинге~\ref{lst:code}.

\begin{lstlisting}[caption={Исходный код.}, label={lst:code}]
import random
import string

max_password_length = 100  # Максимальная длина пароля


def get_valid_int(prompt, min_value=0, max_value=None):
    while True:
        user_input = input(prompt).strip()
        if not user_input:
            print("Ошибка: ввод не должен быть пустым. Попробуйте снова.")
            continue
        try:
            value = int(user_input)
            if value < min_value:
                print(
                    f"Ошибка: значение должно быть не меньше {min_value}. Попробуйте снова."
                )
                continue
            if max_value and value > max_value:
                print(
                    f"Ошибка: значение должно быть не больше {max_value}. Попробуйте снова."
                )
                continue
            return value
        except ValueError:
            print("Ошибка: введите корректное целое число.")


def get_yes_no(prompt):
    while True:
        user_input = input(prompt).strip().lower()
        if user_input in ["yes", "y"]:
            return True
        if user_input in ["no", "n"]:
            return False
        print("Ошибка: введите 'yes' (или 'y') или 'no' (или 'n').")



def get_user_input():
    """Запрашивает у пользователя параметры генерации пароля с проверкой ввода."""
    global max_password_length

    length = get_valid_int(
        f"Введите длину пароля (1-{max_password_length}): ",
        min_value=1,
        max_value=max_password_length,
    )

    use_lower = get_yes_no("Использовать строчные буквы? (yes/y, no/n): ")
    use_upper = get_yes_no("Использовать заглавные буквы? (yes/y, no/n): ")
    use_digits = get_yes_no("Использовать цифры? (yes/y, no/n): ")
    use_special = get_yes_no("Использовать спецсимволы (!@#$%^&*)? (yes/y, no/n): ")

    # Проверяем, что хотя бы один тип символов выбран
    if not (use_lower or use_upper or use_digits or use_special):
        print("Ошибка: необходимо выбрать хотя бы один тип символов.")
        return get_user_input()  # Повторный ввод всех данных

    # Запрашиваем минимальное количество каждого типа символов
    min_lower = (
        get_valid_int("Минимальное количество строчных букв: ", 0) if use_lower else 0
    )
    min_upper = (
        get_valid_int("Минимальное количество заглавных букв: ", 0) if use_upper else 0
    )
    min_digits = get_valid_int("Минимальное количество цифр: ", 0) if use_digits else 0
    min_special = (
        get_valid_int("Минимальное количество спецсимволов: ", 0) if use_special else 0
    )

    return (
        length,
        use_lower,
        use_upper,
        use_digits,
        use_special,
        min_lower,
        min_upper,
        min_digits,
        min_special,
    )


def validate_input(length, min_lower, min_upper, min_digits, min_special):
    """Проверяет, что длина пароля больше суммы минимальных значений."""
    total_required = min_lower + min_upper + min_digits + min_special
    if total_required > length:
        print(
            f"Ошибка: сумма минимальных значений ({total_required}) превышает длину пароля ({length})."
        )
        return False
    return True


def generate_mandatory_chars(
    min_lower,
    min_upper,
    min_digits,
    min_special,
    lower_chars,
    upper_chars,
    digit_chars,
    special_chars,
):
    """Генерирует обязательные символы пароля."""
    password = (
        random.choices(lower_chars, k=min_lower)
        + random.choices(upper_chars, k=min_upper)
        + random.choices(digit_chars, k=min_digits)
        + random.choices(special_chars, k=min_special)
    )
    return password


def fill_password(password, length, all_chars):
    """Дополняет пароль случайными символами до нужной длины."""
    remaining_length = length - len(password)
    password += random.choices(all_chars, k=remaining_length)
    return password


def shuffle_password(password):
    """Перемешивает символы пароля случайным образом."""
    random.shuffle(password)
    return "".join(password)


def generate_password(
    length,
    use_lower,
    use_upper,
    use_digits,
    use_special,
    min_lower,
    min_upper,
    min_digits,
    min_special,
):
    """Генерирует пароль с учётом заданных параметров."""
    lower_chars = string.ascii_lowercase if use_lower else ""
    upper_chars = string.ascii_uppercase if use_upper else ""
    digit_chars = string.digits if use_digits else ""
    special_chars = "!@#$%^&*" if use_special else ""

    all_chars = lower_chars + upper_chars + digit_chars + special_chars

    while not validate_input(length, min_lower, min_upper, min_digits, min_special):
        print("Пожалуйста, введите параметры заново.")
        return generate_password(*get_user_input())

    password = generate_mandatory_chars(
        min_lower,
        min_upper,
        min_digits,
        min_special,
        lower_chars,
        upper_chars,
        digit_chars,
        special_chars,
    )
    password = fill_password(password, length, all_chars)
    password = shuffle_password(password)

    return password


def main():
    """Основная функция программы."""
    max_password_length = 100
    user_data = get_user_input()
    password = generate_password(*user_data)
    print("Сгенерированный пароль:", password)


if __name__ == "__main__":
    main()
\end{lstlisting}
    

    \section{Статический анализ кода приложения}
    \subsection{Описание выбранного инструмента для статического анализа кода}

    В качестве инструмента статического анализа кода проекта был выбран Pylint
    версии 3.3.6. Pylint - это программа для проверки исходного кода, ошибок и качества
    для языка программирования Python. Он назван в соответствии с общепринятым в
    Python соглашением о префиксе «py» и отсылкой к программе lint для программирования на C. Он следует стилю, рекомендованному PEP 8, руководством по стилю
    Python. Он похож на Pychecker и Pyflakes, но включает в себя следующие функции:
    \begin{itemize}
        \item Проверка длины каждой строки;
        \item Проверка правильности формирования имен переменных в соответствии со
        стандартом кодирования проекта;
        \item Проверка того, что заявленные интерфейсы действительно реализованы.
    \end{itemize}

    Pylint классифицирует свои сообщения об ошибках и предупреждениях по категориям, каждая из которых обозначается соответствующим префиксом. Основные
    виды сообщений следующие

    \begin{itemize}
        \item C (Convention): Сообщения, связанные со стилем оформления кода (например,
        нарушение соглашений PEP8), именованием переменных, форматированием и
        т.д.
        \item R (Refactor): Рекомендации по рефакторингу кода для улучшения читаемости,
        структуры и поддерживаемости. Эти сообщения помогают улучшить архитектуру кода.
        \item W (Warning): Предупреждения о потенциальных проблемах, которые могут
        привести к ошибкам или неожиданному поведению во время выполнения. Например, возможное использование необъявленной переменной.
        \item E (Error): Ошибки, которые скорее всего приведут к сбоям выполнения программы, такие как неправильное использование синтаксиса, отсутствие необходимых атрибутов или функций.
        \item F (Fatal): Критические ошибки, при обнаружении которых анализ кода прерывается. Обычно это ошибки синтаксиса или другие проблемы, которые делают
        дальнейший анализ невозможным.                
    \end{itemize}

    \subsection{Разбор Pylint правил из группы «Convention»}
    Эти проверки нацелены на то, чтобы код соответствовал принятым соглашениям о стиле (например, PEP8) и был легко читаемым.
    \begin{itemize}
        \item Именование:
        \begin{itemize}
            \item Проверяется, чтобы имена классов, функций, переменных, модулей и
            констант соответствовали принятым стандартам.
            \item Например, классы должны именоваться в стиле CamelCase (например, MyClass), а функции и переменные – в snake\_case (например,
            my\_function, my\_variable).
            \item Также проверяются длина имен и их осмысленность, чтобы они точно
            отражали назначение объекта в коде.
        \end{itemize}
        \item Стиль оформления:
        \begin{itemize}
            \item Длина строк: Pylint следит за тем, чтобы строки не превышали установленную длину (обычно 79 или 99 символов в зависимости от конфигурации).
            \item Отступы и пробелы: Контролируется корректное использование отступов (обычно 4 пробела), пробелов вокруг операторов, после запятых и
            т.д.
            \item Разбиение на строки: Рекомендуется правильно разбивать длинные выражения или вызовы функций на несколько строк для лучшей читаемости.
            \item Пустые строки: Проверяется количество пустых строк между функциями и классами для поддержания визуальной структуры кода.
        \end{itemize}
        \item Документация:
        \begin{itemize}
            \item Docstrings: Pylint обращает внимание на наличие строк документации
            (docstrings) в модулях, классах, функциях и методах.
            \item Формат документации: Документация должна быть оформлена согласно принятым стандартам (например, в формате reStructuredText или
            Google style), чтобы обеспечить понятное описание функционала и параметров.            
        \end{itemize}
    \end{itemize}

    \subsection{ Разбор Pylint правил из группы «Refactor»}
    Эта группа сообщений направлена на улучшение структуры кода, его упрощение
    и повышение поддерживаемости

    \begin{itemize}
        \item Сложность функций:
        \begin{itemize}
            \item  Цикломатическая сложность: Анализируется количество ветвлений,
            циклов и условных операторов. Функции с высокой сложностью могут
            быть трудными для тестирования и отладки.
            \item Слишком длинные функции: Если функция слишком большая или содержит множество аргументов, Pylint может рекомендовать её разбить
            на более мелкие части.
        \end{itemize}
        \item Дублирование кода:
        \begin{itemize}
            \item Проверка на повторяющиеся участки кода, что может указывать на возможность объединения логики в одну функцию или класс.
            \item Цель – уменьшить количество повторений, чтобы изменение в одной
            части кода не требовало повторения исправлений в нескольких местах.            
        \end{itemize}
        \item Структурные проблемы:
        \begin{itemize}
            \item Обнаружение слишком больших классов или методов, которые выполняют сразу несколько задач
            \item Рекомендации по разделению ответственности (например, принцип
            единственной ответственности из SOLID) для улучшения модульности
            и тестируемости кода.
        \end{itemize}
    \end{itemize}

    \subsection{Разбор Pylint правил из группы «Warning»}
    Эта категория охватывает сообщения, которые указывают на потенциальные
    проблемы, не являющиеся критическими ошибками, но способными привести к
    неожиданному поведению.

    \begin{itemize}
        \item Неиспользуемые элементы:
        \begin{itemize}
            \item Переменные: Если переменная объявлена, но не используется, Pylint сообщает об этом, что помогает избежать загромождения кода.
            \item Импорты: Неиспользуемые модули или функции, импортированные в
            начале файла, могут быть отмечены для удаления.
            \item Аргументы функций: Иногда функция принимает аргументы, которые
            не используются в теле, что может быть сигналом к тому, что интерфейс
            функции следует пересмотреть.            
        \end{itemize}
        \item Подозрительные конструкции:
        \begin{itemize}
            \item Использование переменных до объявления: Если переменная используется до того, как ей было присвоено значение, это может привести к
            ошибкам.
            \item Использование изменяемых значений по умолчанию: Применение изменяемых объектов (например, списков или словарей) в качестве значений
            по умолчанию в параметрах функций может привести к неожиданным эффектам.
        \end{itemize}
        \item Ошибки логики:
        \begin{itemize}
            \item Порой конструкция кода может быть синтаксически корректной, но её
            поведение может быть неочевидным или потенциально приводить к логическим ошибкам (например, некорректное сравнение или неверное использование операторов).
        \end{itemize}
    \end{itemize}

    \newpage
    \section{Процесс тестирования}
    \subsection{Подготовка}

    Перед использованием PyLint необходимо установить Python и PIP с официального сайта. Затем создать виртуальное окружение с помощью команды \texttt{virtualenv venv}.

    Чтобы установить PyLint, достаточно выполнить команду \texttt{pip install pylint} внутри виртуального окружения.

    Для запуска статического анализа достаточно выполнить команду \texttt{pylint file.py}, где \texttt{file.py} -- это название файла с исходным кодом.

    
    \subsection{Результат работы анализатора}

    Полный вывод команды \texttt{pylint passgen.py} представлен в листинге~\ref{lst:res}. Статический анализатор вывел 12 сообщений о различных проблемах в коде. Они связаны с несоответствием стиля именования, отсутствием документации в модуле и функциях, а также слишком длинными строками. Кроме того, есть предупреждения о неинициализированной глобальной переменной, переопределении имени переменной во внутренней области видимости, а также о слишком большом количестве аргументов в функциях. PyLint также даёт общую оценку кода. Для моей программы он вывел оценку 8.48 из 10, что означает, что код в целом неплох, но его можно улучшить.

\begin{lstlisting}[caption={Результат выполнения команды \texttt{pylint passgen.py}.}, label={lst:res}]
************* Module passgen
passgen.py:91:0: C0301: Line too long (103/100) (line-too-long)
passgen.py:1:0: C0114: Missing module docstring (missing-module-docstring)
passgen.py:4:0: C0103: Constant name "max_password_length" doesn't conform to UPPER_CASE naming style (invalid-name)
passgen.py:7:0: C0116: Missing function or method docstring (missing-function-docstring)
passgen.py:30:0: C0116: Missing function or method docstring (missing-function-docstring)
passgen.py:43:4: W0602: Using global for 'max_password_length' but no assignment is done (global-variable-not-assigned)
passgen.py:97:0: R0913: Too many arguments (8/5) (too-many-arguments)
passgen.py:97:0: R0917: Too many positional arguments (8/5) (too-many-positional-arguments)
passgen.py:130:0: R0913: Too many arguments (9/5) (too-many-arguments)
passgen.py:130:0: R0917: Too many positional arguments (9/5) (too-many-positional-arguments)
passgen.py:171:4: W0621: Redefining name 'max_password_length' from outer scope (line 4) (redefined-outer-name)
passgen.py:171:4: W0612: Unused variable 'max_password_length' (unused-variable)

-------------------------------------------
Your code has been rated at 8.48/10 (previous run: 8.48/10, +0.00)
\end{lstlisting}

    \subsection{Результат улучшения кода}

    В соответствии с рекомендациями PyLint в исходный код были добавлены комментарии с документацией для всего модуля и функций, слишком длинные строки были разбиты на более короткие и удобочитаемые, неинициализированная глобальная переменная была удалена, была удалена одна переменная из локальной области видимости, перекрывавшая глобальную переменную, имена всех переменных были приведены в соответствие с принятыми соглашениями языка Python.

    После внесения перечисленных выше изменений статический анализатор PyLint был запущен ещё раз на обновлённом файле с исходным кодом. Результат запуска представлен в листинге~\ref{lst:res-new}. В этот раз PyLint не вывел никаких сообщений.

\begin{lstlisting}[caption={Результат работы PyLint на обновлённом файле с исходным кодом.}, label={lst:res-new}]
-------------------------------------
Your code has been rated at 10.00/10 
\end{lstlisting}

    Обновлённый код программы представлен в листинге~\ref{lst:code-new}.

\begin{lstlisting}[caption={Обновлённый код программы.}, label={lst:code-new}]
"""
Модуль для генерации безопасных паролей с разными настройками.
Пользователь может задавать длину, типы символов и минимальное количество каждого типа.
"""

import random
import string

MAX_PASSWORD_LENGTH = 100  # Максимальная длина пароля


def get_valid_int(prompt, min_value=0, max_value=None):
    """Запрашивает у пользователя целое число, проверяя корректность ввода."""
    while True:
        user_input = input(prompt).strip()
        if not user_input:
            print("Ошибка: ввод не должен быть пустым. Попробуйте снова.")
            continue
        try:
            value = int(user_input)
            if value < min_value:
                print(
                    f"Ошибка: значение должно быть не меньше {min_value}. Попробуйте снова."
                )
                continue
            if max_value and value > max_value:
                print(
                    f"Ошибка: значение должно быть не больше {max_value}. Попробуйте снова."
                )
                continue
            return value
        except ValueError:
            print("Ошибка: введите корректное целое число.")


def get_yes_no(prompt):
    """Запрашивает у пользователя 'yes'/'y' или 'no'/'n', проверяя корректность ввода."""
    while True:
        user_input = input(prompt).strip().lower()
        if user_input in ["yes", "y"]:
            return True
        if user_input in ["no", "n"]:
            return False
        print("Ошибка: введите 'yes' (или 'y') или 'no' (или 'n').")


def get_user_input():
    """Запрашивает у пользователя параметры генерации пароля с проверкой ввода."""
    settings = {
        "length": get_valid_int(
            f"Введите длину пароля (1-{MAX_PASSWORD_LENGTH}): ",
            min_value=1,
            max_value=MAX_PASSWORD_LENGTH,
        ),
        "use_lower": get_yes_no("Использовать строчные буквы? (yes/y, no/n): "),
        "use_upper": get_yes_no("Использовать заглавные буквы? (yes/y, no/n): "),
        "use_digits": get_yes_no("Использовать цифры? (yes/y, no/n): "),
        "use_special": get_yes_no(
            "Использовать спецсимволы (!@#$%^&*)? (yes/y, no/n): "
        ),
    }

    # Проверяем, что хотя бы один тип символов выбран
    if not any(
        [
            settings["use_lower"],
            settings["use_upper"],
            settings["use_digits"],
            settings["use_special"],
        ]
    ):
        print("Ошибка: необходимо выбрать хотя бы один тип символов.")
        return get_user_input()  # Повторный ввод всех данных

    # Запрашиваем минимальное количество каждого типа символов
    settings["min_lower"] = (
        get_valid_int("Минимальное количество строчных букв: ", 0)
        if settings["use_lower"]
        else 0
    )
    settings["min_upper"] = (
        get_valid_int("Минимальное количество заглавных букв: ", 0)
        if settings["use_upper"]
        else 0
    )
    settings["min_digits"] = (
        get_valid_int("Минимальное количество цифр: ", 0)
        if settings["use_digits"]
        else 0
    )
    settings["min_special"] = (
        get_valid_int("Минимальное количество спецсимволов: ", 0)
        if settings["use_special"]
        else 0
    )

    return settings


def validate_input(settings):
    """Проверяет, что длина пароля больше суммы минимальных значений."""
    total_required = sum(
        [
            settings["min_lower"],
            settings["min_upper"],
            settings["min_digits"],
            settings["min_special"],
        ]
    )
    if total_required > settings["length"]:
        print(
            f"Ошибка: сумма минимальных значений ({total_required})"
            f" превышает длину пароля ({settings['length']})."
        )
        return False
    return True


def generate_mandatory_chars(settings, char_sets):
    """Генерирует обязательные символы пароля."""
    password = (
        random.choices(char_sets["lower"], k=settings["min_lower"])
        + random.choices(char_sets["upper"], k=settings["min_upper"])
        + random.choices(char_sets["digits"], k=settings["min_digits"])
        + random.choices(char_sets["special"], k=settings["min_special"])
    )
    return password


def fill_password(password, length, all_chars):
    """Дополняет пароль случайными символами до нужной длины."""
    remaining_length = length - len(password)
    password += random.choices(all_chars, k=remaining_length)
    return password


def shuffle_password(password):
    """Перемешивает символы пароля случайным образом."""
    random.shuffle(password)
    return "".join(password)


def generate_password(settings):
    """Генерирует пароль с учётом заданных параметров."""
    char_sets = {
        "lower": string.ascii_lowercase if settings["use_lower"] else "",
        "upper": string.ascii_uppercase if settings["use_upper"] else "",
        "digits": string.digits if settings["use_digits"] else "",
        "special": "!@#$%^&*" if settings["use_special"] else "",
    }

    all_chars = "".join(char_sets.values())

    while not validate_input(settings):
        print("Пожалуйста, введите параметры заново.")
        return generate_password(get_user_input())

    password = generate_mandatory_chars(settings, char_sets)
    password = fill_password(password, settings["length"], all_chars)
    password = shuffle_password(password)

    return password


def main():
    """Основная функция программы."""
    user_settings = get_user_input()
    password = generate_password(user_settings)
    print("Сгенерированный пароль:", password)


if __name__ == "__main__":
    main()
\end{lstlisting}

    \newpage
    \section*{Заключение}
    \addcontentsline{toc}{section}{Заключение}

    В ходе выполнения данной лабораторной работы было проведено статистическое тестирование для программы: <<Генератор паролей>>.

    Были найдены следующие проблемы:
    \begin{itemize}
        \item 5 нарушений правил оформления исходного кода;
        \item 3 предупреждения о возможных ошибках в исходном коде;
        \item 4 рекомендации по рефакторингу исходного кода.
    \end{itemize}

    В результате проделанной работы программный код был исправлен в соответствии с рекомендациями статистического анализатора. На примере небольшой программы была наглядно продемонстрирована польза от использования статических анализаторов кода. Были сделаны выводы о том, что статистическое тестирование позволяет выявить некорректность как в логике работы программы, так и в стиле её оформления. На примере PyLint был получен первый опыт использования статических анализаторов кода.

    В процессе статистического тестирования были выявлены некоторые недостатки, которых не удалось обнаружить во время инспекции за столом. Поэтому статистическое тестирование является хорошим дополнением к инспекции за столом.
    К тому же статическое тестирование не столь трудозатрано и его можно автоматизировать.

\newpage
\section*{Список литературы}
\addcontentsline{toc}{section}{Список литературы}

\vspace{-1.5cm}
\begin{thebibliography}{0}
    \bibitem{mayers}
    Майерс, Г. Искусство тестирования программ. -- Санкт-Петербург: Диалектика, 2012 г.
\end{thebibliography}

\end{document}