From 9695192bfbeb0acd06ee307923ef818bfb22fa1e Mon Sep 17 00:00:00 2001 From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com> Date: Tue, 14 Jan 2025 22:00:04 -0500 Subject: [PATCH 1/5] Added MIM, SCL and Smell modules to MG doc (#298, #299, #302) --- docs/Design/SoftArchitecture/MG.tex | 39 ++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/docs/Design/SoftArchitecture/MG.tex b/docs/Design/SoftArchitecture/MG.tex index 86f8d76f..731b42f4 100644 --- a/docs/Design/SoftArchitecture/MG.tex +++ b/docs/Design/SoftArchitecture/MG.tex @@ -192,7 +192,8 @@ \section{Module Hierarchy} \label{SecMH} \begin{description} \item [\refstepcounter{mnum} \mthemnum \label{mHH}:] Hardware-Hiding Module -\item ... +\item [\refstepcounter{mnum} \mthemnum \label{mMIMR}:] MakeStaticRefactorer Module +\item [\refstepcounter{mnum} \mthemnum \label{mSCLR}:] UseListAccumulationRefactorer Module \end{description} @@ -207,8 +208,8 @@ \section{Module Hierarchy} \label{SecMH} \midrule \multirow{7}{0.3\textwidth}{Behaviour-Hiding Module} & ?\\ -& ?\\ -& ?\\ +& MakeStaticRefactorer Module\\ +& UseListAccumulationRefactorer Module\\ & ?\\ & ?\\ & ?\\ @@ -268,6 +269,8 @@ \subsection{Hardware Hiding Modules (\mref{mHH})} \subsection{Behaviour-Hiding Module} +% [Record, Library, Abstract Object, or Abstract Data Type] + \begin{description} \item[Secrets:]The contents of the required behaviours. \item[Services:]Includes programs that provide externally visible behaviour of @@ -278,15 +281,33 @@ \subsection{Behaviour-Hiding Module} \item[Implemented By:] -- \end{description} -\subsubsection{Input Format Module (\mref{mInput})} +\subsubsection{MakeStaticRefactorer Module (\mref{mMIMR})} + +% [Record, Library, Abstract Object, or Abstract Data Type] + +\begin{description} +\item[Secrets:] How to parse a given code file to its AST representation, how to traverse the AST tree, how to modify specific nodes in the AST tree, how to convert the modified AST tree back to source code and write it to an output file. +\item[Services:] Refactors the \textit{\textbf{Member Ignoring Method (MIM)}} smell in a provided code file to improve energy efficiency. +\item[Implemented By:] EcoOptimizer +\end{description} + +\subsubsection{UseListAccumulationRefactorer Module (\mref{mSCLR})} + +% [Record, Library, Abstract Object, or Abstract Data Type] + +\begin{description} +\item[Secrets:] How to parse a given code file into its AST representation, how to traverse the AST tree, how to find the initializing variable of the string concatenation, how to find the scope of the concatenation, how to modify the given code file in plain text and write it back to an output file. +\item[Services:] Refactors the \textbf{\textit{String Concatenation Inside Loop (SCL)}} smell in a provided code file to improve energy efficiency. +\item[Implemented By:] EcoOptimizer +\end{description} + +\subsubsection{Smell Module (\mref{mSmell})} \begin{description} -\item[Secrets:]The format and structure of the input data. -\item[Services:]Converts the input data into the data structure used by the - input parameters module. +\item[Secrets:] Data structure of a code smell. +\item[Services:] Provides an interface for other modules to access information of a smell object. \item[Implemented By:] [Your Program Name Here] -\item[Type of Module:] [Record, Library, Abstract Object, or Abstract Data Type] - [Information to include for leaf modules in the decomposition by secrets tree.] +\item[Type of Module:] Abstract Data Type \end{description} \subsubsection{Etc.} From 63148f09e4cd4947a6c5d878cb5a77403db270f3 Mon Sep 17 00:00:00 2001 From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com> Date: Wed, 15 Jan 2025 21:21:30 -0500 Subject: [PATCH 2/5] Added MIS for some modules Modules: Smell, Base Refactorer, UseListAccumulation, MakeStatic closes #298, #299, #302, #315 --- docs/Design/SoftDetailedDes/MIS.tex | 329 ++++++++++++++++++++++++++++ 1 file changed, 329 insertions(+) diff --git a/docs/Design/SoftDetailedDes/MIS.tex b/docs/Design/SoftDetailedDes/MIS.tex index b09df2c1..df611849 100644 --- a/docs/Design/SoftDetailedDes/MIS.tex +++ b/docs/Design/SoftDetailedDes/MIS.tex @@ -227,6 +227,335 @@ \subsubsection{Local Functions} explicitly. Even if they are implemented, they are not exported; they only have local scope.} +\newpage + +\section{MIS of Base Refactorer} \label{mis:baseR} + +\texttt{BaseRefactorer} + +\subsection{Module} + +The interface that all refactorers of this system will inherit from. + +\subsection{Uses} + +None + +\subsection{Syntax} +\noindent +\textbf{Exported Constants}: None + +\noindent +\textbf{Exported Access Programs}: + +\begin{tabularx}{\linewidth}{|l|>{\raggedright\arraybackslash}X|l|l|} + \toprule Name & In & Out & Exceptions \\\hline + \midrule + \texttt{\_\_init\_\_} & \texttt{output\_dir: Path} & None & None \\\hline + \texttt{refactor} & \texttt{file\_path: Path, pylint\_smell: dict, initial\_emissions: float} & None & None \\ + \hline + \bottomrule +\end{tabularx} + +\subsection{Semantics} + +\subsubsection*{State Variables} +\begin{itemize} + \item \texttt{temp\_dir: Path}: Directory path for storing refactored files. +\end{itemize} + +\subsubsection*{Environment Variables} +None + +\subsubsection*{Assumptions} +\begin{itemize} + \item \texttt{output\_dir} exists or can be created, and write permissions are available. +\end{itemize} + +\subsubsection*{Access Routine Semantics} + +\paragraph{\texttt{\_\_init\_\_(self, output\_dir: Path)}} +\begin{itemize} + \item \textbf{transition}: Initializes the \texttt{temp\_dir} variable within \texttt{output\_dir}. + \item \textbf{output}: None. + \item \textbf{exception:} None. +\end{itemize} + +\paragraph{\texttt{refactor(self, file\_path: Path, pylint\_smell: dict, initial\_emissions: float)}} +\begin{itemize} + \item \textbf{transition}: Abstract method. No transition defined. + \item \textbf{output}: None. + \item \textbf{exception:} None. +\end{itemize} + +\subsubsection*{Local Functions} +None. + +\newpage + +\section{MIS of Smell Data Type} \label{mis:smell} +\texttt{Smell} + +\subsection{Module} +Contains data related to a code smell. + +\subsection{Uses} +None + +\subsection{Syntax} +\noindent +\textbf{Exported Constants}: None + +\noindent +\textbf{Exported Access Programs}: None + +\subsection{Semantics} + +\subsubsection*{State Variables} +\begin{itemize} + \item \texttt{absolutePath: str}: Absolute path to the source file containing the smell. + \item \texttt{column: int}: Starting column in the source file where the smell is detected. + \item \texttt{confidence: str}: Confidence level for the smell detection. + \item \texttt{endColumn: int | None}: Ending column for the smell location, if applicable. + \item \texttt{endLine: int | None}: Ending line number for the smell location, if applicable. + \item \texttt{occurences: dict}: Contains positional data related to where the smell is located in a code file. + \item \texttt{message: str}: Descriptive message explaining the smell. + \item \texttt{messageId: str}: Unique identifier for the specific message or warning. + \item \texttt{module: str}: Module or component name containing the smell. + \item \texttt{obj: str}: Specific object associated with the smell. + \item \texttt{path: str}: Relative path to the source file from the project root. + \item \texttt{symbol: str}: Symbol or code construct involved in the smell. + \item \texttt{type: str}: Type or category of the smell. +\end{itemize} + +\subsubsection*{Environment Variables} +None + +\subsubsection*{Assumptions} +\begin{itemize} + \item All values provided to the fields of \texttt{Smell} conform to the expected data types and constraints. +\end{itemize} + +\subsubsection*{Access Routine Semantics} + +\paragraph{\texttt{Smell()}} +\begin{itemize} + \item \textbf{transition}: Creates a dictionary-like structure with the defined attributes representing a code smell. + \item \textbf{output}: Returns a \texttt{Smell} instance. +\end{itemize} + +\subsubsection*{Local Functions} +None. + + +\newpage + +\section{MIS of Use List Accumulation Refactorer} \label{mis:ListAccum} + +\texttt{UseListAccumulationRefactorer} + +\subsection{Module} + +The \texttt{UseListAccumulationRefactorer} module identifies and refactors +string concatenations in loops in Python code to improve the performance and energy efficiency of the software. It specifically handles these concatenations by, instead, adding the string for each iteration to a list that is then converted to a string using Python's \texttt{join()} function, ensuring proper refactoring while maintaining the original functionality. + +\subsection{Uses} +\begin{itemize} + \item Uses \texttt{Smell} interface for data access + \item Inherits from \texttt{BaseRefactorer} + \item Inherits from Python's \texttt{ast} module's \texttt{NodeTransformer} +\end{itemize} + +\subsection{Syntax} +\noindent +\textbf{Exported Constants}: None + +\noindent +\textbf{Exported Access Programs}: + +\begin{tabularx}{\linewidth}{| + l| + >{\raggedright\arraybackslash}X| + l| + l|} + \toprule Name & In & Out & Exceptions \\ + \midrule + \texttt{\_\_init\_\_} & \texttt{output\_dir: Path} & None & None \\ + \hline + \texttt{refactor} & \texttt{file\_path: Path, pylint\_smell: Smell, initial\_emissions: Real} & None & \texttt{TypeError}, \texttt{IOError} \\ + \hline + \texttt{visit} & \texttt{node: nodes.NodeNG} & None & None \\ + \hline + \texttt{find\_last\_assignment} & \texttt{scope: nodes.NodeNG} & None & \texttt{TypeError} \\ + \hline + \texttt{find\_scope} & None & None & \texttt{TypeError} \\ + \hline + \texttt{add\_node\_to\_body} & \texttt{code\_file: str} & \texttt{str} & \texttt{TypeError} \\ + \bottomrule +\end{tabularx} + +\subsection{Semantics} + +\subsubsection*{State Variables} +\begin{itemize} + \item \texttt{target\_line: int}: Line number where refactoring is applied. + \item \texttt{target\_node: ASTnode}: Node representing the concatenation variable. + \item \texttt{assign\_var: str}: Name of the variable the \texttt{target\_node} represents. + \item \texttt{last\_assign\_node: ASTnode}: Last initialization/assignment of the \texttt{assign\_var} prior to the start of the loop. + \item \texttt{concat\_node: ASTnode}: Node where concatenation occurs. + \item \texttt{scope\_node: ASTnode}: Scope where refactoring is inserted. + \item \texttt{outer\_loop: ASTnode}: Outermost loop before the start of the concatenation. +\end{itemize} + +\subsubsection*{Environment Variables} +None + +\subsubsection*{Assumptions} +\begin{itemize} + \item The input file contains valid Python syntax. + \item \texttt{pylint\_smell} provides a valid line number for the detected code smell. +\end{itemize} + +\subsubsection*{Access Routine Semantics} + +\paragraph{\texttt{\_\_init\_\_(self, output\_dir: Path)}} +\begin{itemize} + \item \textbf{transition}: Initializes the refactorer with \texttt{output\_dir} and sets default state variables. + \item \textbf{output}: None. + \item \textbf{exception}: None +\end{itemize} + +\paragraph{\texttt{refactor(self, file\_path: Path, pylint\_smell: Smell, initial\_emissions: float)}} +\begin{itemize} + \item \textbf{transition}: Parses \texttt{file\_path}, identifies string concatenations in loops, modifies code for list accumulation, and writes refactored code to a file. + \item \textbf{output}: None. + \item \textbf{exception}: Raises \texttt{IOError} if input file cannot be read. Raises \texttt{TypeError} if source file cannot be parsed into an AST. +\end{itemize} + +\paragraph{\texttt{find\_last\_assignment(self, scope: nodes.NodeNG)}} +\begin{itemize} + \item \textbf{transition}: Identifies the last assignment of \texttt{assign\_var} within the given \texttt{scope}. + \item \textbf{output}: None. + \item \textbf{exception}: Raises \texttt{TypeError} if given scope is null. +\end{itemize} + +\paragraph{\texttt{find\_scope(self)}} +\begin{itemize} + \item \textbf{transition}: Finds the scope for refactoring based on AST node ancestry. + \item \textbf{output}: None. + \item \textbf{exception}: Raises \texttt{TypeError} if \texttt{concat\_node} is not set. +\end{itemize} + +\paragraph{\texttt{add\_node\_to\_body(self, code\_file: str)}} +\begin{itemize} + \item \textbf{transition}: Inserts list accumulation and join statements into \texttt{code\_file}. + \item \textbf{output}: Returns the modified source code as a string. + \item \textbf{exception}: Raises \texttt{TypeError} if \texttt{target\_node} or \texttt{outer\_loop} is not set. +\end{itemize} + +\subsubsection*{Local Functions} +Functions for internal AST parsing, node manipulation, and validation are defined within the class but are not exported. + +\newpage + +\section{MIS of Make Method Static Refactorer} \label{mis:MakeStatic} + +\texttt{MakeStaticRefactorer} + +\subsection{Module} + +The \texttt{MakeStaticRefactorer} module identifies and refactors +class methods that don't make use of their instance attributes to improve the readability, performance and energy efficiency of the software. It specifically handles these methods by turning them into static functions and ensuring any calls to this method use the proper calling syntax. This ensures proper refactoring while maintaining the original functionality. + +\subsection{Uses} +\begin{itemize} + \item Uses \texttt{Smell} interface for data access + \item Inherits from \texttt{BaseRefactorer} + \item Inherits from Python's \texttt{ast} module's \texttt{NodeTransformer} +\end{itemize} + +\subsection{Syntax} +\noindent +\textbf{Exported Constants}: None + +\noindent +\textbf{Exported Access Programs}: + +\begin{tabularx}{\linewidth}{|l|>{\raggedright\arraybackslash}X|l|l|} + \toprule Name & In & Out & Exceptions \\ + \midrule + \texttt{\_\_init\_\_} & \texttt{output\_dir: Path} & None & None \\ + \hline + \texttt{refactor} & \texttt{file\_path: Path, pylint\_smell: Smell, initial\_emissions: $\mathbb{R}$} & None & \texttt{TypeError}, \texttt{IOError} \\ + \hline + \texttt{visit\_FunctionDef} & \texttt{node: FunctionDef} & \texttt{FunctionDef} & None \\ + \hline + \texttt{visit\_ClassDef} & \texttt{node: ClassDef} & \texttt{ClassDef} & None \\ + \hline + \texttt{visit\_Call} & \texttt{node: Call} & \texttt{Call} & None \\ + \bottomrule +\end{tabularx} + +\subsection{Semantics} + +\subsubsection*{State Variables} +\begin{itemize} + \item \texttt{target\_line: int}: Line number where refactoring is applied. + \item \texttt{mim\_method\_class: str}: Class name containing the method to refactor. + \item \texttt{mim\_method: str}: Method name to refactor. +\end{itemize} + +\subsubsection*{Environment Variables} +None + +\subsubsection*{Assumptions} +\begin{itemize} + \item The input file contains valid Python syntax. + \item \texttt{pylint\_smell} provides a valid line number for the detected code smell. +\end{itemize} + +\subsubsection*{Access Routine Semantics} + +\paragraph{\texttt{\_\_init\_\_(self, output\_dir: Path)}} +\begin{itemize} + \item \textbf{transition}: Initializes the refactorer with \texttt{output\_dir} and sets default state variables. + \item \textbf{output}: None. + \item \textbf{exception}: None. +\end{itemize} + +\paragraph{\texttt{refactor(self, file\_path: Path, pylint\_smell: Smell, initial\_emissions: float)}} +\begin{itemize} + \item \textbf{transition}: Parses \texttt{file\_path}, identifies the target function, modifies it to be static, and validates refactoring. + \item \textbf{output}: None. + \item \textbf{exception}: Raises \texttt{IOError} if input file cannot be read. Raises \texttt{TypeError} if source file cannot be parsed into an AST. +\end{itemize} + +\paragraph{\texttt{visit\_FunctionDef(self, node: ast.FunctionDef)}} +\begin{itemize} + \item \textbf{transition}: Adds the \texttt{staticmethod} decorator to the target method and removes the \texttt{self} parameter if present. + \item \textbf{output}: Returns the modified \texttt{FunctionDef} node. + \item \textbf{exception}: None +\end{itemize} + +\paragraph{\texttt{visit\_ClassDef(self, node: ast.ClassDef)}} +\begin{itemize} + \item \textbf{transition}: Identifies the class containing the target method. + \item \textbf{output}: Returns the modified \texttt{ClassDef} node. + \item \textbf{exception}: None. +\end{itemize} + +\paragraph{\texttt{visit\_Call(self, node: ast.Call)}} +\begin{itemize} + \item \textbf{transition}: Updates method call references to use the class name instead of \texttt{self}. + \item \textbf{output}: Returns the modified \texttt{Call} node. + \item \textbf{exception}: None. +\end{itemize} + +\subsubsection*{Local Functions} +Functions for internal AST parsing, node transformation, and validation are defined within the class but are not exported. + + + \newpage \bibliographystyle {plainnat} From c0bc64ec8be0412509d561858654cd3e5d828ca6 Mon Sep 17 00:00:00 2001 From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com> Date: Fri, 17 Jan 2025 09:17:11 -0500 Subject: [PATCH 3/5] Added Testing Module to MG (#314) --- docs/Design/SoftArchitecture/MG.tex | 92 ++++++++++++++--------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/docs/Design/SoftArchitecture/MG.tex b/docs/Design/SoftArchitecture/MG.tex index 648aa855..20bcdd83 100644 --- a/docs/Design/SoftArchitecture/MG.tex +++ b/docs/Design/SoftArchitecture/MG.tex @@ -191,12 +191,15 @@ \section{Module Hierarchy} \label{SecMH} actually be implemented. \begin{description} -\item [\refstepcounter{mnum} \mthemnum \label{mHH}:] Hardware-Hiding Module +\item [\refstepcounter{mnum} \mthemnum \label{mSmell}:] Smell Module +\item [\refstepcounter{mnum} \mthemnum \label{mBR}:] BaseRefactorer Module \item [\refstepcounter{mnum} \mthemnum \label{mMIMR}:] MakeStaticRefactorer Module \item [\refstepcounter{mnum} \mthemnum \label{mSCLR}:] UseListAccumulationRefactorer Module \item [\refstepcounter{mnum} \mthemnum \label{mUGENR}:] UseAGeneratorRefactorer Module \item [\refstepcounter{mnum} \mthemnum \label{mCRC}:] CacheRepeatedCallsRefactorer Module \item [\refstepcounter{mnum} \mthemnum \label{mLEC}:] LongElementChainRefactorer Module +\item [\refstepcounter{mnum} \mthemnum \label{mPyA}:] Pylint Analyzer Module +\item [\refstepcounter{mnum} \mthemnum \label{mTest}:] Testing Functionality Module \end{description} @@ -207,10 +210,12 @@ \section{Module Hierarchy} \label{SecMH} \textbf{Level 1} & \textbf{Level 2}\\ \midrule -{Hardware-Hiding Module} & ~ \\ +{Hardware-Hiding Module} & None \\ \midrule -\multirow{7}{0.3\textwidth}{Behaviour-Hiding Module} & CacheRepeatedCallsRefactorer Module\\ +\multirow{7}{0.3\textwidth}{Behaviour-Hiding Module} & Smell Module\\ +& BaseRefactorer Module\\ +& CacheRepeatedCallsRefactorer Module\\ & MakeStaticRefactorer Module\\ & UseListAccumulationRefactorer Module\\ & ?\\ @@ -223,9 +228,9 @@ \section{Module Hierarchy} \label{SecMH} & ?\\ \midrule -\multirow{3}{0.3\textwidth}{Software Decision Module} & {?}\\ -& ?\\ -& ?\\ +\multirow{3}{0.3\textwidth}{Software Decision Module} & Pylint Analyzer Module\\ +& Testing Functionality Module\\ +& Measurements Module\\ \bottomrule \end{tabular} @@ -262,46 +267,29 @@ \section{Module Decomposition} \label{SecMD} (\emph{--}) is shown, this means that the module is not a leaf and will not have to be implemented. -\subsection{Hardware Hiding Modules (\mref{mHH})} +\subsection{Hardware Hiding Modules} -\begin{description} -\item[Secrets:]The data structure and algorithm used to implement the virtual - hardware. -\item[Services:]Serves as a virtual hardware used by the rest of the - system. This module provides the interface between the hardware and the - software. So, the system can use it to display outputs or to accept inputs. -\item[Implemented By:] OS -\end{description} +This system has no hardware components. \subsection{Behaviour-Hiding Module} -\subsubsection{Long Element Chain Module (\mref{mLEC})} - -% [Record, Library, Abstract Object, or Abstract Data Type] +\subsubsection{Smell Module (\mref{mSmell})} \begin{description} - \item[Secrets:] How to parse a given code file to its AST representation, traverse the - AST tree to identify dictionary assignments, analyze the structure of nested dictionaries, - and flatten them. Additionally, it identifies all access calls associated with these dictionaries - in the source code and determines how to update them to reflect the new flattened structure. - \item[Services:] Detects nested dictionaries in the source code using AST parsing, simplifies their - structure by flattening them, and updates all associated access calls throughout the file. This improves - code readability, reduces complexity, and ensures correctness while maintaining the program's intended behavior. - \item[Implemented By:] EcoOptimizer +\item[Secrets:] Data structure of a code smell. +\item[Services:] Provides an interface for other modules to access information of a smell object. +\item[Implemented By:] EcoOptimizer +\item[Type of Module:] Abstract Data Type \end{description} -\subsubsection{Input Format Module (\mref{mInput})} +\subsubsection{Base Refactorer Module (\mref{mBR})} % [Record, Library, Abstract Object, or Abstract Data Type] \begin{description} -\item[Secrets:]The contents of the required behaviours. -\item[Services:]Includes programs that provide externally visible behaviour of - the system as specified in the software requirements specification (SRS) - documents. This module serves as a communication layer between the - hardware-hiding module and the software decision module. The programs in this - module will need to change if there are changes in the SRS. -\item[Implemented By:] -- + \item[Secrets:] None + \item[Services:] Offers an interface for other refactoring modules to implement. + \item[Implemented By:] EcoOptimizer \end{description} \subsubsection{MakeStaticRefactorer Module (\mref{mMIMR})} @@ -323,16 +311,6 @@ \subsubsection{UseListAccumulationRefactorer Module (\mref{mSCLR})} \item[Implemented By:] EcoOptimizer \end{description} -\subsubsection{Smell Module (\mref{mSmell})} - -\begin{description} -\item[Secrets:] Data structure of a code smell. -\item[Services:] Provides an interface for other modules to access information of a smell object. -\item[Implemented By:] [Your Program Name Here] -\item[Type of Module:] [Record, Library, Abstract Object, or Abstract Data Type] - [Information to include for leaf modules in the decomposition by secrets tree.] -\end{description} - \subsubsection{UseAGeneratorRefactorer Module (\mref{mUGENR})} \begin{description} \item[Secrets:] How to parse a given code file to its AST representation, how to traverse the AST tree, how to modify specific nodes in the AST tree, how to convert the modified AST tree back to source code and write it to an output file. @@ -347,8 +325,20 @@ \subsubsection{CacheRepeatedCallsRefactorer Module (\mref{mCRC})} \item[Implemented By:] EcoOptimizer \end{description} -\subsubsection{Etc.} +\subsubsection{Long Element Chain Module (\mref{mLEC})} + +% [Record, Library, Abstract Object, or Abstract Data Type] +\begin{description} + \item[Secrets:] How to parse a given code file to its AST representation, traverse the + AST tree to identify dictionary assignments, analyze the structure of nested dictionaries, + and flatten them. Additionally, it identifies all access calls associated with these dictionaries + in the source code and determines how to update them to reflect the new flattened structure. + \item[Services:] Detects nested dictionaries in the source code using AST parsing, simplifies their + structure by flattening them, and updates all associated access calls throughout the file. This improves + code readability, reduces complexity, and ensures correctness while maintaining the program's intended behavior. + \item[Implemented By:] EcoOptimizer +\end{description} \subsection{Software Decision Module} @@ -363,7 +353,7 @@ \subsection{Software Decision Module} \item[Implemented By:] -- \end{description} -\subsubsection{Pylint Analyzer Module} +\subsubsection{Pylint Analyzer Module (\mref{mPyA})} \begin{description} \item[Secrets:] The internal design and execution of static code analysis using Pylint and AST parsing, including custom detection and structuring of smells. These details are hidden from external modules. @@ -376,6 +366,16 @@ \subsubsection{Pylint Analyzer Module} \item[Implemented By:] \texttt{EcoOptimizer} \end{description} +\subsubsection{Testing Functionality Module (\mref{mTest})} + +% [Record, Library, Abstract Object, or Abstract Data Type] + +\begin{description} + \item[Secrets:] Runs testing suites in a subprocess. + \item[Services:] Checks whether the functionality of a given source code has changed. + \item[Implemented By:] EcoOptimizer +\end{description} + \section{Traceability Matrix} \label{SecTM} From 60c8e6e7e448b6c2e73ba41ef6a8932040f09260 Mon Sep 17 00:00:00 2001 From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com> Date: Fri, 17 Jan 2025 09:29:49 -0500 Subject: [PATCH 4/5] Added MIS of Testing Functionality Module (#314) --- docs/Design/SoftDetailedDes/MIS.tex | 138 +++++++++++++++++++++------- 1 file changed, 103 insertions(+), 35 deletions(-) diff --git a/docs/Design/SoftDetailedDes/MIS.tex b/docs/Design/SoftDetailedDes/MIS.tex index 974abb04..601e8d00 100644 --- a/docs/Design/SoftDetailedDes/MIS.tex +++ b/docs/Design/SoftDetailedDes/MIS.tex @@ -259,20 +259,20 @@ \subsection{Syntax} \subsection{Semantics} -\subsubsection*{State Variables} +\subsubsection{State Variables} \begin{itemize} \item \texttt{temp\_dir: Path}: Directory path for storing refactored files. \end{itemize} -\subsubsection*{Environment Variables} +\subsubsection{Environment Variables} None -\subsubsection*{Assumptions} +\subsubsection{Assumptions} \begin{itemize} \item \texttt{output\_dir} exists or can be created, and write permissions are available. \end{itemize} -\subsubsection*{Access Routine Semantics} +\subsubsection{Access Routine Semantics} \paragraph{\texttt{\_\_init\_\_(self, output\_dir: Path)}} \begin{itemize} @@ -288,7 +288,7 @@ \subsubsection*{Access Routine Semantics} \item \textbf{exception:} None. \end{itemize} -\subsubsection*{Local Functions} +\subsubsection{Local Functions} None. \newpage @@ -311,7 +311,7 @@ \subsection{Syntax} \subsection{Semantics} -\subsubsection*{State Variables} +\subsubsection{State Variables} \begin{itemize} \item \texttt{absolutePath: str}: Absolute path to the source file containing the smell. \item \texttt{column: int}: Starting column in the source file where the smell is detected. @@ -328,15 +328,15 @@ \subsubsection*{State Variables} \item \texttt{type: str}: Type or category of the smell. \end{itemize} -\subsubsection*{Environment Variables} +\subsubsection{Environment Variables} None -\subsubsection*{Assumptions} +\subsubsection{Assumptions} \begin{itemize} \item All values provided to the fields of \texttt{Smell} conform to the expected data types and constraints. \end{itemize} -\subsubsection*{Access Routine Semantics} +\subsubsection{Access Routine Semantics} \paragraph{\texttt{Smell()}} \begin{itemize} @@ -344,7 +344,7 @@ \subsubsection*{Access Routine Semantics} \item \textbf{output}: Returns a \texttt{Smell} instance. \end{itemize} -\subsubsection*{Local Functions} +\subsubsection{Local Functions} None. @@ -396,7 +396,7 @@ \subsection{Syntax} \subsection{Semantics} -\subsubsection*{State Variables} +\subsubsection{State Variables} \begin{itemize} \item \texttt{target\_line: int}: Line number where refactoring is applied. \item \texttt{target\_node: ASTnode}: Node representing the concatenation variable. @@ -407,16 +407,16 @@ \subsubsection*{State Variables} \item \texttt{outer\_loop: ASTnode}: Outermost loop before the start of the concatenation. \end{itemize} -\subsubsection*{Environment Variables} +\subsubsection{Environment Variables} None -\subsubsection*{Assumptions} +\subsubsection{Assumptions} \begin{itemize} \item The input file contains valid Python syntax. \item \texttt{pylint\_smell} provides a valid line number for the detected code smell. \end{itemize} -\subsubsection*{Access Routine Semantics} +\subsubsection{Access Routine Semantics} \paragraph{\texttt{\_\_init\_\_(self, output\_dir: Path)}} \begin{itemize} @@ -453,7 +453,7 @@ \subsubsection*{Access Routine Semantics} \item \textbf{exception}: Raises \texttt{TypeError} if \texttt{target\_node} or \texttt{outer\_loop} is not set. \end{itemize} -\subsubsection*{Local Functions} +\subsubsection{Local Functions} Functions for internal AST parsing, node manipulation, and validation are defined within the class but are not exported. \newpage @@ -498,23 +498,23 @@ \subsection{Syntax} \subsection{Semantics} -\subsubsection*{State Variables} +\subsubsection{State Variables} \begin{itemize} \item \texttt{target\_line: int}: Line number where refactoring is applied. \item \texttt{mim\_method\_class: str}: Class name containing the method to refactor. \item \texttt{mim\_method: str}: Method name to refactor. \end{itemize} -\subsubsection*{Environment Variables} +\subsubsection{Environment Variables} None -\subsubsection*{Assumptions} +\subsubsection{Assumptions} \begin{itemize} \item The input file contains valid Python syntax. \item \texttt{pylint\_smell} provides a valid line number for the detected code smell. \end{itemize} -\subsubsection*{Access Routine Semantics} +\subsubsection{Access Routine Semantics} \paragraph{\texttt{\_\_init\_\_(self, output\_dir: Path)}} \begin{itemize} @@ -551,7 +551,7 @@ \subsubsection*{Access Routine Semantics} \item \textbf{exception}: None. \end{itemize} -\subsubsection*{Local Functions} +\subsubsection{Local Functions} Functions for internal AST parsing, node transformation, and validation are defined within the class but are not exported. \newpage @@ -707,23 +707,23 @@ \subsection{Syntax} \subsection{Semantics} -\subsubsection*{State Variables} +\subsubsection{State Variables} \begin{itemize} \item \texttt{file\_path: Path}: The path to the Python file being analyzed. \item \texttt{source\_code: ast.Module}: The parsed abstract syntax tree of the source file. \item \texttt{smells\_data: list[dict]}: A list of detected code smells, represented as dictionaries. \end{itemize} -\subsubsection*{Environment Variables} +\subsubsection{Environment Variables} None -\subsubsection*{Assumptions} +\subsubsection{Assumptions} \begin{itemize} \item The input file is valid Python code and can be parsed into an AST. \item Configuration settings, such as extra Pylint options and custom smell definitions, are valid. \end{itemize} -\subsubsection*{Access Routine Semantics} +\subsubsection{Access Routine Semantics} \paragraph{\texttt{\_\_init\_\_(self, file\_path: Path, source\_code: ast.Module)}} \begin{itemize} @@ -788,13 +788,81 @@ \subsubsection*{Access Routine Semantics} \item \textbf{exception:} None. \end{itemize} -\subsubsection*{Local Functions} +\subsubsection{Local Functions} \begin{itemize} \item \texttt{parse\_line(file\_path: Path, line: int)}: Parses a specific line of code into an AST node. \item \texttt{get\_lambda\_code(lambda\_node: ast.Lambda)}: Returns the string representation of a lambda expression. \end{itemize} +\newpage + +\section{MIS of Testing Functionality} + +\texttt{TestRunner} + +\subsection{Module} + +Responsible for validating that any refactorings made to the source code do not modify it's original functionality. + +\subsection{Uses} +\begin{itemize} + \item Uses Python's subprocess library +\end{itemize} + +\subsection{Syntax} +\noindent +\textbf{Exported Constants}: None + +\noindent +\textbf{Exported Access Programs}: + +\begin{tabularx}{\linewidth}{|l|>{\raggedright\arraybackslash}X|l|l|} +\hline +Name & In & Out & Exceptions \\ +\hline +\texttt{\_\_init\_\_} & \texttt{run\_command: str, project\_path: Path} & None & None \\ +\hline +\texttt{retained\_functionality} & None & \texttt{bool} & \texttt{CalledProcessError} \\ +\hline +\end{tabularx} + +\subsection{Semantics} + +\subsubsection{State Variables} +\begin{itemize} + \item \texttt{project\_path: Path}: Path to the source code directory. + \item \texttt{run\_command: str}: Command used to run the tests. +\end{itemize} + +\subsubsection{Environment Variables} +None + +\subsubsection{Assumptions} +\begin{itemize} + \item The provided \texttt{run\_command} is a valid shell command. + \item \texttt{project\_path} is a valid path working source code directory. +\end{itemize} + +\subsubsection{Access Routine Semantics} + +\paragraph{\texttt{\_\_init\_\_(self, run\_command: str, project\_path: Path)}} +\begin{itemize} + \item \textbf{transition}: Initializes the test runner with the given \texttt{run\_command} and \texttt{project\_path}. + \item \textbf{output}: None. + \item \textbf{exception}: None. +\end{itemize} + +\paragraph{\texttt{retained\_functionality(self)}} +\begin{itemize} + \item \textbf{transition}: Runs the specified test command in the given project path. Logs success or failure, including standard output and error streams. + \item \textbf{output}: Returns \texttt{True} if the tests passed; otherwise, returns \texttt{False}. + \item \textbf{exception}: Raises a \texttt{CalledProcessError} if an eror occurs while running the tests in a subprocess. +\end{itemize} + +\subsubsection{Local Functions} +None. + \newpage \section{MIS of Use A Generator Refactorer} \label{mis:UseGen} @@ -836,22 +904,22 @@ \subsection{Syntax} \subsection{Semantics} -\subsubsection*{State Variables} +\subsubsection{State Variables} \begin{itemize} \item \texttt{temp\_dir: Path}: Directory path for storing refactored files. \item \texttt{output\_dir: Path}: Directory path for saving final refactored code. \end{itemize} -\subsubsection*{Environment Variables} +\subsubsection{Environment Variables} None -\subsubsection*{Assumptions} +\subsubsection{Assumptions} \begin{itemize} \item The input file contains valid Python syntax. \item \texttt{pylint\_smell} provides a valid line number for the detected code smell. \end{itemize} -\subsubsection*{Access Routine Semantics} +\subsubsection{Access Routine Semantics} \paragraph{\texttt{\_\_init\_\_(self, output\_dir: Path)}} \begin{itemize} @@ -874,7 +942,7 @@ \subsubsection*{Access Routine Semantics} \item \textbf{exception}: None. \end{itemize} -\subsubsection*{Local Functions} +\subsubsection{Local Functions} Functions for internal AST parsing, node manipulation, and validation are defined within the class but are not exported. \newpage @@ -911,22 +979,22 @@ \subsection{Syntax} \subsection{Semantics} -\subsubsection*{State Variables} +\subsubsection{State Variables} \begin{itemize} \item \texttt{cached\_var\_name: str}: Name of the temporary variable used for caching. \item \texttt{target\_line: int}: Line number where refactoring is applied. \end{itemize} -\subsubsection*{Environment Variables} +\subsubsection{Environment Variables} None -\subsubsection*{Assumptions} +\subsubsection{Assumptions} \begin{itemize} \item The input file contains valid Python syntax. \item \texttt{pylint\_smell} provides valid occurrences of repeated calls with line numbers and call strings. \end{itemize} -\subsubsection*{Access Routine Semantics} +\subsubsection{Access Routine Semantics} \paragraph{\texttt{\_\_init\_\_(self, output\_dir: Path)}} \begin{itemize} @@ -942,7 +1010,7 @@ \subsubsection*{Access Routine Semantics} \item \textbf{exception}: Raises \texttt{IOError} if input file cannot be read. Raises \texttt{TypeError} if source file cannot be parsed into an AST. \end{itemize} -\subsubsection*{Local Functions} +\subsubsection{Local Functions} \begin{itemize} \item \texttt{\_get\_indentation(lines, line\_number)}: Determines the indentation of a specific line. \item \texttt{\_replace\_call\_in\_line(line, call\_string, cached\_var\_name)}: Replaces repeated calls with the cached variable. From 8c4486bd98cd86d65eec5a6ba7e507254e7174cf Mon Sep 17 00:00:00 2001 From: Sevhena Walker <83547364+Sevhena@users.noreply.github.com> Date: Fri, 17 Jan 2025 10:57:52 -0500 Subject: [PATCH 5/5] Add use Hierarchy Diagram (#319) --- docs/Design/SoftArchitecture/MG.tex | 2 +- docs/Images/use_hierarchy_modules.png | Bin 0 -> 66310 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 docs/Images/use_hierarchy_modules.png diff --git a/docs/Design/SoftArchitecture/MG.tex b/docs/Design/SoftArchitecture/MG.tex index 20bcdd83..368972bc 100644 --- a/docs/Design/SoftArchitecture/MG.tex +++ b/docs/Design/SoftArchitecture/MG.tex @@ -455,7 +455,7 @@ \section{Use Hierarchy Between Modules} \label{SecUse} \begin{figure}[H] \centering -%\includegraphics[width=0.7\textwidth]{UsesHierarchy.png} +\includegraphics[width=\textwidth]{../../Images/use_hierarchy_modules.png} \caption{Use hierarchy among modules} \label{FigUH} \end{figure} diff --git a/docs/Images/use_hierarchy_modules.png b/docs/Images/use_hierarchy_modules.png new file mode 100644 index 0000000000000000000000000000000000000000..af6c57ffbcb1625c0464a46864f06009b9e8ef2a GIT binary patch literal 66310 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 literal 0 HcmV?d00001