檢視原始碼 cover (tools v4.1.1)
Erlang 的覆蓋率分析工具
模組 cover
提供一組用於 Erlang 程式碼覆蓋率分析的函式,計算程式執行時每個可執行行程式碼被執行的次數。可執行行是指函式中子句主體內的程式碼行,以及 case
、receive
或 try
中的程式碼行。子句標頭中的程式碼行、空白行和僅包含註解的程式碼行不是可執行的。
覆蓋率分析可用於驗證測試案例是否涵蓋了被測程式碼中的所有相關程式碼行。它也有助於尋找程式碼中的瓶頸。
在進行任何分析之前,相關的模組必須先經過覆蓋率編譯。這表示在模組編譯成二進制檔並載入之前,會向模組添加一些額外資訊。模組的原始碼檔案不受影響,也不會建立 .beam
檔案。如果執行時系統原生支援覆蓋率,Cover 將自動使用該功能來降低覆蓋率編譯程式碼的執行開銷。
變更
Erlang/OTP 27 中新增了原生覆蓋率支援。
每次呼叫覆蓋率編譯模組中的函式時,都會將有關呼叫的資訊新增到 Cover 的內部資料庫中。覆蓋率分析是透過檢查 Cover 資料庫的內容來執行。輸出 Answer
由兩個參數決定:Level
和 Analysis
。
Level = module
Answer = {Module,Value}
,其中Module
是模組名稱。Level = function
Answer = [{Function,Value}]
,模組中每個函式一個元組。函式由其模組名稱M
、函式名稱F
和元數A
指定為元組{M,F,A}
。Level = clause
Answer = [{Clause,Value}]
,模組中每個子句一個元組。子句由其模組名稱M
、函式名稱F
、元數A
和函式定義中的位置C
指定為元組{M,F,A,C}
。Level = line
Answer = [{Line,Value}]
,模組中每個可執行行一個元組。程式碼行由其模組名稱M
和原始碼檔案中的行號N
指定為元組{M,N}
。Analysis = coverage
Value = {Cov,NotCov}
,其中Cov
是模組、函式、子句或程式碼行中至少執行過一次的可執行行數,而NotCov
是尚未執行的可執行行數。Analysis = calls
Value = Calls
,它是模組、函式或子句被呼叫的次數。在程式碼行級別分析的情況下,Calls
是程式碼行被執行的次數。
分散式
Cover 可以在分散式 Erlang 系統中使用。系統中的其中一個節點必須被選為主節點,並且所有 Cover 命令都必須從該節點執行。如果在遠端節點上呼叫介面函式,則會傳回錯誤原因 not_main_node
。
使用 cover:start/1
和 cover:stop/1
來新增或移除節點。相同的覆蓋率編譯程式碼將載入到每個節點上,並且分析將收集並匯總來自所有節點的覆蓋率資料結果。
若只想從遠端節點收集資料而不停止這些節點上的 cover
,請使用 cover:flush/1
如果與遠端節點的連線中斷,主節點會將其標記為遺失。如果節點恢復連線,它會再次新增。如果遠端節點在斷線期間處於活動狀態,則分析中將包含斷線之前和期間的覆蓋率資料。
摘要
函式
分析由 Arg
指定的一或多個模組。
分析由 Arg1
和 Arg2
指定的一或多個模組。
透過檢查內部資料庫的內容,執行由 Analysis
和 Level
指定的一或多個覆蓋率編譯模組的分析。
如果 Arg
是 analyse_option()
選項的清單,則此呼叫等同於 analyse_to_file('_', Arg)
。
輸出指定模組的原始碼複本,並註解每個可執行行的執行計數。
此函式的工作方式與 analyse_to_file/2
相同,但它是非同步而非同步。
根據包含抽象程式碼(選項 debug_info
)的 .beam
檔案,覆蓋率編譯一或多個模組。
以與 compile_beam/1
相同的方式,覆蓋率編譯目錄 Dir
中的所有 .beam
檔案。
以與 compile_module/1,2
相同的方式,編譯目錄 Dir
中的所有模組(.erl
檔案)以進行 Cover 分析,並傳回 Result
的清單。
覆蓋率編譯一或多個模組。
將 Module
的目前覆蓋率資料匯出到檔案 ExportFile
。
從遠端節點上的 Cover 資料庫擷取資料,並將其儲存在主節點上。
從使用 export/1,2
建立的檔案 ExportFile
匯入覆蓋率資料。
傳回所有已匯入檔案的清單。
傳回所有有匯入資料的模組的清單。
如果模組 Module
已進行覆蓋率編譯,則傳回 {file, File}
,否則傳回 false
。
僅支援在本機節點上執行 Cover。
傳回包含目前已進行覆蓋率編譯的所有模組的清單。
重設所有節點上 Cover 資料庫中所有已進行覆蓋率編譯的模組的所有覆蓋率資料。
重設所有節點上 Cover 資料庫中已進行覆蓋率編譯的模組 Module
的所有覆蓋率資料。
啟動擁有 Cover 內部資料庫的 Cover 伺服器。此函式會由模組中的其他函式自動呼叫。
在每個指定的節點上啟動 Cover 伺服器,並載入所有已進行覆蓋率編譯的模組。
停止 Cover 伺服器並卸載所有已進行覆蓋率編譯的程式碼。
停止指定節點上的 Cover 伺服器並卸載所有已進行覆蓋率編譯的程式碼。
傳回包含屬於覆蓋率分析的所有節點的清單。
類型
-type analyse_answer() :: {ok, OutFile :: file:filename()} | {error, analyse_rsn()}.
-type analyse_fail() :: [{not_cover_compiled, module()}].
-type analyse_file_fail() :: [analyse_rsn()].
-type analyse_file_ok() :: [OutFile :: file:filename()].
-type analyse_item() :: (Line :: {M :: module(), N :: non_neg_integer()}) | (Clause :: {M :: module(), F :: atom(), A :: arity(), C :: non_neg_integer()}) | (Function :: {M :: module(), F :: atom(), A :: arity()}).
-type analyse_ok() :: [{Module :: module(), Value :: analyse_value()}] | [{Item :: analyse_item(), Value :: analyse_value()}].
-type analyse_option() :: html | {outfile, OutFile :: file:filename()} | {outdir, OutDir :: file:filename()}.
-type analyse_rsn() :: {not_cover_compiled, Module :: module()} | {file, File :: file:filename(), Reason :: term()} | {no_source_code_found, Module :: module()}.
-type analyse_value() :: {Cov :: non_neg_integer(), NotCov :: non_neg_integer()} | (Calls :: non_neg_integer()).
-type analysis() :: coverage | calls.
-type beam_mod_file() :: (Module :: module()) | (BeamFile :: file:filename()).
-type beam_mod_files() :: beam_mod_file() | [beam_mod_file()].
-type compile_beam_result() :: {ok, module()} | {error, BeamFile :: file:filename()} | {error, Reason :: compile_beam_rsn()}.
-type compile_beam_rsn() :: non_existing | {no_abstract_code, BeamFile :: file:filename()} | {encrypted_abstract_code, BeamFile :: file:filename()} | {already_cover_compiled, no_beam_found, module()} | {{missing_backend, module()}, BeamFile :: file:filename()} | {no_file_attribute, BeamFile :: file:filename()} | not_main_node.
-type compile_result() :: {ok, Module :: module()} | {error, file:filename()} | {error, not_main_node}.
-type export_reason() :: {not_cover_compiled, Module :: module()} | {cant_open_file, ExportFile :: file:filename(), FileReason :: term()} | not_main_node.
-type file_error() :: eacces | enoent.
-type level() :: line | clause | function | module.
-type mod_file() :: (Module :: module()) | (File :: file:filename()).
-type one_result() :: {ok, {Module :: module(), Value :: analyse_value()}} | {ok, [{Item :: analyse_item(), Value :: analyse_value()}]} | {error, {not_cover_compiled, module()}}.
-type option() :: {i, Dir :: file:filename()} | {d, Macro :: atom()} | {d, Macro :: atom(), Value :: term()} | export_all.
函式
-spec analyse() -> {result, analyse_ok(), analyse_fail()} | {error, not_main_node}.
-spec analyse(Analysis) -> {result, analyse_ok(), analyse_fail()} | {error, not_main_node} when Analysis :: analysis(); (Level) -> {result, analyse_ok(), analyse_fail()} | {error, not_main_node} when Level :: level(); (Modules) -> OneResult | {result, analyse_ok(), analyse_fail()} | {error, not_main_node} when Modules :: modules(), OneResult :: one_result().
分析由 Arg
指定的一或多個模組。
如果 Arg
是 analysis()
中的其中一個值,則此呼叫等同於 analyse('_', Arg, function)
。
如果 Arg
是 level()
中的其中一個值,則此呼叫等同於 analyse('_', coverage, Arg)
。
否則,會假設 Arg
為模組名稱,且此呼叫等同於 analyse(Arg, coverage, function)
。
注意
若要分析名稱與
analysis()
或level()
中的其中一個值重疊的模組,模組名稱必須位於清單中。例如,若要分析名為calls
的模組cover:analyse([calls]).
-spec analyse(Analysis, Level) -> {result, analyse_ok(), analyse_fail()} | {error, not_main_node} when Analysis :: analysis(), Level :: level(); (Modules, Analysis) -> OneResult | {result, analyse_ok(), analyse_fail()} | {error, not_main_node} when Analysis :: analysis(), Modules :: modules(), OneResult :: one_result(); (Modules, Level) -> OneResult | {result, analyse_ok(), analyse_fail()} | {error, not_main_node} when Level :: level(), Modules :: modules(), OneResult :: one_result().
分析由 Arg1
和 Arg2
指定的一或多個模組。
如果 Arg1
是 analysis()
中的其中一個值,且 Arg2
是 level()
中的其中一個值,則此呼叫等同於 analyse('_', Arg1, Arg2)
。
如果 Arg2
是 analysis()
中的其中一個值,則會假設 Arg1
為模組,且此呼叫等同於 analyse(Arg1, Arg2, function)
。
如果 Arg2
是 level()
中的其中一個值,則會假設 Arg1
為模組,且此呼叫等同於 analyse(Arg1, coverage, Arg2)
。
注意
若要分析名稱與
analysis()
中的其中一個值重疊的模組,模組名稱需要位於清單中。例如,若要分析名為calls
的模組cover:analyse([calls], function).
-spec analyse(Modules, Analysis, Level) -> OneResult | {result, analyse_ok(), analyse_fail()} | {error, not_main_node} when Analysis :: analysis(), Level :: level(), Modules :: modules(), OneResult :: one_result().
透過檢查內部資料庫的內容,執行由 Analysis
和 Level
指定的一或多個覆蓋率編譯模組的分析。
如果 Modules
是一個原子(單一模組),則傳回值為 OneResult
,否則傳回值為 {result, Ok, Fail}
。
如果 Modules
是原子 '_'
,則會分析 cover 資料表中具有資料的所有模組。請注意,這包括 cover 編譯的模組和匯入的模組。
如果給定的模組未進行 cover 編譯,則會以錯誤原因 {not_cover_compiled, Module}
指示。
-spec analyse_to_file() -> {result, analyse_file_ok(), analyse_file_fail()} | {error, not_main_node}.
-spec analyse_to_file(Modules) -> Answer | {result, analyse_file_ok(), analyse_file_fail()} | {error, not_main_node} when Modules :: modules(), Answer :: analyse_answer(); (Options) -> {result, analyse_file_ok(), analyse_file_fail()} | {error, not_main_node} when Options :: [analyse_option()].
如果 Arg
是 analyse_option()
選項的清單,則此呼叫等同於 analyse_to_file('_', Arg)
。
否則,會假設 Arg
為模組,且此呼叫等同於 analyse_to_file(Arg, [])
。
注意
若要分析名稱為
html
的模組(與analyse_option()
中的選項重疊),則必須使用cover:analyse_to_file/2
cover:analyse_to_file([html], []).
-spec analyse_to_file(Modules, Options) -> Answer | {result, analyse_file_ok(), analyse_file_fail()} | {error, not_main_node} when Modules :: modules(), Options :: [analyse_option()], Answer :: analyse_answer().
輸出指定模組的原始碼複本,並註解每個可執行行的執行計數。
輸出檔案 OutFile
的預設值為 Module.COVER.out
,如果使用選項 html
,則預設值為 Module.COVER.html
。
如果 Modules
是一個原子(一個模組),則傳回值將為 Answer
,否則傳回值將為清單 {result, Ok, Fail}
。
如果 Modules
為 '_',則會分析 Cover 資料表中具有資料的所有模組。請注意,這包括 cover 編譯的模組和匯入的模組。
如果模組未進行 cover 編譯,則會以錯誤原因 {not_cover_compiled, Module}
指示。
如果無法使用 file:open/2
開啟來源檔案和/或輸出檔案,則函式會傳回 {error, {file, File, Reason}}
,其中 File
是檔案名稱,而 Reason
是錯誤原因。
如果模組是從 .beam
檔案進行 cover 編譯的,也就是使用 compile_beam/1
或 compile_beam_directory/0,1
,則會假設原始碼可以在下列其中一個位置找到
- 與
.beam
檔案相同的目錄 - 相對於具有
.beam
檔案的目錄的../src
Module:module_info(compile)
中的來源路徑,在這種情況下,會檢查兩個路徑- 首先是透過將
../src
和已編譯路徑的尾部,在其尾端的src
元件下方連接起來所建構的路徑 - 已編譯的路徑本身
- 首先是透過將
如果找不到任何原始碼,則會以錯誤原因 {no_source_code_found, Module}
指示。
-spec async_analyse_to_file(Module, OutFile) -> pid() when Module :: module(), OutFile :: file:filename(); (Module, Options) -> pid() when Module :: module(), Options :: [Option], Option :: html.
-spec async_analyse_to_file(Module, OutFile, Options) -> pid() when Module :: module(), OutFile :: file:filename(), Options :: [Option], Option :: html.
此函式的工作方式與 analyse_to_file/2
相同,但它是非同步而非同步。
衍生程序會在建立時與呼叫者連結。如果在執行 cover 分析時發生 analyse_rsn()
類型的錯誤,則程序將會當機,並傳回與 analyse_to_file
將傳回的相同錯誤原因。
-spec compile(ModFiles) -> Result | [Result] when ModFiles :: mod_files(), Result :: compile_result().
-spec compile(ModFiles, Options) -> Result | [Result] when ModFiles :: mod_files(), Options :: [option()], Result :: compile_result().
-spec compile_beam(ModFiles) -> Result | [Result] when ModFiles :: beam_mod_files(), Result :: compile_beam_result().
根據包含抽象程式碼(選項 debug_info
)的 .beam
檔案,覆蓋率編譯一或多個模組。
從 .beam
檔案進行 Cover 編譯比從原始碼進行編譯速度更快且更方便,因為不需要為包含路徑或巨集提供選項。但是,現有的 .beam
檔案必須使用選項 debug_info
進行編譯,以便它們包含抽象程式碼。
如果缺少抽象程式碼,則會傳回錯誤原因 {no_abstract_code, BeamFile}
。如果抽象程式碼已加密,且沒有可用於解密的金鑰,則會傳回錯誤原因 {encrypted_abstract_code, BeamFile}
。
如果只將模組名稱(也就是說,不是 .beam
檔案的完整名稱)提供給此函式,則會透過呼叫 code:which(Module)
來找到 .beam
檔案。如果找不到 .beam
檔案,則會傳回錯誤原因 non_existing
。如果模組已使用 compile_beam/1
進行 cover 編譯,則會從與第一次編譯時相同的位置選取 .beam
檔案。如果模組已使用 compile_module/2
進行 cover 編譯,則無法找到正確的 .beam
檔案,因此會傳回錯誤原因 {already_cover_compiled, no_beam_found, Module}
。
如果無法將編譯的程式碼載入到節點上,則會傳回 {error, BeamFile}
。
如果將 ModFiles
的清單作為輸入,則會傳回 Result
的清單。傳回清單的順序未定義。
-spec compile_beam_directory() -> [Result] | {error, Reason} when Reason :: file_error(), Result :: compile_beam_result().
-spec compile_beam_directory(Dir) -> [Result] | {error, Reason} when Dir :: file:filename(), Reason :: file_error(), Result :: compile_beam_result().
以與 compile_beam/1
相同的方式,覆蓋率編譯目錄 Dir
中的所有 .beam
檔案。
如果成功,此函式會傳回 compile_beam_result()
的清單。否則,如果目錄無法讀取,則會傳回 {error, eacces}
;如果目錄不存在,則會傳回 {error, enoent}
。
-spec compile_directory() -> [Result] | {error, Reason} when Reason :: file_error(), Result :: compile_result().
-spec compile_directory(Dir) -> [Result] | {error, Reason} when Dir :: file:filename(), Reason :: file_error(), Result :: compile_result().
-spec compile_directory(Dir, Options) -> [Result] | {error, Reason} when Dir :: file:filename(), Options :: [option()], Reason :: file_error(), Result :: compile_result().
以與 compile_module/1,2
相同的方式,編譯目錄 Dir
中的所有模組(.erl
檔案)以進行 Cover 分析,並傳回 Result
的清單。
如果目錄無法讀取,則此函式會傳回 {error, eacces}
;如果目錄不存在,則會傳回 {error, enoent}
。
-spec compile_module(ModFiles) -> Result | [Result] when ModFiles :: mod_files(), Result :: compile_result().
-spec compile_module(ModFiles, Options) -> Result | [Result] when ModFiles :: mod_files(), Options :: [option()], Result :: compile_result().
覆蓋率編譯一或多個模組。
模組會透過其模組名稱 Module
或其檔案名稱 File
提供。
可以省略 .erl
副檔名。如果模組不在目前目錄中,則必須指定其完整路徑。
Options
是編譯器選項的清單。只有定義包含檔案目錄和巨集的選項會傳遞至 compile:file/2
;其他所有選項都會遭到忽略。
如果模組 cover 編譯成功,則函式會傳回 {ok, Module}
。否則,函式會傳回 {error, File}
。錯誤和警告會在發生時列印。
如果將 ModFiles
的清單作為輸入,則會傳回 Result
的清單。清單中傳回結果的順序未定義。
請注意,內部資料庫會在編譯期間初始化,這表示模組的任何先前收集的涵蓋範圍資料都會遺失。
-spec export(File) -> ok | {error, Reason} when File :: file:filename(), Reason :: export_reason().
等同於 export(File, '_')
。
-spec export(File, Module) -> ok | {error, Reason} when File :: file:filename(), Module :: module(), Reason :: export_reason().
將 Module
的目前覆蓋率資料匯出到檔案 ExportFile
。
建議使用副檔名 .coverdata
來命名 ExportFile
。
如果 Module
為 '_',則會匯出所有 cover 編譯或先前匯入的模組的資料。
如果要合併來自不同系統的涵蓋範圍資料,此函式很有用。
另請參閱 import/1
。
從遠端節點上的 Cover 資料庫擷取資料,並將其儲存在主節點上。
-spec import(ExportFile) -> ok | {error, Reason} when ExportFile :: file:filename(), Reason :: {cant_open_file, ExportFile, FileReason :: term()} | not_main_node.
從使用 export/1,2
建立的檔案 ExportFile
匯入覆蓋率資料。
在此呼叫之後執行的任何分析都會包含匯入的資料。
請注意,當編譯模組時,所有現有的覆蓋率資料都會被移除,包括匯入的資料。如果模組在匯入資料時已經編譯過,則匯入的資料會新增到現有的覆蓋率資料中。
可以將多個匯出檔案的覆蓋率資料匯入到一個系統中。分析時,覆蓋率資料會被加總。
除非先重置或編譯模組,否則不能從同一個檔案匯入模組的覆蓋率資料兩次。此檢查是基於檔名,因此您可以透過重新命名匯出檔案來輕易地欺騙系統。
-spec imported() -> [file:filename()] | {error, not_main_node}.
傳回所有已匯入檔案的清單。
-spec imported_modules() -> [module()] | {error, not_main_node}.
傳回所有有匯入資料的模組的清單。
-spec is_compiled(Module) -> {file, File :: file:filename()} | false | {error, not_main_node} when Module :: module().
如果模組 Module
已進行覆蓋率編譯,則傳回 {file, File}
,否則傳回 false
。
File
是 compile_module/1,2
使用的 .erl
檔案,或是 compile_beam/1
使用的 .beam
檔案。
-spec local_only() -> ok | {error, too_late}.
僅支援在本機節點上執行 Cover。
必須在任何模組被編譯或加入任何節點之前呼叫此函數。在此模式下執行時,模組將以更有效率的方式進行覆蓋率編譯,但產生的程式碼僅適用於編譯它們的相同節點。
-spec modules() -> [module()] | {error, not_main_node}.
傳回包含目前已進行覆蓋率編譯的所有模組的清單。
-spec reset() -> ok | {error, not_main_node}.
重設所有節點上 Cover 資料庫中所有已進行覆蓋率編譯的模組的所有覆蓋率資料。
-spec reset(Module) -> ok | {error, not_main_node} | {error, {not_cover_compiled, Module}} when Module :: module().
重設所有節點上 Cover 資料庫中已進行覆蓋率編譯的模組 Module
的所有覆蓋率資料。
如果 Module
沒有經過覆蓋率編譯,該函數將返回 {error, {not_cover_compiled, Module}}
。
啟動擁有 Cover 內部資料庫的 Cover 伺服器。此函式會由模組中的其他函式自動呼叫。
-spec start(Nodes) -> {ok, StartedNodes} | {error, not_main_node} | {error, local_only} when Nodes :: node() | [node()], StartedNodes :: [node()].
在每個指定的節點上啟動 Cover 伺服器,並載入所有已進行覆蓋率編譯的模組。
如果已呼叫 cover:local_only/0
,則此呼叫將會失敗。
-spec stop() -> ok | {error, not_main_node}.
停止 Cover 伺服器並卸載所有已進行覆蓋率編譯的程式碼。
停止指定節點上的 Cover 伺服器並卸載所有已進行覆蓋率編譯的程式碼。
從遠端節點的 Cover 資料庫中提取資料,並儲存在主要節點上。
-spec which_nodes() -> [node()].
傳回包含屬於覆蓋率分析的所有節點的清單。
請注意,目前節點不包含在內,因為它始終是分析的一部分。