/// <exception cref="ChocolateyException"></exception>
        public async Task <List <ChocolateyPackage> > GetOutdatedPackagesAsync()
        {
            await OutdatedPckgLock.WaitAsync().ConfigureAwait(false);

            try
            {
                if (OutdatedPckgCache.Count <= 0)
                {
                    List <ChocolateyPackage> outdatedPckgs = await ChocolateyWrapper.FetchOutdatedAsync();

                    outdatedPckgs.ForEach(p => OutdatedPckgCache[p.Name] = p);
                }
            }
            catch (ChocolateyException)
            {
                throw;
            }
            finally
            {
                OutdatedPckgLock.Release();
                Log.Debug("Released lock for outdated packages.");
            }

            return(OutdatedPckgCache.Values.ToList());
        }
        /// <exception cref="ChocolateyException"></exception>
        public async Task <string> GetPackageDetails(ChocolateyPackage package)
        {
            if (!PckgDetailsCache.TryGetValue(package.Name, out string details))
            {
                details = await ChocolateyWrapper.FetchInfoAsync(package);

                PckgDetailsCache[package.Name] = details;
            }

            return(details);
        }
        /// <exception cref="ChocolateyException"></exception>
        public void Unpin(ChocolateyPackage package)
        {
            int exitCode = ChocolateyWrapper.Unpin(package.Name);

            Log.Debug($"choco pin remove operation for {package.Name} returned with {exitCode}.");
            if (exitCode != 0)
            {
                throw new ChocolateyException($"choco did not exit cleanly. Returned {exitCode}.");
            }

            package.Pinned = false;
        }
        /// <exception cref="ChocolateyException"></exception>
        public void Pin(ChocolateyPackage package)
        {
            int exitCode = ChocolateyWrapper.Pin(package.Name, package.CurrVer);

            Log.Debug($"choco add pin operation for {package.Name} returned with {exitCode}.");
            if (exitCode != 0)
            {
                throw new ChocolateyException($"choco did not exit cleanly. Returned {exitCode}.");
            }

            package.Pinned = true;
        }
        /// <summary>
        /// Upgrades a collection of Chocolatey packages. If the <c>validExitCodes</c> parameter
        /// is not present, only zero is considered valid. When specified, the array should
        /// include zero.
        /// If the upgrade process exited with a non-valid code, a <see cref="ChocolateyException"/>
        /// is thrown.
        /// </summary>
        /// <param name="packages"></param>
        /// <param name="validExitCodes">test</param>
        /// <exception cref="ChocolateyException"></exception>
        public void Upgrade(List <ChocolateyPackage> packages, int[] validExitCodes = null)
        {
            int exitCode = ChocolateyWrapper.Upgrade(packages);

            if (validExitCodes == null)
            {
                validExitCodes = new int[] { 0 }
            }
            ;
            if (!validExitCodes.Contains(exitCode))
            {
                throw new ChocolateyException($"choco did not exit cleanly. Returned {exitCode}.");
            }
        }
        /// <exception cref="ChocolateyException"></exception>
        public async Task <List <ChocolateyPackage> > GetInstalledPackagesAsync()
        {
            await InstalledPckgLock.WaitAsync().ConfigureAwait(false);

            try
            {
                if (InstalledPckgCache.Count <= 0)
                {
                    List <ChocolateyPackage> installedPckgs = await ChocolateyWrapper.FetchInstalledAsync();

                    installedPckgs.ForEach(p => InstalledPckgCache[p.Name] = p);
                }
            }
            catch (ChocolateyException)
            {
                throw;
            }
            finally
            {
                InstalledPckgLock.Release();
            }

            return(InstalledPckgCache.Values.ToList());
        }